
1. 设计表时包含这些通用字段
通常,一个表应包含以下字段:
id
:主键。每个表都必须有一个主键 --- 没有例外。create_time
:创建时间 --- 必须包含。modified_time
:最后修改时间 --- 必须包含。每当记录被更新时,这个字段应该被更新。version
:记录的版本号,通常用于乐观锁 --- 可选。modifier
:最后修改记录的人 --- 可选。creator
:创建记录的人 --- 可选。
2. 每个字段都应该有注释,尤其是枚举类型
在设计表时,每个字段都应该包含注释。这对于使用枚举的字段尤为重要 --- 所有枚举值都应该在注释中列出。如果以后发生更改,也应该在注释中反映出来。
坏例子:
sql
CREATE TABLE order_tab (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id BIGINT UNIQUE,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
payment_status VARCHAR(20) DEFAULT 'not_paid',
version INT DEFAULT 0,
created_time DATETIME,
updated_time DATETIME,
creator VARCHAR(255),
modifier VARCHAR(255)
);
好例子:
sql
CREATE TABLE order_tab (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '订单项的唯一标识符,自动递增主键',
order_id BIGINT UNIQUE COMMENT '订单的全局唯一标识符',
user_id BIGINT NOT NULL COMMENT '用户的唯一标识符,与用户表关联',
total_amount DECIMAL(10, 2) NOT NULL COMMENT '总订单金额,精确到小数点后两位',
status VARCHAR(20) NOT NULL DEFAULT 'PENDING' COMMENT '订单状态,例如 PENDING, COMPLETED 等',
payment_status VARCHAR(20) DEFAULT 'not_paid' COMMENT '支付状态,例如 not_paid, paid 等',
version INT DEFAULT 0 COMMENT '用于并发控制的乐观锁版本号',
created_time DATETIME COMMENT '订单创建时间戳',
updated_time DATETIME COMMENT '最后更新时间戳',
creator VARCHAR(255) COMMENT '订单创建者 --- 通常是用户或系统的用户名',
modifier VARCHAR(255) COMMENT '最后修改订单的人或系统'
);
3. 命名规范
为表名、字段名、索引名等使用清晰一致的命名。良好的命名可以提高可读性和理解性。
例如,避免使用以下名称:
acc_no
,1_acc_no
(坏例子)
相反,使用:
account_no
,account_number
(好例子)
其他规则:
- 表和字段名应使用小写字母或数字。
- 不要以数字开头。
- 避免使用缩写。
- 索引命名规范:
-
- 主键:
pk_<字段名>
- 唯一索引:
uk_<字段名>
- 普通索引:
idx_<字段名>
- 主键:
4. 选择合适的字段类型
在设计表时,选择最合适的字段类型:
- 合适数据类型以节省存储空间 --- 例如,按顺序优先选择
tinyint
、smallint
、int
、bigint
。 - 对于货币值,使用
decimal
而不是float
或double
。 - 如果字符串长度是固定,使用
char
。 - 对于可变长度字符串,使用
varchar
,但长度应保持在 5000 以下。 - 对于非常大的值,考虑使用
text
并将其存储在与主表关联的单独表中。 - 表中所有
varchar
字段的总长度不得超过 65535 字节。如果需要,使用TEXT
或LONGTEXT
类型。
5. 设计合理的主键
避免将主键与业务逻辑绑定。例如,不建议使用用户 ID(即使它是唯一的)作为主键。相反,使用一个无意义但唯一的标识符,例如:
- 一个 UUID,
- 一个自动递增的主键,
- 一个由雪花算法生成的主键。
6. 选择合适的字段长度
让我先问你一个问题:在数据库中,字段长度表示字符长度还是字节长度?
在 MySQL 中:
varchar
和char
指定字符长度。- 其他类型通常指定字节长度。
例如:
char(10)
表示 10 个字符。bigint(4)
指显示宽度(不是存储大小),但bigint
总是占用 8 个字节。
在设计表时,仔细考虑字段长度。例如,对于预期长度在 5 到 20 个字符之间的用户名字段,你可以将其定义为 username varchar(32)
。
提示:字段长度通常最好设置为 2 的幂次方(即,2ⁿ)。
7. 优先选择逻辑删除而非物理删除
物理删除:数据从磁盘上永久删除,释放存储空间。
逻辑删除 :添加一个字段(如 is_deleted
)来标记数据为已删除。
物理删除的示例:
sql
DELETE FROM account_info_tab WHERE account_no = '666';
逻辑删除的示例:
sql
UPDATE account_info_tab SET is_deleted = 1 WHERE account_no = '666';
为什么优先选择逻辑删除?
- 物理删除使得数据恢复变得困难。
- 自动递增的主键变得不连续。
- 对于核心业务表,最好更新状态字段而不是物理删除记录。
8. 避免单表字段过多
在设计表时,尽量限制字段数量 --- 通常不超过 20 个。
字段过多可能导致:
- 行尺寸过大,
- 查询性能差。
如果业务逻辑需要许多字段,考虑将大表拆分为具有相同主键的小表。
当一个表有大量字段时,可以将其拆分为:
- 一个"查询条件"表(用于快速过滤),
- 一个"详细"表(用于完整内容),以提高性能。
9. 尽量使用 NOT NULL
除非有特定原因,建议将字段定义为 NOT NULL
。
为什么?
- 防止空指针问题。
NULL
占用额外的存储空间。- 与
NULL
的比较更复杂,阻碍查询优化。 NULL
值可能导致索引失败。- 如果可以安全地将字段默认为一个空字符串或常量,你应该将其定义为
NOT NULL
。
10. 评估哪些字段需要索引
首先,评估你的数据集的大小。如果一个表只有几百行,可能不需要索引。
一般来说,如果一个字段经常用于查询条件,它应该有一个索引。但索引不应被过度使用:
- 避免创建过多的索引 --- 每个表保持在 5 个或更少。
-
- 过多的索引会减慢插入和更新操作。
- 不要为低基数的字段(例如,性别)创建索引。
- 注意可能导致索引失败的情况,例如在索引字段上使用内置的 MySQL 函数。
- 为了减少索引数量,考虑复合(多列)索引。
-
- 使用覆盖索引 技术,并遵循最左前缀规则。
用户表的示例:
sql
CREATE TABLE user_info_tab (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`create_time` datetime NOT NULL,
`modifed_time` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
由于你可能会按 user_id
或 name
查询,并且 user_id
是唯一的,你可以这样定义索引:
sql
CREATE TABLE user_info_tab (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`create_time` datetime NOT NULL,
`modifed_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_name` (`name`) USING BTREE,
UNIQUE KEY un_user_id (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
11. 避免使用 MySQL 保留字
如果数据库名、表名或字段名包含保留字,在 SQL 语句中必须用反引号(`)将其括起来。这使得编写 SQL 语句变得复杂,并增加了出错的风险,特别是在脚本编写或与 shell 变量一起使用时。
因此,避免使用 MySQL 保留字,如 select
、interval
、desc
等。
12. 优先选择 InnoDB 存储引擎
在创建表时,你必须选择一个存储引擎。一般来说,除非你的读写比例小于 1%,在这种情况下可以考虑使用 MyISAM ,否则应优先选择 InnoDB。
13. 选择合适的时间类型
在设计表时,我们通常会包含诸如 create_time
、modified_time
等与时间相关的通用字段。那么应该使用哪种 MySQL 时间类型呢?
主要选项包括:
date
:以yyyy-mm-dd
格式存储日期值。范围:1000-01-01
到9999-12-31
。大小:3 字节。time
:以hh:mm:ss
格式存储时间值。范围:-838:59:59
到838:59:59
。大小:3 字节。datetime
:以yyyy-mm-dd hh:mm:ss
格式存储日期和时间。范围:1000-01-01 00:00:00
到9999-12-31 23:59:59
。大小:8 字节。不依赖时区。timestamp
:以时间戳(yyyymmddhhmmss
)格式存储日期和时间。范围:1970-01-01 00:00:01
到2038-01-19 03:14:07
。大小:4 字节。依赖时区。year
:以yyyy
格式存储年份值。范围:1901
到2155
。大小:1 字节。
建议 :优先使用 datetime
类型来存储日期和时间,因为它具有更大的范围并且不依赖时区。
14. 安全考虑
- 数据加密:敏感信息(如用户密码)应以加密格式存储。
- 数据脱敏:对于个人身份识别信息(PII),如电话号码或电子邮件地址,数据脱敏以更好地保护隐私和合规性。
原文:www.yuque.com/fengjutian/... 《设计 MySQL 表的 14 条原则》