MySQL 表设计和使用规范

通用命名规范

所有数据库对象名称,必须使用小写字母、并用下划线分割

所有数据库对象名称,禁止使用 MySQL 保留关键字

  • 如果表名中包含关键字查询时,需要将其用单引号括起来;

所有数据库对象名称,必须见名识意,最好不要超过 32 个字符

字段名尽量不超过 30 个字符

临时库&表:必须以 tmp_为前缀并以日期为后缀,并加时间后缀

  • 如:temp前缀+模块+表+日期后缀:temp_user_eduinfo_20210719

备份表:必须以 bak_为前缀,并以日期 (时间戳) 为后缀

  • 如:bak前缀+模块+表+日期后缀:bak_user_eduinfo_20210719

unique key 以 uk_ 作为前缀

primary key 以 pk_ 作为前缀

普通索引一般以 idx_ 作为前缀

对于所有存储相同数据字段,的列名和列类型必须一致

  • 一般作为关联列,如果查询时关联列类型不一致、会自动进行数据类型隐式转换,会造成列上的索引失效,导致查询效率降低;

索引名尽量不超过 50 个字符

表设计规范

表设计应至少满足第三范式

  • 一些特殊场景允许反范式化设计,但需要对冗余字段的设计给出解释。

不同应用的表之间应尽可能减少关联

  • 尽量不使用外键对表之间进行关联,一般是由程序控制参照完整性,确保组件对应的表之间的独立性;
  • 这样会降低系统耦合度,提高灵活性和可扩展性,方便系统或表结构的重构。

表一般使用 Innodb 存储引擎

  • 没有特殊要求的情况下,所有表必须使用 Innodb 存储引擎(MySQL5.5 之前默认使用 Myisam,5.6 以后默认的为 Innodb)。
    • 如果是 Innodb 无法满足的功能如:列存储,存储空间数据等,另当别论。
  • Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。

尽可能别使用混合存储引擎

  • 绝对禁止使用混合存储引擎。

数据库和表的字符集统一使用 UTF8 / utf8mb4

  • UTF8 兼容性更好;
  • 统一字符集可以避免由于字符集转换产生的乱码;
  • 不同的字符集进行比较前需要进行转换会造成索引失效;
  • 如果数据库中有存储 emoji 表情的需要,字符集需要采用 utf8mb4 字符集。

主键选择必须满足以下条件

  • 唯一性
  • 非空性
  • 有序性
  • 可读性
  • 可扩展性

  • id,建议使 8字节 unsigned bigint(20) 作为主键的数据类型;

    无符号与有符号的区别: 有符号允许存储负数,无符号只允许从年初正数,无符号最小值为 0,最大值根据类型不同而不同。

  • 主外键的数据类型一定要一致;
  • 每个表中的主键命名保持一致;
  • 一般不使用varchar类型作主键;
    • varchar 不是有序的,可读性也不好;

不建议在数据库表中加外键约束

  • 在数据表中添加外键约束,会影响性能;
    • 例如: 每一次修改数据时,都要在另外的一张表中执行查询。
  • 在数据表中添加外键约束,耦合度太高,不利于解耦和扩展;
  • 建议在应用层,也就是代码层面,来维持外键关系。

选择合适的类型作为键

  • 状态类型用 tinyint,例如:性别等;
  • 时间日期使用 datetime,可读性高些;
  • 不要使用 text 和 blob 数据类型,特别是 blob 必须绝对禁止。

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

  • 使用 comment 从句添加表和列的备注;
  • 从一开始就进行数据字典的维护。

控制单表数据量在 500 万以内

  • 500 万并不是 MySQL 数据库的限制,过大会造成修改表结构,备份,恢复都会有很大的问题。
  • 可以用历史数据归档(应用于日志数据),分库分表(应用于业务数据)等手段来控制数据量大小。

单表字段数最多不要大于50个

  • 字段数过多的宽表,对性能影响很大。

谨慎使用 MySQL 分区表

  • 分区表在物理上表现为多个文件,在逻辑上表现为一个表;
  • 谨慎选择分区键,跨分区查询效率可能更低;
  • 建议采用物理分表的方式管理大数据。

尽量做到冷热数据分离,减小表的宽度

  • 减少磁盘 IO;
    • 保证热数据的内存缓存命中率(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的 IO);
  • 更有效的利用缓存,避免读入无用的冷数据;
  • 经常一起使用的列放到一个表中(避免更多的关联操作)。

禁止在表中建立预留字段

  • 预留字段的命名,很难做到见名识义。
  • 预留字段无法确认存储的数据类型,所以无法选择合适的类型。
  • 对预留字段类型的修改,会对表进行锁定。

禁止在数据库中存储图片、文件等大的二进制数据

  • 这类文件通常很大,会短时间内造成数据量快速增长;
  • 数据库进行数据库读取时,通常会进行大量的随机 IO 操作,文件很大时,IO 操作很耗时。
  • 一般情况下,存储于文件服务器,数据库只存储文件地址信息。

定期删除(或者转移)过期数据的表

  • 一般通过分表解决,常规做法是按照2/8法则,将操作频率较低的历史数据迁移到历史表中,按照时间或者则曾Id做切割点。

字段设计规范

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

  • 在进行排序和创建临时表一类的内存操作时,会使用字段的长度申请内存。
  • 列的字段长度越大,建立索引时所需要的空间也就越大;
    • 这样,一页中所能存储的索引节点的数量、也就越少也越少;
    • 在遍历时所需要的 IO 次数也就越多,索引的性能也就越差。
  • 示例:
    • 将字符串转换成数字类型存储,如:将 IP 地址转换成整形数据
    • MySQL 提供了两个方法来处理 ip 地址:
      • inet_aton,把 ip 转为无符号整型 (4-8 位)
      • inet_ntoa,把整型的 ip 转为地址

        插入数据前,先用 inet_aton 把 ip 地址转为整型,可以节省空间,显示数据时,使用 inet_ntoa 把整型的 ip 地址转为地址显示即可。

对于非负型的数据 (如自增 ID,整型 IP) 来说,要优先使用无符号整型 UNSIGNED INT 来存储

  • 这是因为,无符号 UNSIGNED INT 相对于有符号可以多出一倍的存储空间
      SIGNED INT -2147483648~2147483647
      UNSIGNED INT 0~4294967295
    

VARCHAR(N) 中的 N 代表的是字符数,而不是字节数

  • 使用 UTF8 存储 255 个汉字 Varchar(255)=765 个字节;
  • 过大的长度会消耗更多的内存。

UTF8 存储一个字符最大要 3 个字节

  • UTF-8编码是变长编码,通常汉字占3个字节;
  • Unicode编码一个英文等于两个字节,一个中文(含繁体)等于两个字节;

字符与字节的关系:

1个字符 = 1个字节 = 8bit(ACSII码下)

1个字符 = 2个字节 = 16bit(Unicode码下)

如无特殊需要,原则上单个 VARCHAR 型字段不允许超过 255 个字符

如无特殊需要,不使用MEDIUMTEXT、TEXT、LONGTEXT类型

避免使用 TEXT,BLOB 数据类型,最常见的 TEXT 类型可以存储 64k 的数据

  • 建议把 BLOB 或是 TEXT 列分离到单独的扩展表中,这是因为:
    • MySQL 内存临时表不支持 TEXT、BLOB 这样的大数据类型,如果查询中包含这样的数据,在排序等操作时,就不能使用内存临时表,必须使用磁盘临时表进行。
    • 而且对于这种数据,MySQL 还是要进行二次查询,会使 sql 性能变得很差,但是不是说一定不能使用这样的数据类型。
  • 如果一定要使用,建议把 BLOB 或是 TEXT 列分离到单独的扩展表中;
    • 注意:查询时一定不要使用 select * 而只需要取出必要的列,不需要 TEXT 列的数据时不要对该列进行查询。

TEXT 或 BLOB 类型只能使用前缀索引

  • 因为MySQL 对索引字段长度是有限制的,所以 TEXT 类型只能使用前缀索引,

TEXT 列上是不能有默认值

当字符数量可能超过 20000 个的时候,才可以使用 TEXT 类型来存放字符类数据

所有使用TEXT类型的字段必须和原表进行分拆,与原表主键单独组成另外一个表进行存放,与大文本字段的隔离

避免使用 ENUM 类型

  • 修改 ENUM 值需要使用 ALTER 语句;
  • ENUM 类型的 ORDER BY 操作效率低,需要额外操作;
  • 禁止使用数值作为 ENUM 的枚举值。

如果业务允许,尽可能把所有列定义为 NOT NULL

  • 这是因为索引 NULL 列,需要额外的空间来保存,所以要占用更多的空间;
  • 进行比较和计算时要对 NULL 值做特别的处理

NULL 值处理规范

  • NULL 不代表 0,也不代表 0 长度的字符串;
  • NULL 含义:
    • 真未知;
    • 尚未知;
    • 不适用;

NULL 的长度不是 0 !

  • 同一个数据库,对于 Null 值的处理应有统一的原则;
  • 数据质量要求不高的,默认值可以为 Null,然后对特殊字段特殊处理;
  • 数据质量要求较高的,默认值均为 Not Null,然后特殊字段特殊处理;

如无特殊需要,所有字段必须有默认值

  • 默认值的设置,需要根据业务来确定。

使用 TIMESTAMP(4 个字节) 或 DATETIME 类型 (8 个字节) 存储时间

  • TIMESTAMP 存储的时间范围 1970-01-01 00:00:01 ~ 2038-01-19-03:14:07
  • TIMESTAMP 占用 4 字节和 INT 相同,但比 INT 可读性高
  • 超出 TIMESTAMP 取值范围的使用 DATETIME 类型存储

不要用字符串存储日期型的数据

  • 缺点:
    • 无法用日期函数进行计算和比较,查询时性能会极慢
    • 用字符串存储日期要占用更多的空间

同财务相关的金额类数据必须使用 DECIMAL 类型,严禁使用 FLOAT 和 DOUBLE

  • 不要使用非精准浮点:float,double,必须使用精准浮点:decimal
  • Decimal 类型为精准浮点数,在计算时不会丢失精度
  • Decimal 占用空间由定义的宽度决定,每 4 个字节可以存储 9 位数字,并且小数点要占用一个字节
  • 可用于存储比 bigint 更大的整型数据

索引设计规范

索引的设计目的

  • 建立索引的目的,是希望通过索引进行数据查找,减少随机 IO,增加查询性能;
    • 索引能过滤出越少的数据,则从磁盘中读入的数据也就越少。

限制每张表上的索引数量,建议单张表索引不超过 5 个

  • 过多索引会降低查询速度
    • 索引可以增加查询效率,但同样也会降低插入和更新的效率,甚至有些情况下会降低查询效率。
    • 每个索引都需要占⽤用磁盘空间,索引越多,需要的磁盘空间就越大。
    • 原因是:
      • MySQL 优化器在选择如何优化查询时,会根据统一信息,对每一个可以用到的索引来进行评估,以生成出一个最好的执行计划;
      • 如果同时有很多个索引都可以用于查询,就会 增加 MySQL 优化器生成执行计划的时间,同样会降低查询性能。
  • 修改表时,索引过多会很麻烦
    • 修改表时,对索引的重构和更新很麻烦
    • 越多的索引,会使更新表变得很浪费时间

禁止给表中的每一列都建立单独的索引

  • 5.6 版本之前,一个 sql 只能使用到一个表中的一个索引;
  • 5.6 以后,虽然有了合并索引的优化方式,但是还是远远没有使用一个联合索引的查询方式好。

每个 Innodb 表必须有个主键

  • Innodb 是一种索引组织表:数据的存储的逻辑顺序和索引的顺序是相同的。
  • Innodb 是按照主键索引的顺序来组织表的,每个表都可以有多个索引,但是表的存储顺序只能有一种。

  • 注意:
    • 不要使用更新频繁的列作为主键,不适用多列主键(相当于联合索引)
    • 不要使用 UUID,MD5,HASH,字符串列作为主键(无法保证数据的顺序增长)
    • 主键建议使用自增 ID 值

为经常需要排序、分组和联合操作的字段建立索引

  • 经常需要order by、group by、distinct和union等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效的避免排序操作
  • 字段选择:
    • 出现在 SELECT、UPDATE、DELETE 语句的 WHERE 从句中的字段;
    • 包含在 ORDER BY、GROUP BY、DISTINCT 中的字段;

      注意:并不要将符合前面两条的字段每一个都建立索引,通常将这些字段建立联合索引效果更好;

  • 多表 join 的关联列;

经常被用来过滤记录的字段

  • primary key 字段,系统自动创建主键的索引
  • unique key 字段,系统自动创建对应的索引
  • foreign key 约束所定义的作为外键的字段
  • 在查询中用来连接表的字段

尽量使用数据量少的索引

  • 如果索引的值很长,那么查询的速度会受到影响
  • 例如:对一个char(100)类型的字段进行全文检索,需要的时间肯定比对char(10)类型的字段需要的时间更多

区分度最高的列放在联合索引的最左侧,

  • 区分度=列中不同值的数量/列的总行数;

字段长度小的列放在联合索引的最左侧

  • 因为字段长度越小,一页能存储的数据量越大,IO 性能也就越好

使用最频繁的列放到联合索引的左侧

  • 这样可以比较少的建立一些索引

避免建立冗余索引和重复索引

  • 冗余索引和重复索引,增加了查询优化器生成执行计划的时间
  • 重复索引示例:
    • primary key(id)、index(id)、unique index(id)
  • 冗余索引示例:
    • index(a,b,c)、index(a,b)、index(a)

对于频繁的查询,优先考虑使用覆盖索引

  • 覆盖索引:就是包含了所有查询字段 (where,select,ordery by,group by 包含的字段) 的索引

  • 覆盖索引的好处:

    • 避免 Innodb 表进行索引的二次查询:
      • Innodb 是以聚集索引的顺序来存储的,对于 Innodb 来说,二级索引在叶子节点中所保存的是行的主键信息;
      • 如果是用二级索引查询数据的话,在查找到相应的键值后,还要通过主键进行二次查询、才能获取我们真实所需要的数据。
      • 而在覆盖索引中,二级索引的键值中可以获取所有的数据,避免了对主键的二次查询,减少了 IO 操作,提升了查询效率。
    • 可以把随机 IO 变成顺序 IO 加快查询效率:
      • 由于覆盖索引是按键值的顺序存储的,对于 IO 密集型的范围查找来说,对比随机从磁盘读取每一行的数据 IO 要少的多;
      • 因此,利用覆盖索引访问时、也可以 把磁盘的随机读取的 IO 转变成索引查找的顺序 IO

text 和 blog 尽量使用前缀索引

  • 如果索引字段的值很长,最好使用值的前缀来索引
  • 例如:text和blog类型的字段,进行全文检索会浪费时间。如果只检索字段的前面的若干个字符,这样可以提高检索速度

删除不再使用或者很少使用的索引

  • 表中数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。
  • 库管理理员应当定期找出这些索引,将他们删除,从而减少索引对更新操作的影响。

有些情况不应该创建索引

  • 对于那些在查询中很少使用或者参考的列不应该创建索引。
  • 对于那些取值很少的列,如性别等等。
  • 对于那些定义为text、image和bit数据类型的列不应该增加索引。

使用排序字段做索引,可以减少排序步骤,提升查询效率

  • 索引覆盖排序字段,这样可以减少排序步骤,提升查询效率

索引 SET 规范

  • 尽量避免使用外键约束
    • 不建议使用外键约束(foreign key),但一定要在表与表之间的关联键上建立索引
    • 外键可用于保证数据的参照完整性,但建议在业务端实现
    • 外键会影响父表和子表的写操作,从而降低性能

键设计注意问题

primary key 一般使用自增代理键

  • primary key 应该是有序并且无意义的,由开发人员自定义,尽可能简短,并且是自增序列。

primary key 字段不允许更新

  • primary key 一旦写入,就不能再更新;

unique key 一般以 uk_ 作为前缀

  • 表中除 primary key 以外,还存在唯一性约束的,可以在数据库中创建以“uk_”作为前缀的唯一约束索引。

主键应该尽可能的小

  • 以降低索引的空间占用。

主键应该顺序增长(自增主键)

  • 相比于 UUID 的无序性,顺序主键不会对 innodb 造成过大的 IO 压力。
    • 顺序插入,不存在随机插入会导致的分页问题,性能很低。
    • 顺序读取,性能

AUTO_INCREMENT 约束的字段必须具备 NOT NULL 属性。

AUTO_INCREMENT 约束的字段只能是整数类型(TINYINT、SMALLINT、INT、BIGINT 等)。

提起预估 AUTO_INCREMENT 约束字段的最大值

  • 最大值受该字段的数据类型约束,如果达到上限,AUTO_INCREMENT 就会失效。

不要使用外键约束,外键约束由程序控制。

  • 在数据表中添加外键约束,会影响性能;
  • 外键约束由程序控制,可以降低耦合度,提升灵活性和可扩展性;

状态类型如 性别 用 tinyint

  • 尽可能选择小的数据类型,这样会有很多好处,比如服务端处理效率,传输等都会快些。

键的字段值应该避免 NULL

  • Null 字段存在巨大隐患;

表更新的复杂性

  • 多系统合并、需要多个自然键组合做主键时,尽量不要使用复合键;
  • 可以使用 Checksum key,即对多系统的自然键进行 MD5 计算,用 MD5 结果值作为单键主键;

SQL 语句复杂性

  • 当 SQL 需要多表关联的时候,SQL 复杂性会迅速增加。

空间占用

  • 自然键:
    • 维度表,需要空间比较少;
    • 事实表,通常需要更多空间;
    • 整体来说,需要更多空间;
  • 代理键:
    • 维度表,需要更多空间;
    • 事实表,需要更少空间;
    • 整体来说,需要更少空间;

SQL 语句资源消耗

  • 代理键为整型,查询性能较好;
  • 自然键如是 VARCHAR,性能比较差;
  • 单键的性能较好;
  • 复合键的性能差;
  • 当数据量达到百万级的时候,性能差异会凸显;

数据加载速度

  • 自然键:
    • 维度表,直接从源系统过数据,额外开销比较少;
  • 代理键:
    • 维度表,代理键插入必须计算最新插入的代理键,需要时间,但和自然键差别不大;

SQL 开发规范

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

  • 预编译语句,可以重复使用这些计划,减少 SQL 编译所需要的时间,还可以解决动态 SQL 所带来的 SQL 注入的问题。
  • 只传参数,比传递 SQL 语句更高效。
  • 相同语句可以一次解析,多次使用,提高处理效率。

避免数据类型的隐式转换

  • 隐式转换会导致索引失效,
  • 例如:
    select name,phone from customer where id = '111';
    

禁止对索引字段使用函数、运算符操作,会使索引失效

  • 避免索引失效的原则,实际上就是需要保证索引所对应字段的”干净度“。

充分利用表上已经存在的索引

  • 避免使用“双%号”的查询条件。
    • 如: a like '%123%'
    • 如果无前置%,只有后置%,是可以用到列上的索引的;
  • 一个 SQL 只能利用到复合索引中的一列进行范围查询。
    • 如:有 a,b,c 列的联合索引,在查询条件中有 a 列的范围查询,则在 b,c 列上的索引将不会被用到。
  • 在定义联合索引时,如果 a 列要用到范围查找的话,就要把 a 列放到联合索引的右侧;

  • 使用 left join 或 not exists 来优化 not in 操作,因为 not in 也通常会使用索引失效。

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

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

不要使用 SELECT * 必须使用 SELECT <字段列表> 查询

  • 原因:
    • 消耗更多的 CPU 和 IO 以网络带宽资源
    • 无法使用覆盖索引
    • 可减少表结构变更带来的影响

避免使用子查询,可以把子查询优化为 join 操作

  • 子查询性能差的原因:
    • 这是因为 子查询的结果集无法使用索引
      • 通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响;
      • 特别是对于返回结果集比较大的子查询,其对查询性能的影响也就越大。
    • 由于子查询会产生大量的临时表也没有索引,所以会消耗过多的 CPU 和 IO 资源,产生大量的慢查询。
  • 通常子查询在 in 子句中,且子查询中为简单 SQL(不包含 union、group by、order by、limit 从句) 时,才可以把子查询转化为关联查询进行优化。

避免使用 JOIN 关联太多的表

  • 在 MySQL 中,对于同一个 SQL 多关联(join)一个表,就会多分配一个关联缓存,如果在一个 SQL 中关联的表越多,所占用的内存也就越大。
    • MySQL 是存在关联缓存的,缓存的大小可以由 join_buffer_size 参数进行设置。
    • 如果程序中大量的使用了多表关联的操作,同时 join_buffer_size 设置的也不合理的情况下,就容易造成服务器内存溢出的情况,就会影响到服务器数据库性能的稳定性。
  • 同时对于关联操作来说,会产生临时表操作,影响查询效率,MySQL 最多允许关联 61 个表,建议不超过 5 个。

减少同数据库的交互次数

  • 数据库更适合处理批量操作,合并多个相同的操作到一起,可以提高处理效率。

对应同一列进行 or 判断时,使用 in 代替 or

  • in 的值不要超过 500 个;
  • in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。

禁止使用 order by rand() 进行随机排序

  • order by rand() 会把表中所有符合条件的数据装载到内存中,然后在内存中对所有数据根据随机生成的值进行排序,并且可能会对每一行都生成一个随机值;
    • 如果满足条件的数据集非常大,就会消耗大量的 CPU 和 IO 及内存资源。
  • 推荐在程序中获取一个随机值,然后从数据库中获取数据的方式。

模糊查询 ‘%value%’ 会使索引无效,但是 ‘value%’ 可以有效利用索引

  • 因为无法判断扫描的区间,SQL 变为全表扫描,但是:‘value%’是可以有效利用索引

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

  • 对列进行函数转换或计算时,会导致无法使用索引
  • 不推荐:
    where date(create_time)='20190101'
    
  • 推荐:
    where create_time >= '20190101' and create_time < '20190102'
    

在明显不会有重复值时使用 UNION ALL 而不是 UNION

  • UNION 会把两个结果集的所有数据,放到临时表中后再进行去重操作;
  • UNION ALL 不会再对结果集进行去重操作。

分页查询语句全部都需要带有排序条件,否则很容易引起乱序

使用逻辑删除而不是物理删除

  • 逻辑删除数据可用数据分析等,物理删除一旦删除,即不可恢复。

添加审计字段 add_time、last_update_time

  • 每个表中都应该有 add_timelast_update_time,在查询、以及问题查找定位时有诸多好处。

    `add_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
       
    `last_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
    

拆分复杂的大 SQL 为多个小 SQL

  • 大 SQL 逻辑上比较复杂,需要占用大量 CPU 进行计算的 SQL
  • MySQL 中,一个 SQL 只能使用一个 CPU 进行计算
  • SQL 拆分后可以通过并行执行来提高处理效率

避免使用子查询,可以把子查询优化为join操作

  • 通常子查询在in子句中,且子查询中为简单SQL(不包含union、group by、order by、limit从句)时,才可以把子查询转化为关联查询进行优化。

子查询性能差的原因

  • 子查询的结果集无法使用索引,通常子查询的结果集会被存储到临时表中,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能 会受到一定的影响;

  • 对于返回结果集比较大的子查询,其对查询性能的影响也就越大;

  • 由于子查询会产生大量的临时表也没有索引,所以会消耗过多的CPU和IO资源,产生大量的慢查询。

操作行为规范

超 100 万行的批量写 (UPDATE,DELETE,INSERT) 操作,要分批多次进行操作

  • 大批量操作可能会造成严重的主从延迟
    • 主从环境中,大批量操作可能会造成严重的主从延迟,大批量的写操作一般都需要执行一定长的时间;
    • 而只有当主库上执行完成后,才会在其他从库上执行,所以会造成主库与从库长时间的延迟情况
  • binlog 日志为 row 格式时会产生大量的日志
    • 大批量写操作会产生大量日志;
    • 特别是对于 row 格式二进制数据而言,由于在 row 格式中会记录每一行数据的修改,我们一次修改的数据越多,产生的日志量也就会越多,日志的传输和恢复所需要的时间也就越长,这也是造成主从延迟的一个原因。
  • 避免产生大事务操作
    • 大批量修改数据,一定是在一个事务中进行的;
    • 这就会造成表中大批量数据进行锁定,从而导致大量的阻塞,阻塞会对 MySQL 的性能产生非常大的影响。
    • 特别是长时间的阻塞会占满所有数据库的可用连接,这会使生产环境中的其他应用无法连接到数据库,因此一定要注意大批量写操作要进行分批

大表修改表结构一定要慎重,推荐使用 pt-online-schema-change

  • 对大表数据结构的修改一定要谨慎,会造成严重的锁表操作,尤其是生产环境,是不能容忍的。

  • pt-online-schema-change 工作原理:
    • pt-online-schema-change,首先会建立一个与原表结构相同的新表,并且在新表上进行表结构的修改;
    • 然后再把原表中的数据复制到新表中,并在原表中增加一些触发器,把原表中新增的数据也复制到新表中;
    • 在行所有数据复制完成之后,会把新表命名成原表,并把原来的表删除掉;
    • 这样做的核心原理,就是把原来一个 DDL 操作,分解成多个小的批次进行。
  • pt-online-schema-change 的有点:
    • 避免大表修改产生的主从延迟
    • 避免在对表字段进行修改时进行锁表

禁止为程序使用的账号赋予 super 权限

  • 当达到最大连接数限制时,还运行 1 个有 super 权限的用户连接,这个权限是用来做 DBA 问题处理的
  • super 权限只能留给 DBA 处理问题的账号使用

对于程序连接数据库账号,遵循权限最小原则

  • 程序使用数据库账号只能在一个 DB 下使用,不准跨库
  • 程序使用的账号原则上不准有 drop 权限

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

  • 防止开发测试误操作,导致生产事故;
  • 防止开发测试,影响数据库性能表现;

大批量写操作(UPDATE、DELETE、INSERT),需要分批多次进行操作

  • 大批量操作可能会造成严重的主从延迟,特别是主从模式下,大批量操作可能会造成严重的主从延迟,因为需要slave从master的binlog中读取日志来进行数据同步。
  • binlog日志为row格式时会产生大量的日志