每张表只描述一个业务对象或实体。
✅ 正例:
user
表只存储用户基本信息,不混入登录日志
❌ 反例:user
表里既存基本资料,又有积分、行为记录
snake_case
)sys_user
)created_at
, user_id
, status
等字段设计需尽量原子,避免多个信息混存一个字段中
❌ 反例:手机号 + 邮箱一起存为
contact_info
✅ 正例:phone
,
类型 | 适合字段 |
---|---|
INT | 自增主键、状态码等 |
VARCHAR(n) | 字符串,n 不要设置过大 |
TEXT | 长文本,例如备注 |
DATETIME/TIMESTAMP | 时间字段 |
TINYINT | 枚举类型、布尔标志 |
添加 is_deleted
或 status
字段用于逻辑删除,而非物理删除。
ALTER TABLE user ADD is_deleted TINYINT DEFAULT 0;
user
)CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(128) NOT NULL,
phone VARCHAR(20) UNIQUE,
avatar VARCHAR(255),
status TINYINT DEFAULT 1 COMMENT '0-禁用, 1-正常',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
product
)CREATE TABLE product (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0,
category_id BIGINT,
status TINYINT DEFAULT 1 COMMENT '1-上架, 0-下架',
created_at DATETIME,
updated_at DATETIME
);
order
)CREATE TABLE `order` (
id BIGINT PRIMARY KEY,
user_id BIGINT,
total_amount DECIMAL(10,2),
status TINYINT DEFAULT 0 COMMENT '0-待支付,1-已支付,2-已发货',
created_at DATETIME,
paid_at DATETIME,
shipped_at DATETIME
);
项目 | 建议 |
---|---|
主键 | 使用自增 ID 或雪花算法(推荐 BIGINT) |
外键约束 | 可选用逻辑约束,不强依赖 DB 的外键机制 |
唯一索引 | 登录名、手机号、邮箱等需加唯一索引 |
时间字段 | 建议统一为 created_at , updated_at |
状态标识 | 用 status 、is_deleted 等统一语义字段 |
user_points_log
)CREATE TABLE user_points_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
points INT NOT NULL,
type TINYINT COMMENT '1-签到,2-下单,3-兑换',
description VARCHAR(255),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
status
类字段含义TINYINT(1)
,值为 0/1
原则 | 描述 |
---|---|
单一职责 | 一张表聚焦一个核心对象 |
命名规范 | 字段统一小写 + 下划线 |
结构清晰 | 字段含义明确、类型合适 |
状态标记 | 不物理删除,用状态字段 |
适度冗余 | 合理冗余提升查询性能 |
易扩展 | 留字段余量,避免频繁改表 |
秒杀系统具有以下高并发特征:
外键是一种数据库级别的引用约束,用于确保数据一致性,例如:
ALTER TABLE order
ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES user(id);
这样可以保证 order.user_id
一定是 user.id
中存在的值。
推荐:通过代码逻辑校验关联关系,而非数据库级外键约束
问题 | 说明 |
---|---|
性能开销 | 外键会导致数据库在每次 insert/update/delete 时,必须检查关联表记录,影响吞吐 |
锁表风险 | 大量并发写入关联表,数据库为保证外键完整性可能产生锁,导致阻塞甚至死锁 |
可维护性差 | 外键让表之间耦合度太高,跨库/分表/分库迁移非常困难 |
分布式事务 | 外键机制无法跨库生效,分布式系统中失效,反而容易混乱 |
级联操作风险 | 删除用户触发订单级联删除,在秒杀系统是不可接受的行为 |
所谓逻辑约束是:
在程序代码中通过检查和业务逻辑判断,代替外键的数据库约束。
例如:
userService.existsById(userId)
判断用户是否存在CREATE TABLE order (
id BIGINT PRIMARY KEY,
user_id BIGINT,
voucher_id BIGINT,
FOREIGN KEY (user_id) REFERENCES user(id),
FOREIGN KEY (voucher_id) REFERENCES voucher(id)
);
这种方式:
user
, voucher
都有硬性依赖不加外键,改用代码校验 + 索引保护:
CREATE TABLE order (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
voucher_id BIGINT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_user_id ON order(user_id);
CREATE INDEX idx_voucher_id ON order(voucher_id);
程序逻辑(Java 示例):
User user = userService.getById(userId);
if (user == null) throw new BusinessException("非法用户");
Voucher voucher = voucherService.getById(voucherId);
if (voucher == null || voucher.getStock() < 1) throw new BusinessException("券无效");
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64),
phone VARCHAR(20) UNIQUE,
status TINYINT DEFAULT 1
);
CREATE TABLE voucher (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(128),
stock INT,
begin_time DATETIME,
end_time DATETIME
);
CREATE TABLE voucher_order (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
voucher_id BIGINT NOT NULL,
created_at DATETIME
);
建唯一索引:防止重复下单
CREATE UNIQUE INDEX uniq_user_voucher ON voucher_order(user_id, voucher_id);
Redis预校验:
分布式锁/消息队列:防止并发冲突
后台数据清理:定期校验表之间关联的一致性
方面 | 外键约束 | 逻辑约束(推荐) |
---|---|---|
数据一致性 | 数据库自动校验 | 程序手动校验 |
性能 | 写入时数据库验证有开销 | 无额外数据库负担 |
并发处理 | 有锁表、死锁风险 | 自定义控制并发 |
分布式适配 | 不支持跨库外键 | 完全支持 |
运维复杂度 | 表强依赖,难迁移 | 表可解耦,易扩展 |
在高并发、高性能要求的项目(如秒杀、消息系统、电商等)中,应采用逻辑约束代替数据库外键,由应用层保障数据一致性和业务逻辑正确性。