Chinaunix首页 | 论坛 | 博客
  • 博客访问: 663359
  • 博文数量: 66
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 2204
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-26 21:43
个人简介

曾就职于阿里巴巴担任Oracle DBA,MySQL DBA,目前在新美大担任SRE。[是普罗米修斯还是一块石头,你自己选择!] 欢迎关注微信公众号 “自己的设计师”,不定期有原创运维文章推送。

文章分类

全部博文(66)

文章存档

2017年(2)

2016年(3)

2015年(7)

2014年(12)

2013年(42)

分类: Mysql/postgreSQL

2013-12-13 21:50:54

   FLUSH QUERY CACHE vs RESET QUERY CACHE的区别
  Mysql在开启了QC的情况下,这两个命令会对QC造成影响,那这两个命令会带来什么样的影响呢?我们来看看源码中的解释说明:
  [1] flush query cache
  Query_cache::pack
       - Used when a FLUSH QUERY CACHE is issued. This changes the order of
         the used memory blocks in physical memory order and move all avail-
         able memory to the 'bottom' of the memory.
   这个函数就是执行FLUSH QUERY CACHE是用到的函数。它的作用是改变blocks的顺序,从而将可用的memory添加到QC的底部。

   [2] reset query cache
    Query_cache::flush
       - Used when a RESET QUERY CACHE is issued. This clears the entire
         cache block by block.
 这个函数是执行reset query cache用到的函数,会清除QC里面所有的query block.
 执行flush query cache时会有如下的操作:
 Packing cache.
分成两个步骤:
Query cache packing is divided into two operation:
- pack_cache
- join_results
pack_cache moved all blocks to "top" of cache and create one block of free
space at the "bottom":
把存放数据的block往上移动,空闲的block往下移动,最后可以看到只剩下一个free的block在底部:
 before pack_cache    after pack_cache
 
pack_cache scan blocks in physical address order and move every non-free
block "higher".
pack_cache 函数会按照block的物理地址顺序扫描数据块,并向上移动那些非空闲的块,从而让他们更紧凑.

pack_cach remove every free block it finds. The length of the deleted block
is accumulated to the "gap". All non free blocks should be shifted with the
"gap" step.
标记为delete的数据块也会被当做gap被填充。
join_results scans all complete queries. If the results of query are not
stored in the same block, join_results tries to move results so, that they
are stored in one block.
join_results 会扫描所有完整的结果query,如果同一条query的结果不是存储在同一个block里面,该函数会尝试移动该结果到同一个block.
具体操作如下:
 before join_results  after join_results

If join_results allocated new block(s) then we need call pack_cache again.
下面我们来看看代码的具体实现:
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
  DBUG_ENTER("Query_cache::pack");
  bool interrupt;
  STRUCT_LOCK(&structure_guard_mutex);  //会得到锁,所以此时会存在锁争用问题,如果有其他查询或者dml操作,可能会引起lock contention.
  wait_while_table_flush_is_in_progress(&interrupt);// 会判断是否有flush table或者full flush 存在,并设置interrupt
  if (interrupt)
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    DBUG_VOID_RETURN;
  }
  if (query_cache_size == 0)  //判断query cache是否设置可用
  {
    STRUCT_UNLOCK(&structure_guard_mutex);
    DBUG_VOID_RETURN;
  }
  uint i = 0;
  do
  {
    pack_cache();
  } while ((++i < iteration_limit) && join_results(join_limit));
  STRUCT_UNLOCK(&structure_guard_mutex);
  DBUG_VOID_RETURN;
}
在上面可以看到,在flsuh QC的时候,虽然能够清理碎片,但同时也会导致QC加锁,并发较高的环境,可能会带来一定的影响。
那么我们在来看看reset query cache具体实现如何:
Query_cache::flush
       - Used when a RESET QUERY CACHE is issued. This clears the entire
         cache block by block.
reset query cache会清除所有的query block.
void Query_cache::flush()
{
  DBUG_ENTER("Query_cache::flush");
  STRUCT_LOCK(&structure_guard_mutex);  //同样会加锁,存在
  if (query_cache_size > 0)
  {
    DUMP(this);
    flush_cache();   //会执行cache query block清除操作
    DUMP(this);
  }
  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
  STRUCT_UNLOCK(&structure_guard_mutex);
  DBUG_VOID_RETURN;
}
在这个函数中,同样也会存在mutex竞争问题,当然在flush_cache中会有一段暂时释放锁的过程:
Flush the cache.
  This function will flush cache contents.  It assumes we have
  'structure_guard_mutex' locked. The function sets the m_cache_status flag and
  releases the lock, so other threads may proceed skipping the cache as if it
  is disabled. Concurrent flushes are performed in turn.
void Query_cache::flush_cache()
{
  .....
  /*
    Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
    the cache while we are in the middle of the flush, and we release
    the lock so that other threads won't block.
  */
  m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
  STRUCT_UNLOCK(&structure_guard_mutex); //会解锁
  my_hash_reset(&queries);
  while (queries_blocks != 0)
  {
    BLOCK_LOCK_WR(queries_blocks);
    free_query_internal(queries_blocks);
  }
  STRUCT_LOCK(&structure_guard_mutex);
  m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
  pthread_cond_signal(&COND_cache_status_changed);
}

我们会看到,在一段时间内,锁是释放的,这样其他的线程会跳过QC就像它不存在一样。所以在reset query cache中可能会引起的阻塞时间很短,在物理内存清理期间,不会阻塞查询,所有的查询会跳过QC。
从上总结两点:
                   [1] 在执行flush query cache的时候,会整理QC的碎片,如果碎片过多,可能会导致阻塞时间过程,从而影响其他线程的操作。在一些高并发的环境中不建议使用该操作。
                   [2] 在执行reset query cache的时候会reset QC,将QC的非free的block全部清除,在此过程中会有短暂的lock contention,可能会造成短时间的阻塞,在正执行清理阶段会释放锁。
我们来对flush cache操作,从mysql观察到的结果:
查看QC是否处于开启状态:
mysql> show variables like 'QUERY_CACHE%';
+------------------------------+----------+
| Variable_name                | Value    |
+------------------------------+----------+
| query_cache_limit            | 1048576  | 
| query_cache_min_res_unit     | 4096     | 
| query_cache_size             | 16777216 | 
| query_cache_type             | ON       | 
| query_cache_wlock_invalidate | OFF      | 
+------------------------------+----------+
5 rows in set (0.00 sec)

QC当前的状态信息:
mysql> show status like 'QC%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 3146      | 
| Qcache_free_memory      | 10224984  | 
| Qcache_hits             | 217457548 | 
| Qcache_inserts          | 602107778 | 
| Qcache_lowmem_prunes    | 28154940  | 
| Qcache_not_cached       | 56853134  | 
| Qcache_queries_in_cache | 5938      | 
| Qcache_total_blocks     | 15036     | 
+-------------------------+-----------+
8 rows in set (0.00 sec)

执行flush query cache
mysql> flush query cache;
Query OK, 0 rows affected (0.01 sec)

执行之后的信息:
mysql> show status like 'QC%';
+-------------------------------------+--------------------+
| Variable_name                 | Value             |
+-------------------------------------+--------------------+
| Qcache_free_blocks          | 1                  | 
| Qcache_free_memory       | 10225288      | 
| Qcache_hits                     | 217457548    | 
| Qcache_inserts                 | 602107778    | 
| Qcache_lowmem_prunes   | 28154940      | 
| Qcache_not_cached         | 56853135      | 
| Qcache_queries_in_cache  | 5938             | 
| Qcache_total_blocks         | 11891           | 
+-------------------------------------+--------------------+
8 rows in set (0.00 sec)
Qcache_free_blocks 变为了1,Qcache_total_blocks 有所减少。
我们可以看到,由于执行了flush query cache,所有非free的block都向上移动,而所有free的block都组成了一个free block(Qcache_free_blocks的值可以看到) 。同时由于数据存储更紧凑,总的block也减少了(Qcache_total_blocks 的值可以看到)。
query cache 在很多时候都会存在锁争用的问题,如何使用,是否使用依赖于具体的应用。如果是高并发的环境,又或者数据改动比较频繁,建议最好关闭QC。最近公司好几次都遇到QC导致mysql阻塞,其他查询无法使用的情况。对于OLTP环境建议最好不要使用,或者query_cache_type=2(query加上SQL_CACHE才会存储在QC中).




















阅读(1306) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~