Rumah  >  Artikel  >  php教程  >  对于mysql的query_cache认识的误区

对于mysql的query_cache认识的误区

高洛峰
高洛峰asal
2016-12-22 15:30:271054semak imbas

其实,这一种说法是不完全正确的。首先第一点,mysql的query_cache的键值并不是简单的query,而是query加databasename加flag。这个从源码中就可以看出。在这里不做重点描述,后续可以针对于这一点再具体分析。重要的是第二点,是不是加了空格,mysql就认为是不同的查询呢?实际上这个是要分情况而言的,要看这个空格加在哪。 如果空格是加在query之前,比如是在query的起始处加了空格,这样是丝毫不影响query cache的结果的,mysql认为这是一条query, 而如果空格是在query中,那会影响query cache的结果,mysql会认为是不同的query。


下面我们通过实验及源码具体分析。首先,我们先试验一下:

首先,我们看一下mysql query_cache的状态:

对于mysql的query_cache认识的误区

首先,我们可以确认,mysql的query_cache功能是打开的。

其次,我们看一下状态:

对于mysql的query_cache认识的误区

因为这个db是新的db,所以hits,inset都为0,现在我们执行一条select语句:

状态变为:

对于mysql的query_cache认识的误区

可以看到,执行一条select后,现在的qcache状态为,insert+1,这样我们就可以推断出,现在刚才那条select语句已经加入了qcache中。那我们现在再将刚才那条sql前面加上空格,看看会怎样呢?

对于mysql的query_cache认识的误区

请注意,这条sql,比刚才那条sql前面多了一个空格。

按照网上的理论,这条sql应该会作为另一个键而插入另一个cache,不会复用先前的cache,但结果呢?

对于mysql的query_cache认识的误区

我们可以看到,hits变为了1,而inserts根本没变,这就说明了,这条在前面加了空格的query命中了没有空格的query的结果集。从这,我们就可以得出结论,网上先前流传的说法,是不严谨的。

那究竟是怎么回事呢?到底应该如何呢?为什么前面有空格的会命中了没有空格的query的结果集。其实,这些我们可以通过源码获得答案。

翻看下mysql的源码,我这翻看的是5.1的,在send_result_to_client(这个函数既是mysql调用query_cache的函数)这个函数里面有这样一段,这段代码,、

/* 
Test if the query is a SELECT 
(pre-space is removed in dispatch_command). 

First '/' looks like comment before command it is not 
frequently appeared in real life, consequently we can 
check all such queries, too. 
*/ 
if ((my_toupper(system_charset_info, sql[i]) != 'S' || 
my_toupper(system_charset_info, sql[i + 1]) != 'E' || 
my_toupper(system_charset_info, sql[i + 2]) != 'L') && 
sql[i] != '/') 
{ 
DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached")); 
goto err; 
}

是在检验语句是否为select语句,重点是上面那段注释。特别是括弧中的,pre-space is removed in dispatch_command,也就是说,在语句开始之前的多余的空格已经被处理过了,在dispache_command这个函数中去掉了。


我们看下dispache_command这个方法,在这个方法里有这样一段:

if (alloc_query(thd, packet, packet_length)) 
break; // fatal error is set 
char *packet_end= thd->query() + thd->query_length(); 
/* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */ 
const char* end_of_stmt= NULL;

在这里,会调用alloc_query方法,我们看下这个方法的内容: 

bool alloc_query(THD *thd, const char *packet, uint packet_length) 
{ 
char *query; 
/* Remove garbage at start and end of query */ 
while (packet_length > 0 && my_isspace(thd->charset(), packet[0])) 
{ 
packet++; 
packet_length--; 
} 
const char *pos= packet + packet_length; // Point at end null 
while (packet_length > 0 && 
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1]))) 
{ 
pos--; 
packet_length--; 
} 
/* We must allocate some extra memory for query cache 
The query buffer layout is: 
buffer :== 
<statement> The input statement(s) 
&#39;\0&#39; Terminating null char (1 byte) 
<length> Length of following current database name (size_t) 
<db_name> Name of current database 
<flags> Flags struct 
*/ 
if (! (query= (char*) thd->memdup_w_gap(packet, 
packet_length, 
1 + sizeof(size_t) + thd->db_length + 
QUERY_CACHE_FLAGS_SIZE))) 
return TRUE; 
query[packet_length]= &#39;\0&#39;; 
/* 
Space to hold the name of the current database is allocated. We 
also store this length, in case current database is changed during 
execution. We might need to reallocate the &#39;query&#39; buffer 
*/ 
char *len_pos = (query + packet_length + 1); 
memcpy(len_pos, (char *) &thd->db_length, sizeof(size_t)); 
thd->set_query(query, packet_length); 
/* Reclaim some memory */ 
thd->packet.shrink(thd->variables.net_buffer_length); 
thd->convert_buffer.shrink(thd->variables.net_buffer_length); 
return FALSE; 
}

这个方法在一开始就会对query进行处理(代码第4行),将开头和末尾的garbage remove掉。 
看到这里,我们基本已经明了了,mysql会对输入的query进行预处理,将空格等东西给处理掉,所以不会开头的空格不会影响到query_cache,因为对mysql来说,就是一条query。

更多对于mysql的query_cache认识的误区相关文章请关注PHP中文网!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn