1. 基础设置与规范
- 存储引擎的选择:
- InnoDB: 在95%以上的场景下,我会选择它。因为它支持事务(ACID) 、行级锁 (高并发写入的关键)、以及外键 和崩溃恢复能力。
- MyISAM: 只有在极特殊的只读场景,或者需要全文索引且不在意数据一致性时才会考虑(不过MySQL 5.6以后InnoDB也支持全文索引了)。
- 字符集的选择:
- 我通常会选择
utf8mb4。因为传统的utf8字符集在MySQL中最多支持3个字节,无法存储emoji表情和一些生僻的中文字符。utf8mb4是真正的4字节UTF-8编码,是兼容性最好的方案。 - 排序规则一般选择
utf8mb4_general_ci或utf8mb4_unicode_ci,后者在排序上更准确一些。
- 我通常会选择
2. 字段数据类型的选择
这里的原则是:"在满足业务需求的条件下,尽可能选择占用存储空间小的、简洁的数据类型"。这能显著提升查询效率。
- 整型:
- 根据值的范围选择
TINYINT、SMALLINT、MEDIUMINT、INT或BIGINT。比如age字段用TINYINT UNSIGNED就足够了,不需要用INT。
- 根据值的范围选择
- 字符型:
CHAR与VARCHAR:CHAR适合定长的字段(如MD5加密后的密码、身份证号),因为它是定长的,存取速度快。VARCHAR适合可变长度的字段(如用户名、地址),可以节省空间。注意不要为了省事把所有字符串都定义成VARCHAR(255)。
。
3. 约束与规范
- 主键设计:
- 必须要有主键。 InnoDB是索引组织表,必须要有主键。
- 推荐使用自增ID 或 雪花算法生成的分布式ID。
- 不建议使用业务主键(如身份证号),因为业务可能会变,而且长度通常比自增ID长,会导致辅助索引占用空间变大。
- 不建议使用UUID,因为UUID是无序的,在插入时会导致页分裂,影响写入性能。(簇索引数据会按主键顺序排好)
- 避免NULL:
- 我通常会尽量给字段设置
NOT NULL,并配合DEFAULT值(如空字符串或0)。 - 原因:NULL值占用更多存储空间(标志位),对索引的维护也更复杂,而且在
COUNT()、!=等运算中容易出错。
- 我通常会尽量给字段设置
4. 索引设计
在建表初期,至少要先把基础的索引设计好。
- 根据查询设计索引:
- 在
WHERE条件、ORDER BY、GROUP BY中频繁出现的字段,需要考虑建立索引。
- 在
- 区分度:
- 对于性别这种区分度极低的字段,建立索引意义不大(除非是组合索引的前导列)。
- 联合索引:
- 遵循最左前缀原则。我会把区分度高的字段放在左边。
- 尽量通过联合索引做 "索引覆盖"(即索引中已经包含了需要查询的所有字段),减少"回表"查询。
- 控制索引数量:
- 索引不是越多越好。虽然能加速查询,但会拖慢
INSERT、UPDATE、DELETE的速度,因为索引也需要维护。
- 索引不是越多越好。虽然能加速查询,但会拖慢
5. 扩展性与冗余字段
- 范式与反范式:
- 在数据量大的时候,为了减少表连接,我会适度地做反范式设计。例如在订单表里冗余一份商品名称,这样查询订单详情时就不用再去关联商品表,但代价是如果商品名称修改了,可能需要去同步更新历史订单(业务通常不允许修改历史订单,所以这一点刚好适用)。
6. 核心字段建议
基于实际开发经验,几乎每张表我都会建议包含以下三个字段,这在很多框架中也是标配:
id: BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY (主键)create_time: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP (创建时间)update_time: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP (更新时间) ------ 这样在更新数据时,时间戳会自动刷新,对排查数据变更非常有帮助。