淺談mysql innodb緩存策略:
The InnoDB Buffer Pool
Innodb 持有一個存儲區域叫做buffer pool是為了在內存中緩存數據和索引,知道innodb bufferpool怎么工作,和利用它讀取頻繁訪問的數據,是mysql優化重要的方面。
理想狀況下,把bufferpool的大小調整到足夠大,留下足夠的內存空間給其他該服務器上的進程(使其無缺頁即可)。bufferpool越大,innodb 月表現為內存型數據庫,從硬盤上一次讀取數據,之后並成了從內存中讀取數據。buffer pool甚至緩存那些因為insert,update操作而改變的數據(insert buffer),所以隨機磁盤寫可以聚集在一塊得到更好的性能。
innodb 把緩存池作為鏈式管理,利用LRU(least recently used)算法,當添加新block到pool中時(無空間),innodb 替換(驅逐)一個最近最少使用的block,然后把新的block添加到鏈表的中間,"midpoint insertion strategy"策略把鏈表看出兩條子鏈。
1:在鏈表的頭部,是由一些“NEW”(or "young")block 組成的最近剛被訪問的子鏈;
2:在鏈表的尾部,是由一些'old' block組成的最近沒被訪問(或者最少被訪問的)的子鏈;
該算法使大量查詢 blocks 保持在 new sublist. old sublist 持有最少使用的 blocks;這些blocks將成為替換或驅逐的候選者。
1:3/8 的buffer pool 的大小分配給old sublist
2: 鏈表的 midpoint (中間插入點) 是new sublist 尾部和 old sublist頭部聚合的地方
3:當 innodb 讀取一個block進 buffer pool時,插入到midpoint(old sublist 的頭部),block被讀取發生在 客戶端操作,eg: sql查詢,或者innodb特性 readahead(預讀);
4:當訪問在old sublist中一個 block時,使其變成'young',把它移動到 buffer pool的頭部(new sublist的頭部),如果該block 被讀取是因為客戶端sql查詢,則第一次訪問立即發生,並且該block變成'young'。如果該block被讀取是因為read ahead,第一次唄訪問不會發生,並且有可能在該Block被替換之前根本不能發生);
5:隨着數據庫操作,在buffer pool 中的沒被訪問的blocks(年紀大的)被移動到鏈表的尾部.在old sublist中的blocks 比插入在midpoint上的block老,最終,一個Block一段長時間未被使用會到達old sublist的尾部會被替換。
默認情況下,被讀取的blocks會立即移動到 NEW sublist 的 head,同時意味着他們待着buffer pool中很長一段時間。當掃表時(eg, mysqldump 操作,或者 沒有where語句的select操作 )可以使大量的blocks push into buffer pool中,並且驅逐大量的older 數據,即使那些所謂剛加入的 new blocks 不會再次被訪問,相同的,read ahead 后台線程一次載入大量的blocks ,這些情況使經常被訪問的blocks push into 到 old sublist中,然后它們成為被驅逐的候選者。
一些innodb 系統變量控制着buffer pool的大小和使你調整LRU算法
1:innodb buffer pool size
指明Buffer pool的大小,如果你的buffer pool 空間小,並且有充足的空間,使pool大點可以減小磁盤IO的次數來提高性能;
2: innodb buffer pool instances : 分成多個獨立的區域,各個區域相同,來減少在並發內存讀寫操作的競爭;
3:innodb old blocks pct:默認3/8;
4: innodb old blocks time: 指定多長時間以毫秒為單位(ms),block插入到老子列表必須呆在那里第一次訪問后多久,才能搬到新的子列表(解決預讀時,緩存污染問題);
檢查查詢緩存是否存在於你的MySQL服務器:
mysql> show variables like 'have_query_cache'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | have_query_cache | YES | +------------------+-------+ 1 row in set (0.00 sec)
監控查詢緩存的性能,使用顯示狀態查看緩存狀態變量:
mysql> show status like 'qcache%'; +-------------------------+----------+ | Variable_name | Value | +-------------------------+----------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 16768376 | | Qcache_hits | 0 | | Qcache_inserts | 0 | | Qcache_lowmem_prunes | 0 | | Qcache_not_cached | 227 | | Qcache_queries_in_cache | 0 | | Qcache_total_blocks | 1 | +-------------------------+----------+ 8 rows in set (0.00 sec)
mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) --Qcache_hits表示sql查詢在緩存中命中的累計次數,是累加值。 mysql> SHOW STATUS LIKE 'Qcache_hits'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Qcache_hits | 0 | --0次 +---------------+-------+ 8 rows in set (0.00 sec) mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) mysql> SHOW STATUS LIKE 'Qcache%'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Qcache_hits | 1 | --表示sql在緩存中直接得到結果,不需要再去解析 +---------------+-------+ 8 rows in set (0.00 sec) mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 6 | +----------+ 1 row in set (0.00 sec) mysql> SHOW STATUS LIKE 'Qcache_hits'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Qcache_hits | 3 | --上面的sql也是是從緩存中直接取到結果 +---------------+-------+ 1 row in set (0.00 sec) mysql> insert into animals select 9,'testsds' ; --插入數據后,跟這個表所有相關的sql緩存就會被清空掉 Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 7 | +----------+ 1 row in set (0.00 sec) mysql> SHOW STATUS LIKE 'Qcache_hits'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Qcache_hits | 3 | --還是等於3,說明上一條sql是沒有直接從緩存中直接得到的 +---------------+-------+ 1 row in set (0.00 sec) mysql> select count(*) from animals; +----------+ | count(*) | +----------+ | 7 | +----------+ 1 row in set (0.00 sec) mysql> SHOW STATUS LIKE 'Qcache_hits'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | Qcache_hits | 4 | +---------------+-------+ 1 row in set (0.00 sec)