query cache在其他資料庫裡面也稱為結果集緩存。顧名思義,它的目的是将select語句與其傳回結果緩存到query cache中,如果重複執行相同的select語句的話,我們可以跳過mysql的解析、優化、執行階段,将select的查詢結果直接傳回到用戶端,加速select語句的執行。
query_cache
對整個query cache進行管理,負責提供接口供server調用。
query_cache_block
query cache中的所有存儲都是以query_cache_block來組織的。每一個query_cache_block的格式都是block_header + data.
query_cache_table
用來記錄單個資料表的block_header。
query_cache_block_table
用來存儲與query相關的資料表的block_header。每一個資料表都有一個獨立的query_cache_block_table結構對應。與query_cache_table的差別是,所有query使用的同一個資料表對應同一個query_cache_table。
query_cache_query
用來記錄query的block_header。
query_cache_result
用來記錄結果集的block_header。
query_cache_memory_bin
用來管理query cache的記憶體使用,query cache的整體記憶體被劃分為多個bins。該結構在query cache初始化的過程中完成。請參考下面query cache記憶體初始化部分。
query_cache_memory_bin_step
用來輔助初始化query_cache_memory_bins,記錄記憶體管理的每一層的bins記憶體配置設定的大小。請參考下面query cache記憶體初始化部分。
query_cache_query_flags
用來記錄影響query傳回結果的環境變量,比如sql_mode,字元集,time_zone等。
query cache中是利用query_cache_block對象進行存儲的,存儲結構如下圖:
說明:
block header:指的就是query_cache_block這個結構體自身需要存儲的一些資訊。詳細資訊可以參考sql/sql_cache.h中的結構體說明。
table of database table lists:這部分是用來記錄與query相關的所有資料表header資訊(query_cache_block_table結構)。對于一條query來說,每個相關的資料表都有一個這樣的header資訊。而對于結果集資料塊來說,這部分不包含資料。
type depended header :這部分用來記錄依賴于該block的具體對象header,比如是query_cache_query還是query_cache_result。
data:顧名思義,就是記錄我們目前存儲對象的資料資訊。
query cache依靠自己對記憶體進行管理。當使用者調整query_cache_size系統參數時,query cache會自動的修改記憶體大小。
query cache的記憶體初始化包含兩個部分:
一次性申請query_cache_size大小的記憶體空間。
初始化記憶體管理方式。
query cache的記憶體管理方式如下圖所示:
query cache對于整個記憶體的管理是将整個記憶體塊劃分多層大小不同的多個<code>query_cache_memory_bins</code>(簡稱bins)。具體的層數由<code>query_cache_memory_bin_steps</code> (簡稱steps)确定,steps是動态根據<code>query_mem_size</code>确定的,如何确定steps請參考<code>query_cache::init_cache</code>。第n層的bins數量是由(前n-1層bins數量總和 + query_cache_mem_bin_parts_inc) * query_cache_mem_bin_parts_mul 确定的。<code>query_cache_mem_bin_parts_inc</code>和<code>query_cache_mem_bin_parts_mul</code>是定義的控制每一層bins增量的兩個宏。
query cache剛初始化後,整個記憶體作為一個大的free_block。當需要申請新的資料塊時,query cache首先尋找大小最為接近的bins進行比對,判斷bins中有沒有free_block。如果有則使用;如果沒有則從其上一個bins中尋找free_block(bins是按照空間大小降序排列的,上層的bins大小要比下層的bins空間大),直到找到合适大小的一個。 如果最後沒有找到合适大小的,query cache将啟動淘汰機制進行淘汰,以滿足空間申請的需求。
在需要申請記憶體塊時,如果沒有找到合适大小的free block, query cache将啟動淘汰機制來淘汰最舊的緩存記錄,釋放的block将被配置設定到對于大小的bins中。如果配置設定過程中發現目前回收的free block和bins中free block位址相鄰, 目前回收的free block将會和相鄰的free block進行合并,重新插入到大小合适的bins中。另外query cache通過flush query cache語句可以自動整理query cache的defragments。
範圍
session
類型
enum
功能
a) on: cache所有select語句。
b) off: 關閉query cache。(預設)。
c) demand: 使用sql_cache hint的select語句才可以緩存
global
ulong
a) 設定query cache的大小,設定範圍是(0 ~ ulong_max)。
b) 預設值是1m。
a) 設定最大結果集大小範圍,如果結果集超出了該值的大小,将不會被緩存。
設定範圍是(0 ~ ulong_max)。
b) 預設值是1m。
a) 設定記憶體申請的最小單元塊的大小,如果設定太大會影響query cache的實際使用率,造成空間浪費。
如果設定太小會增加記憶體的配置設定以及合并成本。設定範圍是(0 ~ ulong_max)。
b) 預設值是4k。
功能: 目前sql語句忽略使用query cache.
功能: 當query_cache_type為on或者demand時,考慮緩存目前sql語句的結果集。
為了快速查找,query cache中維護着一個hash map,用來記錄query對應的其所在的query_cache_block。其對應的key由query + database + flag(包含影響執行結果的所有環境變量)組成。如果目前query與任何key比較一緻的話,對應的結果集就可以被直接傳回。
當一個表發生改變時,所有與該表相關的cached queries将失效。一個表發生變化,包含多種語句,比如 insert, update, delete, truncate table,alter table, drop table, 或者 drop database。
為了加速失效過程,query cache中維護着一個hash map, 用來記錄table對應的query_cache_block_table。其key由db_name + table_name構成。通過key的比較可以擷取query_cache_block_table,該對象包含一個雙向連結清單,記錄了所有與該表相關的cached queries。通過這樣的方式便可以快速的失效一個表對應的所有cached queries。
query cache 通過show status可以看到如下一系列的狀态資訊:
qcache_total_blocks
query cache總的塊數。
qcache_free_blocks
query cache的free blocks數。
qcache_hits
通過query cache,命中的query次數。
qcache_inserts
該變量記錄了之前有多少條query及其資料內建功插入了query cache中,即便插入的記錄後來被淘汰了,該值也不會受到影響。
qcache_lowmem_prunes
query cache中淘汰的query記錄數。
qcache_not_cached
未被緩存的query數。
qcache_queries_in_cache
目前query cache存在的query記錄數,如果有記錄被淘汰了,該值就會發生變化。注意與qcache_inserts的不同。
目前query cache中總的blocks個數。
optimizer_trace中會顯示“query_result_read_from_cache:true”這樣的資訊。
如果目前使用者頻繁使用同樣的query進行查詢,query cache對于性能的提升是顯而易見的。但是也有不太理想的情況,query cache由于不僅依賴于執行的query,同時也依賴于當時的執行環境,比如sql_mode資訊,字元集等,是以對于多使用者共享query cache,很難做到完全的共享。
另外,對于失效方式,目前mysql做的并不到位,隻要是表或表中的資料發生了變化便會引發相關cached queries失效,而更理想的方式是隻有影響目前cached queries的執行結果的表的變化才引發相關cached plan失效。比如如果update語句并未修改某個cached queries的執行記過,其實可以不用對其失效。
我們會在接下來的文章中繼續剖析query cache的并發處理過程,敬請期待。