下面我們通過實驗及源碼具體分析。首先,我們先試驗一下:
首先,我們看一下mysql query_cache的狀態:
首先,我們可以確認,mysql的query_cache功能是打開的。
其次,我們看一下狀態:
因為這個db是新的db,所以hits,inset都為0,現在我們執行一條select語句:
狀態變為:
可以看到,執行一條select後,現在的qcache狀態為,insert+1,這樣我們就可以推斷出,現在剛才那條select語句已經加入了qcache中。那我們現在再將剛才那條sql前面加上空格,看看會怎樣呢?
請注意,這條sql,比剛才那條sql前面多了一個空格。
按照網上的理論,這條sql應該會作為另一個鍵而插入另一個cache,不會復用先前的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)
'\0' 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]= '\0';
/*
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 'query' 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。