今天收到报警,提示从库延时,首先当然是上去查看情况,首先查看机器负载,如下:
可以看到使用cpu已经100%,io没有等待。那么查看mysql是什么情况,执行show processlist没有发现任何异常,执行show slave status查看延时,发现延时一直在增加,且卡在了某个pos点不动了,已经hang住了。这个从库没有跑任何业务的。
继续查下去,执行show engine innodb status查看一下有没有异常:
我擦,还真发现了问题,怎么会提示锁住了23张表?继续排查,根据卡住的pos点,解析binlog,发现一直在操作某张表,而且是非常简单的delete语句。查看该表发现是分区表。 刚好分了23个分区。仔细再看该表结构,发现只有普通索引,没有唯一索引,没有主键。也就解释了上面显示锁住了23张表。找到原因以后,添加唯一索引以后问题解决(添加与业务无关的自增ID做主键也可以解决。)负载下降,延时慢慢减小。哎,历史遗留下来的坑,慢慢踩吧。每个表都要显式指定主键,如果没有指定主键的话,会导致在row模式下,每次修改都要全表扫描,尤其是大表就非常可怕了,延迟会更严重,甚至导致整个slave库都被挂起。
总结:
对于innodb的表议用自增列做主键,在无特殊需求的情况下,建议使用与业务无关的自增ID作为主键。
InnoDB引擎表的一些关键特征:
1. InnoDB引擎表是基于B+树的索引组织表(IOT);
2. 每个表都需要有一个聚集索引(clustered index);
3. 所有的行记录都存储在B+树的叶子节点(leaf pages of the tree);
4. 基于聚集索引的增、删、改、查的效率相对是最高的;
5. 如果我们定义了主键(PRIMARY KEY),那么InnoDB会选择器作为聚集索引;
6. 如果没有显式定义主键,则InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引;
7. 如果也没有这样的唯一索引,则InnoDB会选择内置6字节长的ROWID作为隐含的聚集索引(ROWID随着行记录的写入而主键递增,这个ROWID不像ORACLE的ROWID那样可引用,是隐含的)。
综上总结,如果InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的,也就是下面这几种情况的存取效率最高:
1. 使用自增列(INT/BIGINT类型)做主键,这时候写入顺序是自增的,和B+数叶子节点分裂顺序一致;
2. 该表不指定自增列做主键,同时也没有可以被选为主键的唯一索引(上面的条件),这时候InnoDB会选择内置的ROWID作为主键,写入顺序和ROWID增长顺序一致;
除此以外,如果一个InnoDB表又没有显示主键,又没有可以被选择为主键的唯一索引,但该唯一索引可能不是递增关系时(例如字符串、UUID、多字段联合唯一索引的情况),该表的存取效率就会比较差。
参考文章: