首先我們說一下大查詢會不會把內存打爆?
比如說主機內存有5g,但是我們一個大查詢的數據有10g,這樣會不會把內存打爆呢?
答案:不會
為什么?
因為mysql讀取數據是采取邊讀邊發的策略
select * from t1
這條語句的流程是這樣的
1.讀取數據放入net_buffer中,net_buffer大小是由net_buffer_length控制
2.net_buffer放滿了以后,調用網絡棧發送數據到客戶端
3.如果發送成功就清空net_buffer,繼續讀取數據放入net_buffer中
4.如果發送函數返回EAGAIN或者WSAEWOULDBLOCK就表示本地網絡棧滿了,這時候就進入等待,知道網絡棧重新可寫,再繼續發送。
根據這個流程來看,讀取數據的時候占用的內存最多也就是net_buffer的大小。
InnoDB內存(buffer pool)管理
我們都知道mysql查詢數據是先看內存中有沒有數據,如果沒有就從磁盤中讀出來,然后在讀入內存
所以說bufferpool對查詢有加速效果,加速效果依賴於一個指標也就是內存命中率,如果命中率能達到100%那是最好的
通過
show engine innodb status
可以查看命中率
innodb buffer pool的大小是由參數innodb_buffer_pool_size控制的,一般設置為可用物理內存的60%-80%
內存淘汰
既然內存是一塊固定大小的,那么存放在內存里的數據就肯定有的會被淘汰
下面是一個lur算法的基本模型
innodb管理bufferpool的lru算法是基於鏈表實現的
state1:我們要查詢p3的數據,由於p3是在內存中的,那么久直接把p3移動到鏈表頭部,
也就是對應圖中state2的狀態
state3中由於我們查詢的px數據不是在px中,那么就從磁盤中查詢出px的數據放入鏈表頭部,
但是由於內存滿了,所以
就會把pm的數據從鏈表尾部淘汰掉,從現象上來看就是最久沒有被訪問都的數據會被淘汰
這種算法對於mysql來說有什么問題??
如果我們對一個冷數據表進行全表掃描,比如說日志表,這些不是正常用戶訪問的表,
那么在bufferpool中就會大量存在這些數據的表,那么就會導致用戶正常訪問存放的業務數據會被淘汰掉,
就會導致大量數據需要重新讀磁盤放入內存,這樣性能就會大大降低
mysql肯定不會允許這種情況發生的,所以它基於上面的lru算法做了改進
下圖就是改進后的模型
innodb把整個內存的前八分之五記為young區域,后八分之三記為old區域,
我們看上圖state1中由於我們訪問的p3是在young區域,那么就把p3移動到鏈表頭部
但是如果我們訪問的數據如果是在old區域,比如說我們訪問了px,這個時候會做個判斷
如果px在內存中存活時間超過1秒,就會把它移動到young區域的鏈表頭部,否則位置不動
這個1秒是由參數
innodb_old_blocks_time控制的,默認值是1000,單位毫秒
這樣我們在看掃描全表的步驟
掃描過程中被訪問的數據頁會被放在old區域
一個數據頁有多條記錄會被訪問,所以這數據頁會被多次訪問到,但是由於是順序掃描,
這個數據頁第一次被訪問和最后一次被訪問的時間間隔不會超過一秒,所以就會一直在old區域
在繼續掃描后面的數據頁,之前的這個數據頁也不會被訪問到,因此就會一直在old區域,也就很快就會被淘汰掉了
可以看到這個策略的最大收益,就是在掃描的過程中,雖然也用到了bufferpool,
但是不會對young區域造成影響,也就保證了bufferpool響應業務的內存命中率
了解更多:https://www.toutiao.com/c/user/83293539887/#mid=1633933053814798