DML對QUERY CACHE 處理過程之原始碼分析
問題:
當一個大的SELECT查詢在執行時,會把UPDATE堵住;
SHOW INNODB STATUS看,UPDATE的狀態是:invalidating query cache entries
SELECT結束,現象消失。
版本號:mysql 5.1.40
首先說明一下,QUERY CACHE 只有一把全域性鎖。
private:
Cache_lock_status m_cache_lock_status;
下面說到的鎖,就是這個全域性獨佔鎖。
[@more@]
MYSQL在處理SELECT過程中,對QUERY CACHE會有兩個操作:
High.Performance.MySQL.Second.Edition.pdf -- P161
1) 在SQL PARSE 之前,就會先到QUERY CACHE裡去找,是否有這個SQL的CACHE;如果有,就把結果前直接發給使用者;
2) 第一步無果,進行查詢處理,查詢處理後,把結果集放到QUERY CACHE中;
(都會調這個函式, 是否真正需要放,在函式內判斷)
我們來看第1步:呼叫的是以下函式:
/*
Check if the query is in the cache. If it was cached, send it
to the user.
RESULTS
1 Query was not cached.
0 The query was cached and user was sent the result.
-1 The query was cached but we didn't have rights to use it.
No error is sent to the client yet.
NOTE
This method requires that sql points to allocated memory of size:
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
*/
int
Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
在這個函式中, SERVER會先試著去拿鎖,如果拿不到鎖,就直接返回錯誤,表示找不到SQL;
拿不到鎖,有可能是剛好有其他任務在讀或者有更新;
/*
Try to obtain an exclusive lock on the query cache. If the cache is
disabled or if a full cache flush is in progress, the attempt to
get the lock is aborted. 去QUERY CACHE查詢時,
*/
if (try_lock())
goto err;
if (query_cache_size == 0)
goto err_unlock;
如果是這樣,有一點是不能解釋:
雖然從QUERY CACHE中透過HASH值拿到RESULT很快;
但畢竟是一個全域性鎖,他這樣設計,對SELECT併發大的環境,是不是會有很多查詢都拿不到RESULT,而去重新查詢;
第二步,是把查詢結果註冊到QUERY CACHE中; 調的是以下函式:
/* register query in cache */
void store_query(THD *thd, TABLE_LIST *used_tables);
/*
A table- or a full flush operation can potentially take a long time to
finish. We choose not to wait for them and skip caching statements
instead.
*/
if (try_lock())
DBUG_VOID_RETURN;
這裡也是,如果拿不到鎖就直接跳出。不再進行寫QUERY CACHE。
如果一直等待,那麼SELECT就HANG起
對以上兩個操作,小結了一下: MYSQL在處理SELECT時,對QUERY CACHE的態度時,能用就用,用不上拉到,(響應優先)
下面來看MYSQL在處理INSERT,UPDATE,DELETE過程,
每個事務提交完成前,會對被更新的所有表進行QUERY CACHE清理工作;
事務處理int ha_commit_one_phase(THD *thd, bool all) 呼叫以下函式:
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
THD *thd= current_thd;
for (; tables_used; tables_used= tables_used->next)
{
thd_proc_info(thd, "invalidating query cache entries (table list)");
invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
tables_used->key+
strlen(tables_used->key)+1));
}
DBUG_VOID_RETURN;
}
在這個函式里面,每個表的清理工作:
void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
/*
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
*/
lock();
DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
if (query_cache_size > 0)
invalidate_table_internal(thd, key, key_length);
unlock();
在這裡我們發現,呼叫的是lock(),這個函式需要拿QUERY CACHE的獨佔鎖,如果拿不到,就等待,直到超時;
void Query_cache::lock(void)
{
DBUG_ENTER("Query_cache::lock");
pthread_mutex_lock(&structure_guard_mutex);
while (m_cache_lock_status != Query_cache::UNLOCKED)
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
到這裡為止,大查詢會阻塞UPDATE這一說法還是成立的;
如果你的QUERY CAHCE足夠大,以及你的查詢結果集夠大,Void store_query()工作時,會拿到鎖,
而UPDATE要清理,也要拿鎖,這時等待;
但我們有幾個引數要看一下:
| query_cache_limit | 2097152 ## 超過2M的的結果集不CACHE
| query_cache_size | 67108864 ## QUERY CACHE 大小
這裡,如果QUERY CAHCE超過2M,其實不會被CACHE; status variable:Qcache_not_cached+1;
但即使儲存2M的結果集,也不會需要很長時間;
要超時,實在是不理解;
到這裡,雖然分析了,DML時QUERY CACHE的相關處理過程,
但還是沒能解釋我們資料庫中,SELECT把我們資料庫UPDATE堵成超時的真正原因;
先作為知識儲備。
當一個大的SELECT查詢在執行時,會把UPDATE堵住;
SHOW INNODB STATUS看,UPDATE的狀態是:invalidating query cache entries
SELECT結束,現象消失。
版本號:mysql 5.1.40
首先說明一下,QUERY CACHE 只有一把全域性鎖。
private:
Cache_lock_status m_cache_lock_status;
下面說到的鎖,就是這個全域性獨佔鎖。
[@more@]
MYSQL在處理SELECT過程中,對QUERY CACHE會有兩個操作:
High.Performance.MySQL.Second.Edition.pdf -- P161
1) 在SQL PARSE 之前,就會先到QUERY CACHE裡去找,是否有這個SQL的CACHE;如果有,就把結果前直接發給使用者;
2) 第一步無果,進行查詢處理,查詢處理後,把結果集放到QUERY CACHE中;
(都會調這個函式, 是否真正需要放,在函式內判斷)
我們來看第1步:呼叫的是以下函式:
/*
Check if the query is in the cache. If it was cached, send it
to the user.
RESULTS
1 Query was not cached.
0 The query was cached and user was sent the result.
-1 The query was cached but we didn't have rights to use it.
No error is sent to the client yet.
NOTE
This method requires that sql points to allocated memory of size:
tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
*/
int
Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
在這個函式中, SERVER會先試著去拿鎖,如果拿不到鎖,就直接返回錯誤,表示找不到SQL;
拿不到鎖,有可能是剛好有其他任務在讀或者有更新;
/*
Try to obtain an exclusive lock on the query cache. If the cache is
disabled or if a full cache flush is in progress, the attempt to
get the lock is aborted. 去QUERY CACHE查詢時,
*/
if (try_lock())
goto err;
if (query_cache_size == 0)
goto err_unlock;
如果是這樣,有一點是不能解釋:
雖然從QUERY CACHE中透過HASH值拿到RESULT很快;
但畢竟是一個全域性鎖,他這樣設計,對SELECT併發大的環境,是不是會有很多查詢都拿不到RESULT,而去重新查詢;
第二步,是把查詢結果註冊到QUERY CACHE中; 調的是以下函式:
/* register query in cache */
void store_query(THD *thd, TABLE_LIST *used_tables);
/*
A table- or a full flush operation can potentially take a long time to
finish. We choose not to wait for them and skip caching statements
instead.
*/
if (try_lock())
DBUG_VOID_RETURN;
這裡也是,如果拿不到鎖就直接跳出。不再進行寫QUERY CACHE。
如果一直等待,那麼SELECT就HANG起
對以上兩個操作,小結了一下: MYSQL在處理SELECT時,對QUERY CACHE的態度時,能用就用,用不上拉到,(響應優先)
下面來看MYSQL在處理INSERT,UPDATE,DELETE過程,
每個事務提交完成前,會對被更新的所有表進行QUERY CACHE清理工作;
事務處理int ha_commit_one_phase(THD *thd, bool all) 呼叫以下函式:
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
THD *thd= current_thd;
for (; tables_used; tables_used= tables_used->next)
{
thd_proc_info(thd, "invalidating query cache entries (table list)");
invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
tables_used->key+
strlen(tables_used->key)+1));
}
DBUG_VOID_RETURN;
}
在這個函式里面,每個表的清理工作:
void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
/*
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
*/
lock();
DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
if (query_cache_size > 0)
invalidate_table_internal(thd, key, key_length);
unlock();
在這裡我們發現,呼叫的是lock(),這個函式需要拿QUERY CACHE的獨佔鎖,如果拿不到,就等待,直到超時;
void Query_cache::lock(void)
{
DBUG_ENTER("Query_cache::lock");
pthread_mutex_lock(&structure_guard_mutex);
while (m_cache_lock_status != Query_cache::UNLOCKED)
pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
到這裡為止,大查詢會阻塞UPDATE這一說法還是成立的;
如果你的QUERY CAHCE足夠大,以及你的查詢結果集夠大,Void store_query()工作時,會拿到鎖,
而UPDATE要清理,也要拿鎖,這時等待;
但我們有幾個引數要看一下:
| query_cache_limit | 2097152 ## 超過2M的的結果集不CACHE
| query_cache_size | 67108864 ## QUERY CACHE 大小
這裡,如果QUERY CAHCE超過2M,其實不會被CACHE; status variable:Qcache_not_cached+1;
但即使儲存2M的結果集,也不會需要很長時間;
要超時,實在是不理解;
到這裡,雖然分析了,DML時QUERY CACHE的相關處理過程,
但還是沒能解釋我們資料庫中,SELECT把我們資料庫UPDATE堵成超時的真正原因;
先作為知識儲備。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/703656/viewspace-1053088/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- DML 語句處理過程
- unusable index對DML/QUERY的影響Index
- 【原始碼】Redis命令處理過程原始碼Redis
- Mapreduce原始碼分析分片、處理流程原始碼
- Guava 原始碼分析(Cache 原理)Guava原始碼
- Spring cache原始碼分析Spring原始碼
- MyBatis原始碼分析之核心處理層MyBatis原始碼
- Laravel Excpetions(錯誤處理) 原始碼分析Laravel原始碼
- Sidekiq 訊號處理原始碼分析IDE原始碼
- [ thanos原始碼分析系列 ]thanos query元件原始碼簡析原始碼元件
- Flashback Query 針對DML誤操作的恢復
- oracle 的DML命令的詳細處理過程Oracle
- #21 Go errors 處理及 zap 原始碼分析GoError原始碼
- 分析直播間原始碼的特點以及對音影片的處理方式原始碼
- 大數量的DML時對索引處理的技巧索引
- cache2go – cachetable原始碼分析Go原始碼
- MySQL Query CacheMySql
- 閃回之 Flashback Query (dml表、過程、函式、包等)、Flashback version Query函式
- external-attacher原始碼分析(2)-核心處理邏輯分析原始碼
- kube-scheduler原始碼分析(2)-核心處理邏輯分析原始碼
- zookeeper原始碼 — 五、處理寫請求過程原始碼
- ViewGroup事件分發和處理原始碼分析View事件原始碼
- ThinkPHP6 原始碼分析之請求處理PHP原始碼
- 原始碼分析:Android訊息處理機制原始碼Android
- 【Zookeeper】原始碼分析之請求處理鏈(一)原始碼
- arm 中斷配置以及處理的原始碼分析原始碼
- Library cache pin問題的處理過程
- beego cache模組原始碼分析筆記四Go原始碼筆記
- Oracle Query Result CacheOracle
- SpringMVC請求處理過程原始碼簡析SpringMVC原始碼
- Kafka原始碼分析(四) - Server端-請求處理框架Kafka原始碼Server框架
- 通過原始碼學習@functools.lru_cache原始碼
- 一次Row Cache Lock問題處理過程
- spring事務管理原始碼分析(二)事務處理流程分析Spring原始碼
- Guava 原始碼分析(Cache 原理【二階段】)Guava原始碼
- LIRE教程之原始碼分析 | LIRE Tutorial of Analysis of the Source Code原始碼
- thinkphp原始碼分析(四)—錯誤及異常處理篇PHP原始碼
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼