1. 程式人生 > 其它 >MySQL實戰45講第33講

MySQL實戰45講第33講

全表掃描如100G記憶體的主機對200G的大表做全表掃描會把資料庫主機記憶體用光嗎?邏輯備份也是全表掃描。答案不會。原理:
1.server層對全表掃描的處理:server端獲取一行資料寫到server端的net_buffer中(該記憶體預設16k,由引數net_buffer_length定義),重複獲取行,寫滿net_buffer,呼叫網路介面發出去,傳送成功清空net_buffer,繼續讀取下一行並寫入net_buffer。
若傳送函式返回EAGAIN或WSAEWOULDBLOCK,表示本地網路棧(socket send buffer)寫滿了,進入等待,知道網路棧重新可寫再繼續傳送。所以在傳送過程中,佔用的mysql內部記憶體最大就是net_buffer_length大小。而socket send buffer預設212992位元組(百度說229376位元組),
是在作業系統的/proc/sys/net/core/wmem_default定義的(預設的TCP資料傳送視窗大小(位元組))。
mysql邊讀編髮,因此客戶端接收變慢導致socket receive buffer中的內容堆積滿了,則服務端結果發不出去,導致事務執行時間變成,在show processlist能看到某個事務的狀態處於Sending to client,服務端發不出去,導致net_buffer滿了,不再讀取行,
所以這種情況可適當調大net_buffer_length,使得語句能夠執行完,把查詢結果放到net_buffer中,雖然還是Sending to client狀態,但是對執行器來說語句執行完不會再佔著資源(如MDL讀鎖)。
另一個知識點:客戶端使用-quick引數,會使用mysql_use_result方法,讀一行處理一行,假設業務邏輯複雜,每讀一行資料後續處理很慢,導致客戶端很久才取下一行資料,也會出現上述情況。所以線上業務,若查詢結果不會很多則建議使用mysql_store_result介面,
將查詢結果儲存到應用程式本地記憶體。查詢結果太多則使用mysql_use_result,避免應用程式OOM,mysql jdbc的fechSize()方法一次大查詢然後客戶端流式讀取,也是用了mysql_use_result原理,這種場景也可採用分批讀取策略。
另一個知識點:show processlist還有一種狀態叫Sending data,查詢語句進入執行階段會把狀態置為Sending data,然後傳送執行結果列相關資訊(meta data)給客戶端,再繼續執行語句流程,執行完成後把狀態設定為空字串,所以Sending data指執行器正在執行,
並非正在傳送資料。
2.InnoDB對全表掃描的處理:WAL機制,InnoDB記憶體資料頁在Buffer Pool(BP)中管理,用於儲存更新結果,配合redo log,避免隨機寫盤,WAL裡Buffer Pool起到加速更新作用,Buffer Pool另一重要作用加速查詢。由於WAL機制,事務提交時,磁碟上的資料是舊的,
查詢請求讀取該資料頁,直接讀最新的記憶體頁即可,不需讀磁碟,加速查詢,因此有個重要指標:記憶體命中率,可通過show engine innodb status檢視(Buffer pool hit rate),顯示穩定服務要保證響應時間,記憶體命中率要>99%。InnodbDB Buffer Pool大小
由引數innodb_buffer_pool_size確定,建議設定為可用實體記憶體60%-80%,但不會大於單機磁碟儲存資料量,因此Buffer Pool滿了以後要從磁碟讀取新的資料頁,需要淘汰舊的資料頁,InnoDB記憶體管理使用連結串列儲存資料頁,使用最近改進版的最少使用演算法LRU來淘汰最久未使用的資料,
LRU將訪問到的資料頁移動到連結串列頭,新從磁碟讀取資料時,淘汰連結串列尾的資料頁。改進版的LRU演算法將連結串列分為5/8的young區域和3/8區域,young區域被訪問的資料頁移動到連結串列頭,新從磁碟讀取資料時,淘汰連結串列尾的資料頁,新插入的資料頁插入old區域,
訪問old區域的資料頁要做判斷,若該資料頁在連結串列中存在時間>1s,移動到連結串列頭部,<1s則保持不變,可設定引數innodb_old_blocks_time,預設是1000ms即1s。該策略讓全表掃描載入到記憶體中的大量只訪問一次的資料頁一直放在old區域,方便及時淘汰並且不影響業務正常使用的young區域資料頁,
若不用改進版的LRU,原生的LRU在全表掃描時導致記憶體中的資料頁都被淘汰,則正常業務用到的表都需要重新查詢磁碟(記憶體命中率急劇下降),響應時間下降,且此時磁碟IO壓力加大,效能進一步降低。因此不建議在業務系統做冷資料全部掃描(耗費IO資源)。
注:從上述可看出,連結串列old區域實際存放的是新近從磁碟讀取的資料頁,官方叫它old區域,確實容易造成誤解。old區域的資料頁存在1s以上且被訪問到就會挪到young區域,有點類似jvm堆記憶體的老年代新生代的感覺。