云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

设计 MySQL 表的 14 条原则

jxf315 2025-04-26 19:01:45 教程文章 18 ℃

1.设计表时包含这些通用字段

通常,一个表应包含以下字段:

  • id:主键。每个表都必须有一个主键 — 没有例外。
  • create_time:创建时间 — 必须包含。
  • modified_time:最后修改时间 — 必须包含。每当记录被更新时,这个字段应该被更新。
  • version:记录的版本号,通常用于乐观锁 — 可选。
  • modifier:最后修改记录的人 — 可选。
  • creator:创建记录的人 — 可选。

2.每个字段都应该有注释,尤其是枚举类型

在设计表时,每个字段都应该包含注释。这对于使用枚举的字段尤为重要 — 所有枚举值都应该在注释中列出。如果以后发生更改,也应该在注释中反映出来。

坏例子:

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)
);

好例子:

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)来标记数据为已删除。

物理删除的示例:

DELETE FROM account_info_tab WHERE account_no = '666';

逻辑删除的示例:

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 函数。
  • 为了减少索引数量,考虑复合(多列)索引
    • 使用覆盖索引技术,并遵循最左前缀规则。

用户表的示例:

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 是唯一的,你可以这样定义索引:

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),如电话号码或电子邮件地址,数据脱敏以更好地保护隐私和合规性。

原文:
https://www.yuque.com/fengjutian/eyzi2i/muqhrehwm8e3ac3w?singleDoc# 《设计 MySQL 表的 14 条原则》

最近发表
标签列表