今天收到報警,提示從庫延時,首先當然是上去查看情況,首先查看機器負載,如下:

可以看到使用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、多字段聯合唯一索引的情況),該表的存取效率就會比較差。
參考文章:
