# mysql设计规范

## 引擎规范

所有表均使用InnoDB存储引擎，默认字符使用utf8；

## 主键规范

所有表均要有一个自增主键id，以提高插入性能，节约空间，减少页分裂，满足后续横向扩展需要；

杜绝使用联合主键，采用自增主键+联合唯一索引实现；

## 命名规范

库名、表名、字段名使用英文字母、数字、下划线组成，全部小写，并以英文字母开头，禁止使用连字符（-），避免使用关键字和保留关键字作为表名（MySQL 5.7关键字清单参考：<https://dev.mysql.com/doc/refman/5.7/en/keywords.html；>

命名须见名知意，且不易过长，一般不超过三个英文单词；

用单数形式表示名称，例如，使用app\_version，而不是app\_versions；

非必要情况下不使用TEXT、BLOB类型\
MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型，如果查询中包含这样的数据，在排序等操作时，就不能使用内存临时表，必须使用磁盘临时表进行，效率低下，同时该数据类型不能设置默认值、不便于排序、不便于建立索引；

避免null值出现\
null值在MySQL内部需要进行特殊处理，增加数据库处理记录的复杂性；同等条件下，表中有较多空字段的时候，数据库的处理性能会降低很多；null值很难查询优化且占用额外的索引空间，尽量提供默认值，或使用数字0/空字符串等代替null。

使用varchar(n) n值要节制\
n设定为满足业务需要的长度即可，更长的字段会消耗更多的内存，因为MySQL通常会分配固定大小的内存块来保存值，这对排序或使用基于内存的临时表尤其不好。同样的事情也会发生在使用文件排序或者基于磁盘的临时表的时候。

小数类型为decimal，禁止使用float和double\
float和double在存储的时候，存在精度损失的问题，很可能在值的比较时，得到不正确的结果。如果存储的数据范围超过decimal的范围，则将数据拆成整数和小数分开存储（一般很少遇到）。

禁止使用存储过程、视图、触发器、Event\
高并发业务下，架构设计思路是“解放数据库CPU，将计算转移到服务层”，并发量大的情况下，这些功能很可能将数据库拖死，业务逻辑放到服务层具备更好的扩展性，能够轻易实现“增机器就加性能”，数据库擅长存储与索引，CPU计算还是上移到服务层。

避免在数据库中存储文件等大的二进制数据\
若在数据库表中存储文件，且文件很大，当数据库进行读取操作时，会进行大量的随机IO操作，大文件使得IO操作非常耗时耗性能，同时也会造成短时间内数据量快速增长。

单个表的数据量大小控制在500万以内\
500万并不是MySQL数据库的极限，但数据量太多不利于对表结构进行修改、备份和恢复数据，适当采用分表等手段来控制单表数据量的大小。

## 索引设计规范

InnoDB存储引擎的索引的数据结构是B+Tree，B+Tree的查询效率很高，建立索引可以提升SQL的查询性能，建议大家在必要字段积极使用索引来提升查询效率。

索引命名约束\
非唯一索引以“idx\_字段1\_字段2”命名，唯一索引以“uniq\_字段1\_字段2”命名；

索引数量限制\
仅在需要建立索引的字段创建索引，单字段索引尽量不要超过三个，单表索引数量尽量不超过五个，避免冗余和重复索引；

索引字段要求\
不在低基数列上建立索引，例如状态、类型等，索引选择性 = 基数 / 数据行数，值越接近1说明使用索引的过滤效果越好，不在频繁更新的列上建立索引；

索引SQL约束\
不在索引列进行数学运算和函数运算，参与了运算的列不会使用索引；

普通索引 VS 唯一索引\
除非业务须要唯一约束，一般采用普通索引而不是唯一索引；

普通索引的更新操作性能比唯一索引更好，因为普通索引能利用change buffer来做更新操作；而唯一索引要判断更新的值是否是唯一的，每次都需要将磁盘中的数据读取到 buffer pool中，插入性能较低。

复合索引\
当需要频繁使用固定几个字段进行条件查询时，可以建立对应的复合索引，要注意将单独出现最多的字段放在前面，以满足更多的查询语句；

如现在我们有两个字段a和b经常同时作为查询条件：

A: select \* from t where a = 1 and b = 2;

也有很多查询会单独使用字段a作为查询条件：

B: select \* from t where a = 2;

此时我们建立复合索引index(a,b)，那么A和B两条语句均可以使用到索引，如果建立的复合索引为index(b,a)，则只有A语句可以使用索引；

建立索引index(a,b,c),相当于建立了index(a)，index(a,b)和index(a,b,c)三个索引。

最左前缀匹配原则\
如果我们使用的是复合索引，需要遵循最左前缀匹配原则。MySQL会从左向右匹配，遇到范围查询(>、 3 and d = 4;

应该建立的复合索引是：index(a,b,d,c) 而不是 index(a,b,c,d)。因为字段c是范围查询，当MySQL遇到范围查询就停止索引的匹配了；

覆盖索引\
通过覆盖索引避免回表，大幅提升查询效率；

场景一：只查询主键作为返回值，主键索引（聚簇索引）的叶子节点是整行数据，普通索引（二级索引）的叶子节点是主键的值，当我们只查询主键值时，可以直接获取对应叶子节点的内容，而避免回表。

场景二：查询字段就在索引中，假如此时我们有一个复合索引index(name,age)，有一条SQL：select name,age from t where name like '时%'；由于是字段 name 是右模糊查询所以可以走复合索引，匹配到name时，不需要回表，因为查询字段name和age已经在索引中，直接返回索引值就可以了。

前缀索引\
巧妙的使用前缀索引，避免索引值过大；

例如有一个字段是addr varchar(255)，如果整个建立索引index(addr)，会很浪费磁盘空间，可以选择建立前缀索引index(addr(64))。

前缀索引避免不了回表，也就无法使用覆盖索引这个优点，因为索引值只是字段的前n个字符，需要回表才能判断查询值和字段值是否一致；

建立前缀索引，须要关注字段的区分度。例如像身份证号码这种字段的区分度很低，只要出生地一样，前面多个字符都是一样的，会导致大量的回表，效率极低；可以采取倒序存放的方式进行优化。

扩展索引 VS 新增索引\
当前索引不满足查询需要时，我们应尽量扩展索引，而不是新增索引，一个表的索引越多，更新操作耗费性能越多。

where条件的字段顺序不需要与索引顺序一致\
where条件里的字段顺序与索引顺序无关，优化器会自动调整，不需要刻意调整查询条件在语句中的顺序；

建立索引后，最好先explain，确定索引使用符合预期；\
SQL使用规范\
OR优化\
单字段场景：采用in代替，in可以有效利用索引；

多字段场景：在Innodb引擎下or关键字无法使用组合索引，为避免全表扫描，可以分别建立单字段索引，然后采用union进行组合。

IN优化

可以采用join代替in的方式进行查询，提高查询效率；

Like优化\
like用于模糊查询，但如果是全模糊查询，将不能命中对应字段的索引，应尽量采用右模糊查询，如select name,age from t where name like '时%'；而不是select name,age from t where name like '%时%'；

Group By优化\
尽量让group by过程用到表的索引，不但不需要临时表，还不需要额外的排序；

避免在where条件的属性上使用函数或者表达式\
计算逻辑尽量在服务层的业务逻辑中进行；

拆分大的delete或insert语句\
如须要delete或insert大批量数据，须要拆分成多条语句执行；

非必要情况下不使用select\
select 会增加cpu/io/内存/带宽的消耗，且指定字段能有效利用覆盖索引；

服务层必须捕获可能的SQL异常，并有相应处理

ORM使用规范\
关联增删改操作放在事务中执行，以保证原子性

杜绝在事务中执行IO操作，如发送API请求等

与数据库无关操作，避免放在事务中执行，减少事务持续时间

增删改操作放在事务最后执行，减少锁占用时间

建议使用relationship，但杜绝将lazy参数设为False

建议在model中自定义to\_dict方法，对数据进行序列化

表初始化脚本中定义表结构务必与ORM中定义一致，杜绝留坑

谨慎设置级联删除，避免误操作

谨慎使用execute方法直接执行SQL语句，如使用需做严格参数检查，防止SQL注入


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://qiangrens-organization.gitbook.io/qkd90/shu-ju-ku-he-zhong-jian-jian/mysql-she-ji-gui-fan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
