婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > 淺談MySQL數據查詢太多會OOM嗎

淺談MySQL數據查詢太多會OOM嗎

熱門標簽:外東北地圖標注 話務外呼系統怎么樣 大眾點評星級酒店地圖標注 400電話可以辦理嗎 高清地圖標注道路 云南電商智能外呼系統價格 拉卡拉外呼系統 智能外呼系統復位 臨清電話機器人

我的主機內存只有100G,現在要全表掃描一個200G大表,會不會把DB主機的內存用光?

邏輯備份時,可不就是做整庫掃描嗎?若這樣就會把內存吃光,邏輯備份不是早就掛了?
所以大表全表掃描,看起來應該沒問題。這是為啥呢?

全表掃描對server層的影響

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

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

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

那么,這個“結果集”存在哪里呢?
服務端無需保存一個完整結果集。取數據和發數據的流程是這樣的:

  • 獲取一行,寫到net_buffer。這塊內存的大小是由參數net_buffer_length定義,默認16k
  • 重復獲取行,直到net_buffer寫滿,調用網絡接口發出去
  • 若發送成功,就清空net_buffer,然后繼續取下一行,并寫入net_buffer
  • 若發送函數返回EAGAIN或WSAEWOULDBLOCK,就表示本地網絡棧(socket send buffer)寫滿了,進入等待。直到網絡棧重新可寫,再繼續發送

查詢結果發送流程

可見:

  • 一個查詢在發送過程中,占用的MySQL內部的內存最大就是net_buffer_length這么大,不會達到200G
  • socket send buffer 也不可能達到200G(默認定義/proc/sys/net/core/wmem_default),若socket send buffer被寫滿,就會暫停讀數據的流程

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

比如下面這個狀態,就是當客戶端不讀socket receive buffer內容時,在服務端show processlist看到的結果。

服務端發送阻塞


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

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

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

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

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

若要快速減少處于這個狀態的線程的話,可以將net_buffer_length設置更大。

有時,實例上看到很多查詢語句狀態是“Sending data”,但查看網絡也沒什么問題,為什么Sending data要這么久?
一個查詢語句的狀態變化是這樣的:

  • MySQL查詢語句進入執行階段后,先把狀態設置成 Sending data
  • 然后,發送執行結果的列相關的信息(meta data) 給客戶端
  • 再繼續執行語句的流程
  • 執行完成后,把狀態設置成空字符串。

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

讀全表被鎖:

session1 session2
begin
select * from t where id=1 for update
啟動事務
select * from t lock in share mode
(blocked)

Sending data狀態

可見session2是在等鎖,狀態顯示為Sending data。

  • 僅當一個線程處于“等待客戶端接收結果”的狀態,才會顯示"Sending to client"
  • 若顯示成“Sending data”,它的意思只是“正在執行”

所以,查詢的結果是分段發給客戶端,因此掃描全表,查詢返回大量數據,并不會把內存打爆。

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

全表掃描對InnoDB的影響

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

內存的數據頁是在Buffer Pool (簡稱為BP)管理,在WAL里BP起加速更新的作用。
BP還能加速查詢。

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

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

而BP對查詢的加速效果,依賴于一個重要的指標,即:內存命中率。
可以在show engine innodb status結果中,查看一個系統當前的BP命中率。一般情況下,一個穩定服務的線上系統,要保證響應時間符合要求的話,內存命中率要在99%以上。

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


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

InnoDB Buffer Pool的大小是由參數 innodb_buffer_pool_size確定,一般建議設置成可用物理內存的60%~80%。

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

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

InnoDB內存管理

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

  • 基本LRU算法

TODO

  • InnoDB管理BP的LRU算法,是用鏈表實現的:
  • state1,鏈表頭部是P1,表示P1是最近剛被訪問過的數據頁
  • 此時,一個讀請求訪問P3,因此變成狀態2,P3被移到最前
  • 狀態3表示,這次訪問的數據頁不存在于鏈表,所以需要在BP中新申請一個數據頁Px,加到鏈表頭。但由于內存已滿,不能申請新內存。于是清空鏈表末尾Pm數據頁內存,存入Px的內容,放到鏈表頭部

最終就是最久沒有被訪問的數據頁Pm被淘汰。
若此時要做一個全表掃描,會咋樣?若要掃描一個200G的表,而這個表是一個歷史數據表,平時沒有業務訪問它。

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

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

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

改進的LRU算法

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

改進后的LRU算法執行流程:

1. 狀態1,要訪問P3,由于P3在New區,和優化前LRU一樣,將其移到鏈表頭部 =》狀態2
2. 之后要訪問一個新的不存在于當前鏈表的數據頁,這時依然是淘汰掉數據頁Pm,但新插入的數據頁Px,是放在LRU_old處
3. 處于old區的數據頁,每次被訪問的時候都要做如下判斷:

  • 若該數據頁在LRU鏈表中存在的時間超過1s,就把它移動到鏈表頭部
  • 若該數據頁在LRU鏈表中存在的時間短于1s,位置保持不變。1s是由參數innodb_old_blocks_time控制,默認值1000,單位ms。

該策略,就是為了處理類似全表掃描的操作量身定制。還是掃描200G歷史數據表:
4. 掃描過程中,需要新插入的數據頁,都被放到old區域
5. 一個數據頁里面有多條記錄,這個數據頁會被多次訪問到,但由于是順序掃描,這個數據頁第一次被訪問和最后一次被訪問的時間間隔不會超過1秒,因此還是會被保留在old區域
6. 再繼續掃描后續的數據,之前的這個數據頁之后也不會再被訪問到,于是始終沒有機會移到鏈表頭部(New區),很快就會被淘汰出去。

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

小結

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

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

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

到此這篇關于淺談MySQL數據查詢太多會OOM嗎的文章就介紹到這了,更多相關MySQL數據查詢OOM內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • MySQL的時間差函數TIMESTAMPDIFF、DATEDIFF的用法
  • MySQL的時間差函數(TIMESTAMPDIFF、DATEDIFF)、日期轉換計算函數(date_add、day、date_format、str_to_date)
  • 實現MySQL + PostgreSQL批量插入更新insertOrUpdate
  • MYSQL row_number()與over()函數用法詳解
  • MySQL隱式類型轉換導致索引失效的解決
  • Django搭建MySQL主從實現讀寫分離
  • MySQL多表連接查詢詳解
  • mysql中TIMESTAMPDIFF案例詳解

標簽:阿里 福州 溫州 山西 揚州 無錫 三明 定西

巨人網絡通訊聲明:本文標題《淺談MySQL數據查詢太多會OOM嗎》,本文關鍵詞  淺談,MySQL,數據查詢,太多,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《淺談MySQL數據查詢太多會OOM嗎》相關的同類信息!
  • 本頁收集關于淺談MySQL數據查詢太多會OOM嗎的相關信息資訊供網民參考!
  • 推薦文章
    主站蜘蛛池模板: 文昌市| 东明县| 武鸣县| 兴城市| 沙洋县| 自治县| 成安县| 庆云县| 班戈县| 东港市| 安远县| 岱山县| 个旧市| 新巴尔虎左旗| 白沙| 容城县| 和林格尔县| 万荣县| 庆城县| 扶沟县| 江源县| 敖汉旗| 东山县| 黔江区| 石屏县| 贡山| 集安市| 郯城县| 汉源县| 彭州市| 安平县| 阳信县| 屏东市| 射洪县| 保康县| 隆昌县| 库车县| 九龙城区| 策勒县| 曲周县| 黑山县|