RDS MySQL內(nèi)存使用問(wèn)題
本文介紹查看內(nèi)存使用情況的方式,以及各種內(nèi)存問(wèn)題的原因和解決方案。
背景信息
實(shí)例內(nèi)存使用率和緩沖池命中率是RDS MySQL的關(guān)鍵指標(biāo)。如果內(nèi)存使用率過(guò)高,會(huì)有內(nèi)存耗盡風(fēng)險(xiǎn);如果緩沖池命中率低,大量的數(shù)據(jù)頁(yè)無(wú)法命中緩沖池的數(shù)據(jù)頁(yè),需要從磁盤(pán)讀取數(shù)據(jù),造成I/O吞吐增加和延遲增加。
查看內(nèi)存使用情況
RDS管理控制臺(tái)提供多種查看活躍線程的方法:
監(jiān)控與報(bào)警
在控制臺(tái)的監(jiān)控與報(bào)警頁(yè)面,單擊標(biāo)準(zhǔn)監(jiān)控頁(yè)簽,可以查看實(shí)例的MySQL CPU/內(nèi)存 利用率和InnoDB Buffer Pool 命中率。
數(shù)據(jù)庫(kù)自治服務(wù)DAS
在控制臺(tái)的
頁(yè)面,單擊性能趨勢(shì)頁(yè)簽,查看MySQL CPU/內(nèi)存 利用率和InnoDB Buffer Pool 命中率情況。
您還可以使用performance_schema,設(shè)置相關(guān)的內(nèi)存儀表(instrumentation),通過(guò)內(nèi)存占用統(tǒng)計(jì)表查看內(nèi)存占用。詳情請(qǐng)參見(jiàn)MySQL官方文檔。
要在實(shí)例啟動(dòng)時(shí)開(kāi)啟內(nèi)存檢測(cè),您可以在控制臺(tái)修改performance_schema參數(shù),RDS MySQL5.6設(shè)置為ON,RDS MySQL5.7、8.0設(shè)置為1,設(shè)置方法請(qǐng)參見(jiàn)查看實(shí)例參數(shù)。設(shè)置完成后重啟實(shí)例即生效。
要在實(shí)例運(yùn)行中開(kāi)啟內(nèi)存檢測(cè),請(qǐng)執(zhí)行如下命令:
update performance_schema.setup_instruments set enabled = 'yes' where name like 'memory%';
從各個(gè)維度統(tǒng)計(jì)內(nèi)存消耗的相關(guān)表如下:
memory_summary_by_account_by_event_name:統(tǒng)計(jì)指定賬號(hào)(用戶(hù)和主機(jī)組合)的事件和事件名稱(chēng)。
memory_summary_by_host_by_event_name:統(tǒng)計(jì)指定主機(jī)的事件和事件名稱(chēng)。
memory_summary_by_thread_by_event_name:統(tǒng)計(jì)指定線程的事件和事件名稱(chēng)。
memory_summary_by_user_by_event_name:統(tǒng)計(jì)指定用戶(hù)的事件和事件名稱(chēng)。
memory_summary_global_by_event_name:統(tǒng)計(jì)指定事件名稱(chēng)的事件。
RDS MySQL內(nèi)存高常見(jiàn)原因
通常InnoDB Buffer Pool的內(nèi)存占用是最大的,Buffer Pool的內(nèi)存占用上限受到Buffer Pool配置參數(shù)的限制,但是還有很多內(nèi)存是在請(qǐng)求執(zhí)行中動(dòng)態(tài)分配和調(diào)整的,例如內(nèi)存臨時(shí)表消耗的內(nèi)存、prefetch cache、table cache、哈希索引、行鎖對(duì)象等,詳細(xì)的內(nèi)存占用和相關(guān)參數(shù)限制,請(qǐng)參見(jiàn)MySQL官方文檔。
多語(yǔ)句(multiple statements)
MySQL支持將多個(gè)SQL語(yǔ)句用英文分號(hào)(;)分隔,然后一起發(fā)給MySQL,MySQL會(huì)逐條處理SQL,但是某些內(nèi)存需要等到所有的SQL執(zhí)行結(jié)束才釋放。
這種multiple statements的發(fā)送方式,如果一次性發(fā)送的SQL非常多,例如達(dá)到數(shù)百兆,SQL實(shí)際執(zhí)行過(guò)程中各種對(duì)象分配累積消耗的內(nèi)存非常大,很有可能導(dǎo)致MySQL進(jìn)程內(nèi)存耗盡。
一般場(chǎng)景下,如果存在大批量的multiple statements,網(wǎng)絡(luò)流量會(huì)有突增,可以從網(wǎng)絡(luò)流量監(jiān)控和SQL洞察,判斷是否有這種現(xiàn)象。建議業(yè)務(wù)實(shí)現(xiàn)中盡量避免multiple statements的SQL發(fā)送方式。
緩沖池(Buffer Pool)問(wèn)題
所有表的數(shù)據(jù)頁(yè)都存放在緩沖池中,查詢(xún)執(zhí)行的時(shí)候如果需要的數(shù)據(jù)頁(yè)直接命中緩沖池,就不會(huì)發(fā)生物理I/O,SQL執(zhí)行的效率較高,緩沖池采用LRU算法管理數(shù)據(jù)頁(yè),所有的臟頁(yè)放到Flush List鏈表中。
RDS MySQL的InnoDB Buffer Pool大小默認(rèn)設(shè)置為內(nèi)存的75%,這部分內(nèi)存通常是實(shí)例內(nèi)存中占比最大的。
Buffer Pool相關(guān)的常見(jiàn)問(wèn)題:
數(shù)據(jù)頁(yè)預(yù)熱不足導(dǎo)致查詢(xún)的延遲較高。通常發(fā)生在實(shí)例重啟、冷數(shù)據(jù)讀取或緩沖池命中率較低的場(chǎng)景,建議升級(jí)實(shí)例規(guī)格或大促前預(yù)熱數(shù)據(jù)。
臟頁(yè)累積太多。當(dāng)未刷新臟頁(yè)的最舊LSN和當(dāng)前LSN的距離超過(guò)76%時(shí),會(huì)觸發(fā)用戶(hù)線程同步刷新臟頁(yè),導(dǎo)致實(shí)例性能?chē)?yán)重下降。優(yōu)化方式是均衡寫(xiě)入負(fù)載、避免寫(xiě)入吞吐過(guò)高、調(diào)整刷新臟頁(yè)參數(shù)或升級(jí)實(shí)例規(guī)格等。
高內(nèi)存實(shí)例的參數(shù)innodb_buffer_pool_instances設(shè)置較小。高QPS負(fù)載情況下,緩沖池的鎖競(jìng)爭(zhēng)會(huì)比較激烈。建議高內(nèi)存的實(shí)例將參數(shù)innodb_buffer_pool_instances設(shè)置為8或16,甚至更高。
臨時(shí)表
內(nèi)存臨時(shí)表大小受到參數(shù)tmp_table_size和max_heap_table_size限制,超過(guò)限制后將轉(zhuǎn)化為磁盤(pán)臨時(shí)表,如果瞬間有大量的連接創(chuàng)建大量的臨時(shí)表,可能會(huì)造成內(nèi)存突增。MySQL 8.0實(shí)現(xiàn)了新的temptable engine,所有線程分配的內(nèi)存臨時(shí)表大小之和必須小于參數(shù)temptable_max_ram,temptable_max_ram默認(rèn)為1 GB,超出后轉(zhuǎn)換為磁盤(pán)臨時(shí)表。
其他原因
如果實(shí)例內(nèi)表特別多或QPS很高,Table Cache可能也會(huì)消耗內(nèi)存,建議實(shí)例避免創(chuàng)建太多表或設(shè)置參數(shù)table_open_cache過(guò)大。
自適應(yīng)哈希索引占用的內(nèi)存默認(rèn)是Bufffer Pool的1/64。如果查詢(xún)或?qū)懭腴L(zhǎng)度非常大的Blob大字段,會(huì)對(duì)大字段動(dòng)態(tài)分配內(nèi)存,也會(huì)造成內(nèi)存增加。
還有非常多的原因會(huì)造成內(nèi)存上漲,如果出現(xiàn)內(nèi)存使用率異常增加或?qū)嵗齼?nèi)存耗盡,您可以參考MySQL官方文檔排查上漲原因。