站長資訊網(wǎng)
        最全最豐富的資訊網(wǎng)站

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        本篇文章給大家?guī)砹巳绻鸐ySQL數(shù)據(jù)查詢太多會不會OOM的相關知識,希望對大家有幫助。

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        主機內(nèi)存只有100G,現(xiàn)在要全表掃描一個200G大表,會不會把DB主機的內(nèi)存用光?

        邏輯備份時,可不就是做整庫掃描嗎?若這樣就會把內(nèi)存吃光,邏輯備份不是早就掛了?

        所以大表全表掃描,看起來應該沒問題。這是為啥呢?

        全表掃描對server層的影響

        假設,我們現(xiàn)在要對一個200G的InnoDB表db1. t,執(zhí)行一個全表掃描。當然,你要把掃描結果保存在客戶端,會使用類似這樣的命令:

        mysql -h$host -P$port -u$user -p$pwd -e   "select * from db1.t" > $target_file

        InnoDB數(shù)據(jù)保存在主鍵索引上,所以全表掃描實際上是直接掃描表t的主鍵索引。這條查詢語句由于沒有其他判斷條件,所以查到的每一行都可以直接放到結果集,然后返回給客戶端。

        那么,這個“結果集”存在哪里呢?

        服務端無需保存一個完整結果集。取數(shù)據(jù)和發(fā)數(shù)據(jù)的流程是這樣的:

        獲取一行,寫到**「net_buffer」。這塊內(nèi)存的大小是由參數(shù)「net_buffer_length」**定義,默認16k

        重復獲取行,直到**「net_buffer」**寫滿,調用網(wǎng)絡接口發(fā)出去

        若發(fā)送成功,就清空**「net_buffer」,然后繼續(xù)取下一行,并寫入「net_buffer」**

        若發(fā)送函數(shù)返回**「EAGAIN」或「WSAEWOULDBLOCK」**,就表示本地網(wǎng)絡棧(socket send buffer)寫滿了,進入等待。直到網(wǎng)絡棧重新可寫,再繼續(xù)發(fā)送

        查詢結果發(fā)送流程

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        可見:

        • 一個查詢在發(fā)送過程中,占用的MySQL內(nèi)部的內(nèi)存最大就是**「net_buffer_length」**這么大,不會達到200G

        • socket send buffer 也不可能達到200G(默認定義/proc/sys/net/core/wmem_default),若socket send buffer被寫滿,就會暫停讀數(shù)據(jù)的流程

        所以MySQL其實是“邊讀邊發(fā)”。這意味著,若客戶端接收得慢,會導致MySQL服務端由于結果發(fā)不出去,這個事務的執(zhí)行時間變長。

        比如下面這個狀態(tài),就是當客戶端不讀**「socket receive buffer」**內(nèi)容時,在服務端show processlist看到的結果。

        服務端發(fā)送阻塞

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        若看到State一直是“Sending to client”,說明服務器端的網(wǎng)絡棧寫滿了。

        若客戶端使用–quick參數(shù),會使用mysql_use_result方法:讀一行處理一行。假設某業(yè)務的邏輯較復雜,每讀一行數(shù)據(jù)以后要處理的邏輯若很慢,就會導致客戶端要過很久才取下一行數(shù)據(jù),可能就會出現(xiàn)上圖結果。

        因此,對于正常的線上業(yè)務來說,若一個查詢的返回結果不多,推薦使用**「mysql_store_result」**接口,直接把查詢結果保存到本地內(nèi)存。

        當然前提是查詢返回結果不多。如果太多,因為執(zhí)行了一個大查詢導致客戶端占用內(nèi)存近20G,這種情況下就需要改用**「mysql_use_result」**接口。

        若你在自己負責維護的MySQL里看到很多個線程都處于“Sending to client”,表明你要讓業(yè)務開發(fā)同學優(yōu)化查詢結果,并評估這么多的返回結果是否合理。

        若要快速減少處于這個狀態(tài)的線程的話,可以將**「net_buffer_length」**設置更大。

        有時,實例上看到很多查詢語句狀態(tài)是“Sending data”,但查看網(wǎng)絡也沒什么問題,為什么Sending data要這么久?

        一個查詢語句的狀態(tài)變化是這樣的:

        • MySQL查詢語句進入執(zhí)行階段后,先把狀態(tài)設置成 「Sending data」

        • 然后,發(fā)送執(zhí)行結果的列相關的信息(meta data) 給客戶端

        • 再繼續(xù)執(zhí)行語句的流程

        • 執(zhí)行完成后,把狀態(tài)設置成空字符串。

        即“Sending data”并不一定是指“正在發(fā)送數(shù)據(jù)”,而可能是處于執(zhí)行器過程中的任意階段。比如,你可以構造一個鎖等待場景,就能看到Sending data狀態(tài)。

        讀全表被鎖:

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        Sending data狀態(tài)

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        可見session2是在等鎖,狀態(tài)顯示為Sending data。

        • 僅當一個線程處于“等待客戶端接收結果”的狀態(tài),才會顯示"Sending to client"

        • 若顯示成“Sending data”,它的意思只是“正在執(zhí)行”

        所以,查詢的結果是分段發(fā)給客戶端,因此掃描全表,查詢返回大量數(shù)據(jù),并不會把內(nèi)存打爆。

        以上是server層的處理邏輯,在InnoDB引擎里又是怎么處理?

        全表掃描對InnoDB的影響

        InnoDB內(nèi)存的一個作用,是保存更新的結果,再配合redo log,避免隨機寫盤。

        內(nèi)存的數(shù)據(jù)頁是在Buffer Pool (簡稱為BP)管理,在WAL里BP起加速更新的作用。

        BP還能加速查詢。

        由于WAL,當事務提交時,磁盤上的數(shù)據(jù)頁是舊的,若這時馬上有個查詢來讀該數(shù)據(jù)頁,是不是要馬上把redo log應用到數(shù)據(jù)頁?

        不需要。因為此時,內(nèi)存數(shù)據(jù)頁的結果是最新的,直接讀內(nèi)存頁即可。這時查詢無需讀磁盤,直接從內(nèi)存取結果,速度很快。所以,Buffer Pool能加速查詢。

        而BP對查詢的加速效果,依賴于一個重要的指標,即:內(nèi)存命中率。

        可以在show engine innodb status結果中,查看一個系統(tǒng)當前的BP命中率。一般情況下,一個穩(wěn)定服務的線上系統(tǒng),要保證響應時間符合要求的話,內(nèi)存命中率要在99%以上。

        執(zhí)行show engine innodb status ,可以看到“Buffer pool hit rate”字樣,顯示的就是當前的命中率。比如下圖命中率,就是100%。

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        若所有查詢需要的數(shù)據(jù)頁都能夠直接從內(nèi)存得到,那是最好的,對應命中率100%。

        InnoDB Buffer Pool的大小是由參數(shù) **「innodb_buffer_pool_size」**確定,一般建議設置成可用物理內(nèi)存的60%~80%。

        在大約十年前,單機的數(shù)據(jù)量是上百個G,而物理內(nèi)存是幾個G;現(xiàn)在雖然很多服務器都能有128G甚至更高的內(nèi)存,但是單機的數(shù)據(jù)量卻達到了T級別。

        所以,**「innodb_buffer_pool_size」**小于磁盤數(shù)據(jù)量很常見。若一個 Buffer Pool滿了,而又要從磁盤讀入一個數(shù)據(jù)頁,那肯定是要淘汰一個舊數(shù)據(jù)頁的。

        InnoDB內(nèi)存管理

        使用的最近最少使用 (Least Recently Used, LRU)算法,淘汰最久未使用數(shù)據(jù)。

        基本LRU算法

        InnoDB管理BP的LRU算法,是用鏈表實現(xiàn)的:

        • state1,鏈表頭部是P1,表示P1是最近剛被訪問過的數(shù)據(jù)頁

        • 此時,一個讀請求訪問P3,因此變成狀態(tài)2,P3被移到最前

        • 狀態(tài)3表示,這次訪問的數(shù)據(jù)頁不存在于鏈表,所以需要在BP中新申請一個數(shù)據(jù)頁Px,加到鏈表頭。但由于內(nèi)存已滿,不能申請新內(nèi)存。于是清空鏈表末尾Pm數(shù)據(jù)頁內(nèi)存,存入Px的內(nèi)容,放到鏈表頭部

        最終就是最久沒有被訪問的數(shù)據(jù)頁Pm被淘汰。

        若此時要做一個全表掃描,會咋樣?若要掃描一個200G的表,而這個表是一個歷史數(shù)據(jù)表,平時沒有業(yè)務訪問它。

        那么,按此算法掃描,就會把當前BP里的數(shù)據(jù)全部淘汰,存入掃描過程中訪問到的數(shù)據(jù)頁的內(nèi)容。也就是說BP里主要放的是這個歷史數(shù)據(jù)表的數(shù)據(jù)。

        對于一個正在做業(yè)務服務的庫,這可不行呀。你會看到,BP內(nèi)存命中率急劇下降,磁盤壓力增加,SQL語句響應變慢。

        所以,InnoDB不能直接使用原始的LRU。InnoDB對其進行了優(yōu)化。

        一起聊聊MySQL數(shù)據(jù)查詢太多會OOM嗎

        改進的LRU算法

        InnoDB按5:3比例把鏈表分成New區(qū)和Old區(qū)。圖中LRU_old指向的就是old區(qū)域的第一個位置,是整個鏈表的5/8處。即靠近鏈表頭部的5/8是New區(qū)域,靠近鏈表尾部的3/8是old區(qū)域。

        改進后的LRU算法執(zhí)行流程:

        狀態(tài)1,要訪問P3,由于P3在New區(qū),和優(yōu)化前LRU一樣,將其移到鏈表頭部 =》狀態(tài)2

        之后要訪問一個新的不存在于當前鏈表的數(shù)據(jù)頁,這時依然是淘汰掉數(shù)據(jù)頁Pm,但新插入的數(shù)據(jù)頁Px,是放在**「LRU_old」**處

        處于old區(qū)的數(shù)據(jù)頁,每次被訪問的時候都要做如下判斷:

        若該數(shù)據(jù)頁在LRU鏈表中存在的時間超過1s,就把它移動到鏈表頭部

        若該數(shù)據(jù)頁在LRU鏈表中存在的時間短于1s,位置保持不變。1s是由參數(shù)**「innodb_old_blocks_time」**控制,默認值1000,單位ms。

        該策略,就是為了處理類似全表掃描的操作量身定制。還是掃描200G歷史數(shù)據(jù)表:

        4. 掃描過程中,需要新插入的數(shù)據(jù)頁,都被放到old區(qū)域

        5. 一個數(shù)據(jù)頁里面有多條記錄,這個數(shù)據(jù)頁會被多次訪問到,但由于是順序掃描,這個數(shù)據(jù)頁第一次被訪問和最后一次被訪問的時間間隔不會超過1秒,因此還是會被保留在old區(qū)域

        6. 再繼續(xù)掃描后續(xù)的數(shù)據(jù),之前的這個數(shù)據(jù)頁之后也不會再被訪問到,于是始終沒有機會移到鏈表頭部(New區(qū)),很快就會被淘汰出去。

        可以看到,這個策略最大的收益,就是在掃描這個大表的過程中,雖然也用到了BP,但對young區(qū)完全沒有影響,從而保證了Buffer Pool響應正常業(yè)務的查詢命中率。

        小結

        MySQL采用的是邊算邊發(fā)的邏輯,因此對于數(shù)據(jù)量很大的查詢結果來說,不會在server端保存完整的結果集。所以,如果客戶端讀結果不及時,會堵住MySQL的查詢過程,但是不會把內(nèi)存打爆。

        而對于InnoDB引擎內(nèi)部,由于有淘汰策略,大查詢也不會導致內(nèi)存暴漲。并且,由于InnoDB對LRU算法做了改進,冷數(shù)據(jù)的全表掃描,對Buffer Pool的影響也能做到可控。

        全表掃描還是比較耗費IO資源的,所以業(yè)務高峰期還是不能直接在線上主庫執(zhí)行全表掃描的。

        推薦學習:mysql視頻教程

        贊(0)
        分享到: 更多 (0)
        網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
        主站蜘蛛池模板: 精品无码人妻一区二区免费蜜桃| 久久久久人妻一区精品| 91麻豆精品一二三区在线| 宅男宅女精品国产AV天堂| 1区1区3区4区产品芒果精品| 精品国产乱码久久久久久1区2区 | 亚洲国产精品高清久久久| 国产精品最新国产精品第十页| 91在线视频精品| 日韩一区精品视频一区二区| 欧美日韩国产精品| 精品久久久久久无码人妻蜜桃| 国产福利精品在线观看| 一本大道久久a久久精品综合| 久久96国产精品久久久| 99精品视频在线观看| 精品国产一区二区三区色欲| 久久综合国产乱子伦精品免费| 亚洲精品成人无限看| 中文字幕久久精品无码| 中文字幕久久精品| 一本一道久久a久久精品综合| 青春草无码精品视频在线观| 国产精品亚洲综合一区| 国产精品国产三级国产潘金莲| 2021国产精品视频网站| 99久久精品国产一区二区蜜芽| 一区二区精品在线| 国产成人亚洲精品91专区手机| 国产精品免费久久| 国产午夜精品久久久久九九| 精品久久久久久久久久中文字幕 | 亚洲国产精品成人精品无码区 | 国产精品国产三级国产AV主播| 少妇人妻偷人精品免费视频| 91精品国产品国语在线不卡| 久久96国产精品久久久| 华人在线精品免费观看| 国产精品无码AV一区二区三区| 男女男精品视频网站在线观看| 韩国三级中文字幕hd久久精品|