程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> 關於MYSQL數據庫 >> 對於mysql的query_cache認識的誤區

對於mysql的query_cache認識的誤區

編輯:關於MYSQL數據庫
其實,這一種說法是不完全正確的。首先第一點,mysql的query_cache的鍵值並不是簡單的query,而是query加databasename加flag。這個從源碼中就可以看出。在這裡不做重點描述,後續可以針對於這一點再具體分析。重要的是第二點,是不是加了空格,mysql就認為是不同的查詢呢?實際上這個是要分情況而言的,要看這個空格加在哪。 如果空格是加在query之前,比如是在query的起始處加了空格,這樣是絲毫不影響query cache的結果的,mysql認為這是一條query, 而如果空格是在query中,那會影響query cache的結果,mysql會認為是不同的query。

下面我們通過實驗及源碼具體分析。首先,我們先試驗一下:

首先,我們看一下mysql query_cache的狀態:

image

首先,我們可以確認,mysql的query_cache功能是打開的。

其次,我們看一下狀態:

image

因為這個db是新的db,所以hits,inset都為0,現在我們執行一條select語句:

狀態變為:

image

可以看到,執行一條select後,現在的qcache狀態為,insert+1,這樣我們就可以推斷出,現在剛才那條select語句已經加入了qcache中。那我們現在再將剛才那條sql前面加上空格,看看會怎樣呢?

image

請注意,這條sql,比剛才那條sql前面多了一個空格。

按照網上的理論,這條sql應該會作為另一個鍵而插入另一個cache,不會復用先前的cache,但結果呢?

image

我們可以看到,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。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved