「MySQL」 - SQL设计规范

苦于知乎没有后台文章管理页面,只能把相关文章手动做成索引。

i.9ish:「MySQL」 – SQL设计规范

i.9ish:「MySQL」 – SQL执行计划

i.9ish:「MySQL」 – 分区表

i.9ish:「MySQL」 – SQL查询语句执行流程

i.9ish:「MySQL」 – SQL更新语句执行流程

i.9ish:「MySQL」 – 事务隔离

i.9ish:「MySQL」 – 数据库索引-上

i.9ish:「MySQL」 – 数据库索引-下

i.9ish:「MySQL」 – 全局锁和表锁

i.9ish:「MySQL」 – 行锁

i.9ish:「MySQL」 – SQL Cheat Sheet – 未完成

i.9ish:「MySQL」 – SQL练习 – 未完成

i.9ish:「MySQL」 – 自增主键id

一、数据库命名规范

A、对象名称使用小写字母、下划线分割

  • Windows默认情况下无法建立大写库名
  • Linux大小写敏感,MySQL数据文件(库名、表名、表别名严格区分大小写)
    • Linux查询大小写敏感
    • Windows查询大小写不敏感
    • MySQL 5.6,lower_case_table_names,0:表示区分大小写、1:表示不区分大小写
    • MySQL 5.7,lower_case_table_names,2:表示区分大小写、1:表示不区分大小写
  • 统一命名使用全小写字母和下划线分割
  • 列名与列表名在所有情况下忽略大小写

B、对象名禁止使用MySQL关键字/保留字

  • 使用关键字,建表时不会报错,CURD时会提示SQL语句错误
    • 对于SQL语句中的关键字,写SQL时需要单引号包围,该要求难以统一,还是不使用关键字作为表字段
    • 关键字列表 – 9.3 Keywords and Reserved Words

C、对象命名做到见名知意,限定在32个字符范围内

  • 字符过长增加网络传输开销

D、临时表,tmp前缀、日期作为后缀,方便筛选、倒库、清理

E、备份表,bak前缀、日期作为后缀,方便筛选、倒库、清理

  • 大表,导出为SQL文件进行备份

F、存储相同数据的列名和列类型必须一致

  • 关联列,类型不一致会导致隐式类型转换,引起索引失效,降低查询效率

二、基本设计规范

A、统一使用InnoDB存储引擎(5.6之后为默认)

  • TODO MySQL不同存储引擎比较

B、统一使用utf8字符集

  • 只存储中文字符,可以使用GBK、GB2312
  • 对于emoj需要使用utf8mb4
    • TODO utf8和utf8mb4之前还有争论,关注讨论结论
  • 编码转换容易导致乱码,以及索引失效

C、所有表和字段都需添加注释

  • 建库时维护好数据字典

D、控制数据量大小,控制在500w行

  • InnoDB未做最大行限制,受限于存储设备和文件系统
  • 分库分表
    • TODO 分库分表策略

E、谨慎使用MySQL分区表

  • 分区表在物理上表现为多个文件,逻辑上表现为一个文件
    • 需要把多个物理文件分布于磁盘阵列,才能提高IO利用率
  • 减少跨分区查询
  • 更倾向于使用物理分表方式管理海量数据

F、减小表宽度、尽量做到冷热数据分离

  • MySQL限制单表最多存储4096列
  • 每行数据大小不超过65535 BYTE = 64K
  • 控制宽度,对列进行垂直拆分
  • 减少IO,保留热数据的内存缓存命中率
  • 使用字段进行查询,避免读入无用的冷数据

G、禁止在表中建立预留字段

  • 预留字段难以做到见名知意
  • 预留字段难以确认存储类型
    • 修改类型,会引起全表锁定
  • 修改表字段代价 >> 增加表字段

H、禁止在数据库存储图片、文件等bin数据

I、禁止在线上库做压力测试

J、禁止从开发环境、测试环境直连生产环境数据库

三、索引设计规范

A、不滥用、乱用索引

  • 建议单表索引不超过5个
  • 提高查询效率,降低插入和更新效率
  • MySQL优化器,会评估索引,生成最优执行计划,索引过多,导致生成执行计划时间过长,降低效率

B、InnoDB表,必须有一个主键 – B树

  • InnoDB使用主键按照顺序组织表结构
  • 如果没有主键,会按照表顺序,选择第一个非空唯一索引组织表结构
  • 如果没有符合以上规则的,MySQL会自动生成6BYTE主键(性能不佳)、
  • 不适用频繁更新的列作为主键
  • 不使用联合索引作为主键
  • 不使用UUID、MD5、HASH、字符串作为主键(无法保证顺序增长)
  • 建议选择/使用自增ID列
  • TODO 索引于B树

C、常见索引列

  • SELECT、UPDATE、DELETE语句中WHERE从句中的列
  • 包含ORDER BY、GROUP BY、DISTINCT中的列
  • 多表的JOIN关联列

D、索引列顺序

  • 联合索引从左向右使用
  • 区分度最高的列(主键、唯一),置于联合索引的最左侧
  • 字段长度小的列置于联合索引的最左侧
    • 元素占用空间小,内存页中元素多,索引速度快
  • 使用最频繁的列,置于联合索引的最左侧
  • 避免冗余和重复索引
  • 对于频繁的查询,优先考虑使用覆盖索引(所有的列都建立索引)
    • 避免InnoDB索引的二次查找
    • 把随机IO变为顺序IO加快查询效率
  • 避免使用外键
    • 外键用于保证数据的参照完整性,建议置于业务端实现
    • 外键影响父表和子表的写操作,从而降低性能
    • 不建议使用外键约束,但一定在表与表之间的关联键上建立索引

上述描述中有一条联合索引最左侧,使用数据区分度高的列。关于区分度,唯一值和总行数比值,区分度越高(接近1)。

SELECT
COUNT(DISTINCT cat_1)/COUNT(*) AS cat_1_rate,
COUNT(DISTINCT cat_6)/COUNT(*) AS cat_6_rate
FROM OuterCooperationDB.product_core;

+------------+------------+ | cat_1_rate | cat_6_rate |
+------------+------------+ |     0.0002 |     0.0381 |
+------------+------------+

四、字段设计规范

A、优先选择符合存储需要的最小数据类型

  • 将字符串转化为数字类型存储(INET_AION、IP->Integer;INET_NTOA、Integer->IP)
  • 对于非负整数,优先使用无符号整型存储(id)
  • VARCHAR(N)代表的是字符数,不是BYTE数
    • VARCHAR(255),可以保存255中文字符,使用UTF8,实际占用765BYTE
  • 过大长度消耗更多的内存

B、避免使用TEXT、BLOB数据类型

  • TinyText、Text(64K)、MidumText、LongText
  • 进行排序等查询,无法使用内存临时表,而必须使用磁盘临时表
  • 读取数据时,需要二次查询
  • 非用不可的情况,可以把BLOB和TEXT拆分到单独的拓展表中

C、避免使用ENUM数据类型

D、尽可能把所有列定义为NOT NULL

  • 索引NULL列,需要额外的空间,要占用更多的空间
  • 进行比较和计算时,需要对NULL值做特别的处理

E、使用TIMESTAMP或DATETIME类型存储时间

  • 禁止使用字符串存储日期型数据
    • 无法用日期函数进行计算和比较
    • 使用字符串存储占用更多的空间
  • TIMESTAMP(4BYTE),DATETIME(8BYTE)
    • TIMESTAMP范围小,1970-01-01 00:00:01 – 2038-01-19 03:14:07
    • DATETIME范围大

F、金额相关类型,使用decimal类型存储

  • float、double不精确
  • decimal精确
    • decimal占用空间由宽度决定
    • 可以存储比bigint更大的整数数据

五、SQL开发规范

A、建议使用预编译语句进行数据库操作

  • 重复使用执行计划、减少编译所需时间
  • 传递参数比传递SQL语句,减少网络IO
  • 避免动态SQL导致的注入问题
mysql> PREPARE stmt
    -> FROM 'SELECT SQRT(POW(?,2) + POW(?, 2)) AS hypotenuse';
mysql> SET @a = 3;
mysql> SET @b = 4;
mysql> EXECUTE stmt USING @a, @b;
mysql> DEALLOCATE PREPARE stmt;

B、避免类型隐式转换

  • 常见于WHERE从句,列类型与参数类型不一致,可能出现隐式转换
  • 隐式转换导致索引失效

C、充分利用表中已存在的索引

  • 避免使用双%的查询条件,如a like %.log%
  • 避免使用前置%的查询条件;对于后置%,可以利用列上索引
  • 一个SQL只能利用联合索引中的一列进行范围查询
  • 使用LEFT JOIN或NOT EXISTS来优化NOT IN查询

D、程序连接不同数据库,需使用不同账号,禁止跨库查询

  • 为数据库迁移和分库分表留出余地
  • 减低业务耦合度
  • 避免权限过大而产生的安全风险

E、禁止使用SELECT *进行查询

  • 避免消耗CPU和网络IO
    • TEXT类型还会进行二次读取
  • 无法使用覆盖索引
  • 减少表结构变更对程序带来的影响

F、禁止使用不含字段列表的INSERT语句

  • 减少表结构变更对程序带来的影响

G、避免使用子查询

  • 子查询结果集无法使用索引,结果集数据量大则严重影响效率
  • 子查询会产生临时表,消耗CPU和IO资源,引起慢查询
  • 子查询可读性更高,但是会影响性能,可以优化为JOIN操作

H、避免使用JOIN关联太多的表

  • 每JOIN一个表会多占用关联内存(join_buffer_size)
  • MySQL最多允许61个表,建议不超过5个

I、减少通数据库交互次数

  • 数据库更适合批量操作
    • 提高SQL处理效率

J、使用IN代替OR语句

  • IN不要超过500个
  • IN操作可以有效利用索引

K、禁止使用ORDER BY RAND()进行随机排序

  • 会把所有符合条件的数据加载到内存中,进行排序
    • 通过业务代码完成随机数据获取

L、禁止WHERE从句中对列进行函数转换和计算

  • 导致索引失效,WHERE DATE(createtime) = ‘20180925’
  • 可以变通SQL,继续使用列索引,WHERE createtime >= ‘20180925’ AND createtime < ‘20180926’

M、(明显不会有重复值时)使用UNION ALL而不是UNION进行结果集合并

  • UNION会把所有数据放入临时表进行去重操作

N、拆分复杂大SQL为多个小SQL进行查询

  • MySQL不支持一个SQL的多CPU并行计算
  • SQL拆分后可以通过并行执行提高处理效率

六、数据库操作行为规范(开发、运维)

A、对于超过百万行的批量写操作,需要分批多次进行操作

  • binlog日志为row格式时会产生大量的日志
  • 大批量写操作,可能会导致严重的主从延迟
  • 涉及事务,对大量数据进行锁定,产生大量阻塞,导致可用链接被消耗

B、对于大表结构修改

  • pt-online-schema-change工具进行修改
  • TODO 原理
  • 避免大表修改产生的主从延迟
  • 避免对表字段修改时的锁表

C、禁止为程序使用账号赋予super权限(维护)

  • 遵循最小权限原则
  • 原则上不具有DROP权限
    原文作者:isisiwish
    原文地址: https://zhuanlan.zhihu.com/p/58063499
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞