通用命名规范
所有数据库对象名称,必须使用小写字母、并用下划线分割
所有数据库对象名称,禁止使用 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。
- 避免 Innodb 表进行索引的二次查询:
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_time
、last_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格式时会产生大量的日志