mysql 緩存機制
命中條件
緩存存在一個hash表中,通過查詢SQL,查詢數據庫,客戶端協議等作爲key,在判斷命中前,mysql不會解析SQL,而是使用SQL去查詢緩存,SQL上的任何字符的不同,如空格,註釋,都會導致緩存不命中。如果查詢有不確定的數據like now(),current_date(),那麼查詢完成後結果者不會被緩存,包含不確定的數的是不會放置到緩存中。
工作流程
1.服務器接收SQL,以SQL和一些其他條件爲key查找緩存表
2.如果找到了緩存,則直接返回緩存
3.如果沒有找到緩存,則執行SQL查詢,包括原來的SQL解析,優化等。
4.執行完SQL查詢結果以後,將SQL查詢結果緩存入緩存表
緩存失敗
當某個表正在寫入數據,則這個表的緩存(命中緩存,緩存寫入等)將會處於失效狀態,在Innodb中,如果某個事務修改了這張表,則這個表的緩存在事務提交前都會處於失效狀態,在這個事務提交前,這個表的相關查詢都無法被緩存。
緩存的內存管理
緩存會在內存中開闢一塊內存(query_cache_size)來維護緩存數據,其中大概有40K的空間是用來維護緩存數據的元數據的,例如空間內存,例如空間內存,數據表和查詢結果映射,SQL和查詢結果映射的。
mysql將這個大內存塊分爲小內存塊(query_cache_min_res_unit),每個小塊中存儲自身的類型、大小和查詢結果數據,還有前後內存塊的指針。
mysql需要設置單個小存儲塊大小,在SQL查詢開始(還未得到結果)時就去申請一塊內存空間,所以即使你的緩存數據沒有達到這個大小也需要這個大小的數據塊去保存(like linux filesystem’s block)。如果超出這個內存塊的大小,則需要再申請一個內存塊。當查詢完成發現申請的內存有富餘,則會將富餘的內存空間是放點,這就會造成內存碎片的問題,見下圖
緩存的使用時機
衡量打開緩存是否對系統有性能提升是一個很難的話題
- 通過緩存命中率判斷, 緩存命中率 = 緩存命中次數 (Qcache_hits) / 查詢次數 (Com_select)
- 通過緩存寫入率, 寫入率 = 緩存寫入次數 (Qcache_inserts) / 查詢次數 (Qcache_inserts)
- 通過 命中-寫入率 判斷, 比率 = 命中次數 (Qcache_hits) / 寫入次數 (Qcache_inserts), 高性能MySQL中稱之爲比較能反映性能提升的指數,一般來說達到3:1則算是查詢緩存有效,而最好能夠達到10:1
緩存參數配置
- query_cache_type: 是否打開緩存
可選項
1) OFF: 關閉
2) ON: 總是打開
3) DEMAND: 只有明確寫了SQL_CACHE的查詢纔會吸入緩存 - query_cache_size: 緩存使用的總內存空間大小,單位是字節,這個值必須是1024的整數倍,否則MySQL實際分配可能跟這個數值不同(感覺這個應該跟文件系統的blcok大小有關)
- query_cache_min_res_unit: 分配內存塊時的最小單位大小
- query_cache_limit: MySQL能夠緩存的最大結果,如果超出,則增加 Qcache_not_cached的值,並刪除查詢結果
- query_cache_wlock_invalidate: 如果某個數據表被鎖住,是否仍然從緩存中返回數據,默認是OFF,表示仍然可以返回
GLOBAL STAUS 中 關於 緩存的參數解釋:
Qcache_free_blocks: 緩存池中空閒塊的個數
Qcache_free_memory: 緩存中空閒內存量
Qcache_hits: 緩存命中次數
Qcache_inserts: 緩存寫入次數
Qcache_lowmen_prunes: 因內存不足刪除緩存次數
Qcache_not_cached: 查詢未被緩存次數,例如查詢結果超出緩存塊大小,查詢中包含可變函數等
Qcache_queries_in_cache: 當前緩存中緩存的SQL數量
Qcache_total_blocks: 緩存總block數
減少碎片策略
- 選擇合適的block大小
- 使用 FLUSH QUERY CACHE 命令整理碎片.這個命令在整理緩存期間,會導致其他連接無法使用查詢緩存
PS: 清空緩存的命令式 RESET QUERY CACHE
InnoDB與查詢緩存
Innodb會對每個表設置一個事務計數器,裏面存儲當前最大的事務ID.當一個事務提交時,InnoDB會使用MVCC中系統事務ID最大的事務ID跟新當前表的計數器.
只有比這個最大ID大的事務能使用查詢緩存,其他比這個ID小的事務則不能使用查詢緩存.
另外,在InnoDB中,所有有加鎖操作的事務都不使用任何查詢緩存
查詢必須是完全相同的(逐字節相同)才能夠被認爲是相同的。另外,同樣的查詢字符串由於其它原因可能認爲是不同的。使用不同的數據庫、不同的協議版本或者不同 默認字符集的查詢被認爲是不同的查詢並且分別進行緩存。