ITPub博客

首页 > 应用开发 > IT综合 > DML对QUERY CACHE 处理过程之源码分析

DML对QUERY CACHE 处理过程之源码分析

原创 IT综合 作者:Steven1981 时间:2011-07-27 22:35:11 0 删除 编辑
问题:
当一个大的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/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
  • 博文量
    127
  • 访问量
    838718