- 浮点数用decimal类型,不要用double和float,因为他俩是近似,不准确。
- 状态类型用tinyint。
- 时间类型用datetime,不推荐用时间戳timestamp,因为timestamp只到2038年,除非你的业务只能活到2038年,非要用时间戳的话就用bigint来替代timestamp。
- 自增主键推荐用bigint,因为业务增长快的话,它范围更大,绝对够你用了。
- 比varchar更大的字段,比如text等等,就不推荐用mysql来存储了,这些大字段性能低于varchar,而且占用内存更多。
比如有这样一个表:
1 | CREATE TABLE `LOGS` ( |
这个设计行为就很颠,推荐改成两个表:
1 | CREATE TABLE `LOGS` ( |
- 如果非要用大字段,那么就“压缩存储”或者“单独表存储”,好处是“降低binlog大小”和“降低engine到server的传输”。
- 比如一个很大的json如果只是更新一个小value,那么用text的话,性能得不偿失,因为要“读出来再写进去,这里面有大量网络交互,同时还要写到binlog里,有大量性能损耗,还会强制生成硬盘临时表”。
- 数据库的字段越简单越好,大小越小,16k索引页就能放越多的索引字段,查起来更快。B+树高降低。
- op操作可能会导致抖动和主从延迟,建议放业务低峰期操作。
- 大sql会造成脏页,会刷binlog,要避免。
- 长事务可能会有很长时间的锁。
- 单个数据库实例不要订阅太多东西,比如一个实例上绑了好几个数据传输dts,因为一次数据库的更新变更,可能会binlog爆发好几个,对物理机的网卡会有影响。
- 尽量不要用数据库去计算!把计算交给代码去实现。也尽量不要去整体的join。
- 数据库字段越精确越好,用where去过滤的话,比如数字的比较成本比字符串的比较成本更低。字符串要进行编码转换且要挨个比对,有一个排序的过程。
- 尽量多用组合索引,比如索引 : idx_abc(a,b,c) 能走索引的Case:
1
2
3
4a. where a=1 / where a=1 order by b / where a=1 order by b,c (利用a过滤,b,c排序)
b. where a=1 and b=1 / where a=1 and b=1 order by c (利用a,b过滤,c排序)
c. where a=1 and b=1 and c=1
注意:如果后面的排序需要走到索引,需要保证前面的过滤必须是等值 - 唯一键是最好的索引。
- 尽量不要用null,因为null的优化很复杂,也会让索引统计变得复杂。而且null占用的空间更多,尽量用0,’’这样的东西。
- 不要让索引参与计算。比如 select id from XXX where id+1=5;这句话不会让mysql先算出来id=4,反而会把索引的值都取出来一个一个比对,直到=5,这样会导致全表扫和全索引扫。
- 索引是不是越多越好?不是的。很多开发在创建索引时想法很简单,就是希望动态生成SQL时,无论传什么where条件,都可以走到一个对应的索引,创建了一大片索引。但很明显这是一个反面教材,把数据库当成了搜索引擎,这样建索引对数据库来说,基本是个事故现场。原因是:
- 因为“当执行DML语句时,除了维护正常的表数据之外,还需要同步更新索引列的数据,索引越多,写放大效应越厉害”。
- 索引为了维护其有序性,更新是删除旧值再在对应顺序位置插入新值,如果新插入位置所在的数据块已经写满,那就需要把当前数据块部份内容移动一个新的数据块,再修改前后叶子块甚至是分枝块的链式指针,然后再写入数据,这就是我们前面说的块分裂,块分裂非常影响数据库性能,那么索引越多,也会加大了块分裂的可能性。
- MySQL优化器相对其他商业数据库还外于一个很初级的阶段,它每次执行SQL时都会重新生成执行计划,而在生成执行计划时,如果有多个索引可被使用时,这些索引会进入possible_key,然后优化器会对每一个possible_key进行采样并计算数据分布情况,进而最终确认使用哪个真实的索引(key)去执行SQL。