數(shù)據(jù)庫作為一個(gè)大型的并發(fā)存儲(chǔ)系統(tǒng),其內(nèi)部設(shè)計(jì)極其復(fù)雜,開發(fā)者在使用數(shù)據(jù)庫時(shí),面臨著可用性、可靠性、性能、安全、擴(kuò)展性等多重挑戰(zhàn),這就使得數(shù)據(jù)庫的使用具有較高的技術(shù)門檻。一般大型團(tuán)隊(duì)都會(huì)雇傭?qū)B毜腄BA來維護(hù)數(shù)據(jù)庫的日常運(yùn)行,指導(dǎo)開發(fā)者建立正確的使用姿勢(shì),但是對(duì)于一般的中小型團(tuán)隊(duì),受限于人力成本,這種模式難以實(shí)現(xiàn)。隨著DevOps理念的流行,開發(fā)者開始更多的參與和承擔(dān)數(shù)據(jù)庫的運(yùn)維工作,其中就包括對(duì)數(shù)據(jù)庫的定期巡檢。
對(duì)數(shù)據(jù)庫定期進(jìn)行健康檢查是數(shù)據(jù)庫日常維護(hù)的重要環(huán)節(jié),通過檢查數(shù)據(jù)庫的各項(xiàng)運(yùn)行指標(biāo),評(píng)估系統(tǒng)的運(yùn)行風(fēng)險(xiǎn),提前將風(fēng)險(xiǎn)消滅在搖籃中,能夠有效提高數(shù)據(jù)庫服務(wù)的質(zhì)量,今天我們就來聊聊開發(fā)者如何對(duì)數(shù)據(jù)庫進(jìn)行健康檢查。
數(shù)據(jù)庫的健康檢查涉及索引設(shè)計(jì)、容量規(guī)劃、服務(wù)安全、參數(shù)配置、用戶訪問、集群復(fù)制6個(gè)方面。
索引設(shè)計(jì)
合理的索引設(shè)計(jì)能夠有效加速數(shù)據(jù)庫的訪問,提高查詢的執(zhí)行效率,減少用戶查詢對(duì)服務(wù)端的資源消耗。但是不合理的、低效的、冗余的甚至無效的索引不僅無法起到加速查詢的效果,反而會(huì)影響數(shù)據(jù)庫的插入、更新性能,甚至是數(shù)據(jù)庫的高可用方案能否生效。
- 主鍵索引缺失: 由于MySQL默認(rèn)存儲(chǔ)引擎InnoDB(MySQL 5.6版本 以上)使用的是聚簇索引表設(shè)計(jì),這就要求所有的表必須包含一個(gè)主鍵,所有的數(shù)據(jù)記錄按照主鍵次序構(gòu)建B+樹。如果用戶在創(chuàng)建表時(shí)顯式指定主鍵,則數(shù)據(jù)庫會(huì)使用用戶指定的主鍵構(gòu)建B+樹,但是如果用戶沒有顯式指定主鍵,同時(shí)也沒有創(chuàng)建任何唯一鍵索引,InnoDB為了確保每張表至少包含一個(gè)主鍵,則默認(rèn)會(huì)為用戶生成一個(gè)“隱含主鍵”,該主鍵對(duì)用戶不可見,甚至對(duì)于MySQL Server層的binlog也不可見。binlog是連接MySQL主從復(fù)制節(jié)點(diǎn)的紐帶,所有主節(jié)點(diǎn)的更新都是通過binlog傳遞給從節(jié)點(diǎn)的,一旦binlog中沒有更新記錄的主鍵ID,這就會(huì)導(dǎo)致基于Row格式的binlog在從節(jié)點(diǎn)執(zhí)行時(shí),無法唯一確定一條記錄,只能通過全表掃描來進(jìn)行匹配,大幅降低了從機(jī)的執(zhí)行效率,造成復(fù)制延遲。如果是高可用故障切換的從節(jié)點(diǎn),會(huì)導(dǎo)致切換的時(shí)間大幅增加,甚至?xí)?dǎo)致高可用機(jī)制失效。如果是實(shí)現(xiàn)讀寫分離的只讀從節(jié)點(diǎn),則會(huì)導(dǎo)致應(yīng)用讀到的數(shù)據(jù)可能是很久以前的舊數(shù)據(jù)。所以我們建議使用InnoDB存儲(chǔ)引擎的MySQL用戶在創(chuàng)建表時(shí),必須顯式指定主鍵。
- 主鍵索引與業(yè)務(wù)相關(guān):如果用戶在創(chuàng)建表時(shí)指定的主鍵與業(yè)務(wù)相關(guān),可能會(huì)被頻繁的更新,這樣會(huì)引起MySQL數(shù)據(jù)庫的InnoDB存儲(chǔ)引擎進(jìn)行頻繁的節(jié)點(diǎn)合并和分裂,造成大量額外的系統(tǒng)IO開銷,影響數(shù)據(jù)庫的插入和更新性能。我們推薦開發(fā)者在創(chuàng)建表時(shí)指定與業(yè)務(wù)無關(guān)的自增字段作為主鍵,這樣不僅會(huì)提高按時(shí)間序插入的性能(順序?qū)懭胗脖P),同時(shí)也可以提高按插入時(shí)間范圍檢索的查詢效率。
- 冗余索引:如果一個(gè)索引涉及的字段屬性包含另外一個(gè)索引涉及的字段屬性,同時(shí)兩個(gè)索引字段順序一致,且兩個(gè)索引的首字段屬性相同,則可以認(rèn)為涉及字段少的索引為冗余索引。在MySQL 5.7推出sys庫之前,我們可以通過percona的工具pt-duplicate-key-checker來完成對(duì)冗余索引的檢查,在MySQL 5.7中,我們可以通過sys庫schema_redundant_indexes表來完成。
- 低效索引:索引的作用在于通過索引,查詢能夠掃描更少的記錄。數(shù)據(jù)庫中的記錄在索引字段區(qū)分度越高,掃描的記錄數(shù)就越少,執(zhí)行的效率就越高。如果數(shù)據(jù)庫表中的記錄在索引字段區(qū)分度不大,索引對(duì)記錄的篩選結(jié)果就不明顯,索引就無法起到加速查詢的作用。通過數(shù)據(jù)庫記錄在索引字段的區(qū)分度,我們可以衡量索引的執(zhí)行效率。MySQL系統(tǒng)庫mysql庫下,innodb_index_stats表的stat_value字段,記錄了某張表在某個(gè)索引的不同取值的記錄個(gè)數(shù),innodb_table_stats表的n_rows字段記錄了某張表的總記錄數(shù),二者相除,即可得到數(shù)據(jù)庫記錄在某個(gè)索引的區(qū)分度,越接近1,表示區(qū)分度越高,低于0.1,則說明區(qū)分度較差,開發(fā)者應(yīng)該重新評(píng)估SQL語句涉及的字段,選擇區(qū)分度高的多個(gè)字段創(chuàng)建索引,通過運(yùn)行下面的SQL語句,就可以計(jì)算每張表的索引區(qū)分度。