MySQL高級特性node
1. 分區表:分區表是一種粗粒度的、簡易的索引策略,適用於大數據量的過濾場景。最適合的場景是,在沒有合適的索引時,對幾個分區進行全表掃描,或者是隻有一個分區和索引是熱點,並且這個分區和索引算法
可以在內存中;限制單表分區數不要超過150個,而且注意某些致使沒法分區過濾的細節,分區表對單條記錄的查詢並無什麼優點,須要注意這類查詢的性能。數據庫
1). 對於用戶來講,分區表是一個獨立的邏輯表,可是底層由多個物理子表組成。實現分區的代碼其實是對一組底層表的句柄對象(Handler)的封裝。緩存
2). MySQL實現分區的方式--對底層表的封裝--意味着索引也是按照分區的子表定義的,而沒有全局索引。安全
3). MySQL在建立表的時候使用PARTITION BY子句定義每一個分區存放的數據。在執行查詢的時候,優化器會根據分區過濾那些沒有咱們須要的數據的分區,這樣查詢就沒法掃描全部分區--只要查找包含服務器
須要數據的分區就能夠了。網絡
4). 下面場景中分區能夠起到很大的做用:併發
a. 表很是大以致於沒法所有放到內存中,或者只在表的最後部分有熱點數據,其餘均是歷史數據。框架
b. 分區表的數據更容易維護。分佈式
c. 分區表的數據能夠分佈在不一樣的物理設備上,從而高效的利用多個硬件設備。
e. 可使用分區表來避免某些特殊的瓶頸,例如InnoDB的單個索引的互斥訪問、ext3文件系統的inode鎖競爭。
f. 若是須要,還能夠備份和恢復獨立的分區,這在很是大的數據集的場景下效果很是好。
5). 分區自己也有一些限制,下面是其中比較重要的幾點:
a. 一個表最多隻有1024個分區
b. 在MySQL5.1中,分區表達式必須是整數或者返回整數表達式。在MySQL5.5中,某些場景能夠直接使用列進行分區。
c. 若是分區字段中有主鍵或者惟一索引的列,那麼全部主鍵列和惟一索引列都必須包含進來。
d. 分區中沒法使用外鍵約束。
6). 爲了保證大數據量的可擴展性,通常有下面兩個策略:
a. 全量掃描數據,不要任何索引:可使用簡單的分區方式存放表,不要任何索引,根據分區的規則大體定位須要的數據位置。只要可以使用WHERE條件,將須要的數據限制在少數分區中,則效率是很高的。
b. 索引數據,並分離熱點:若是數據有明顯的"熱點",而除了這部分數據,其餘數據都不多被訪問到,那麼能夠將這部分熱點數據單獨放在一個分區中,讓這個分區的數據可以有機會都緩存在內存中。這樣查詢
就能夠訪問一個很小的分區表,可以使用索引,也可以有效地使用緩存。
7). 分區可能遇到的問題:
a. NULL值會使分區過濾無效:
b. 分區列和索引列不匹配:若是定義的索引索引列和分區列不匹配,會致使查詢沒法進行分區過濾。
c. 選擇分區的成本可能很高:能夠經過限制分區數量來緩解此問題,根據經驗,對大多數系統來首,100個全部的分區是沒有問題的。
d. 打開並鎖住全部底層表的成本可能很高:這個操做在分區過濾以前發生。
e. 維護分區的成本可能很高:例如重組分區。
8). 查詢優化:
a. 分區最大的優勢就是優化器能夠根據分區函數來過濾一些分區。根據粗粒度索引的優點,經過分區過濾一般可讓查詢掃描更少的數據(在某些場景下)。因此,對於訪問分區表來講,很重要的一點是要在WHERE
條件帶入到分區列中。有時候即便看似多餘的也要帶上,這樣就可讓優化器過濾掉無需訪問的分區。若是沒有這些條件,MySQL就須要讓對應存儲引擎訪問這個表的全部分區,若是表很是大,可能會很是慢。
b. MySQL只能在使用分區函數的列自己進行比較時才能過濾分區,而不能根據表達式的值去過濾分區,即便這個表達式就是分區函數也不行。
c. 一個很重要的原則是:即使在建立分區時可使用表達式,但在查詢時卻只能根據列來過濾分區。
2. 視圖:對於好幾個表的複雜查詢,使用視圖有時候會大大簡化問題。當視圖使用臨時表時,沒法將WHERE條件下推到各個具體的表中,也不能使用任何索引,須要特別注意這列查詢的性能。若是爲了便利,使用視圖是很
合適的。
1). 視圖自己是一個虛擬表,不存聽任何數據。在使用SQL語句訪問視圖的時候,它返回的數據是MySQL從其餘表中生成的。
2). 實現視圖最簡單的方法是將SELECT語句的結果存放到臨時表中。當須要訪問視圖的時候,直接訪問這個臨時表就能夠了。
CREATE VIEW Oceania AS SELECT * FROM Country WHERE Continent='Oceania' WITH CHECK OPTION;
3). 處理視圖有兩種算法,稱爲合併算法和臨時表算法,若是可能,會盡量地使用合併算法。MySQL甚至能夠嵌套地定義視圖,也就是在一個視圖上再定義另外一個視圖。
4). 若是視圖中包含GROUP BY、DISTINCT、任何聚合函數、UNION、子查詢等,只要沒法在原表記錄和視圖記錄中創建一一映射的場景中,MySQL都將使用臨時表算法來實現視圖。
2.1 可更新視圖:可更新視圖是指能夠經過更新這個視圖來更新視圖涉及的相關表。只要指定了合適的條件,就能夠更新、刪除甚至向視圖中寫入數據。臨時表視圖不能被更新。
2.2 視圖對性能的影響:多數人認爲視圖不能提高性能,實際上,在MySQL中某些狀況下視圖也能夠幫助提高性能。並且視圖還能夠和其餘提高性能的方式疊加使用。例如:在重構schema的時候可使用視圖,使得在
修改視圖底層表結構的時候,應用代碼還能繼續不報錯的運行。
1). 可使用視圖實現基於列的權限控制,卻不須要真正的在系統中建立列權限,所以沒有額外的開銷。
2). 使用臨時表算法實現的視圖,在某些時候性能會很糟糕。MySQL以遞歸的方式執行這類視圖,外層查詢的WHERE條件沒法"下推"到構建視圖的臨時表查詢中,臨時表也沒法創建索引。
3). MySQL的視圖還不是那麼成熟。
2.3 視圖的限制:在其餘的關係數據庫中,你可能使用過物化視圖,MySQL還不支持物化視圖(物化視圖是指將視圖結果數據存放在一個能夠查看的表中,並按期從原始表中刷新數據到這個表中).MySQL也不支持在視圖中
建立索引。不過,能夠經過使用構建緩存表或者彙總表的辦法來模擬物化視圖和索引。
3. 外鍵:外鍵限制會將約束放到MySQL中,這對於必須維護外鍵的場景,性能會更高。不過這也會帶來額外的複雜性和額外的索引消耗,還會增長多表之間的交互,會致使系統中更多的鎖和競爭。外鍵能夠被看
做是一個確保系統完整性的額外的特性,可是若是設計的是一個高性能的系統,那麼外鍵就顯得很臃腫了。不少人在更在乎系統的性能的時候都不會使用外鍵,而是經過應用程序來維護的。
1). InnoDB是目前MySQL中惟一支持外鍵的內置存儲引擎。
2). 使用外鍵是有成本的。好比外鍵一般都要求每次在修改數據時都要在另外一張表中多執行一次查詢操做。雖然InnoDB強制外鍵約束使用索引,仍是沒法消除這種約束檢查的開銷。若是外鍵列的選擇性很低,則會致使一個
很是大且選擇性很低的索引。
3). 在某些場合下,外鍵會提高一些性能。若是想確保兩個相關表始終有一致的數據,那麼使用外鍵比下應用程序中檢查一致性的性能要高得多。此外,外鍵在相關數據的刪除和更新上,也比在應用中維護要更高效,
不過,外鍵維護操做是逐行進行的,因此這樣的更新會比批量刪除和更新要慢些。
4). 外鍵約束使得查詢須要額外訪問一些表,這也意味着須要額外的鎖。
5). 有時可使用觸發器來代替外鍵。對於相關數據的同時更新外鍵更合適,可是若是外鍵只是用做數值約束,那麼觸發器或者顯式地限制取值會更好一些(這裏能夠直接使用ENUM類型)。
6). 若是隻是使用外鍵作約束,那一般在應用程序裏實現該約束會更好。外鍵會帶來很大的額外消耗。
4. 存儲過程:MySQL自己實現了存儲過程、觸發器、存儲函數和事件,老實說,這些特性並沒什麼特別的。並且對於基於語句的複製還有不少問題。一般,使用這些特性能夠幫你節省不少網絡開銷--不少狀況下,
減小網路開銷能夠大大提高系統的性能。在某些經典的場景下,你可使用這些特性(例如中心化業務邏輯、繞過全線系統等),但須要注意在MySQL中,這些特性並無別的數據庫系統那麼成熟和全面。
1). MySQL容許經過觸發器、存儲過程、函數的形式來存儲代碼。從MySQL 5.1開始,還能夠在定時任務中存放代碼,這個定時任務也被成爲"事件"。存儲過程和存儲函數都被統稱爲"存儲程序"。
2). 通常來講,存儲代碼是一種很高的共享和複用代碼的方法。
3). 存儲代碼的優勢:
a. 在服務器內部運行,離數據最近,另外在服務器上執行還能夠節省帶寬和網絡延遲。
b. 代碼重用。
c. 能夠簡化代碼的維護和版本更新。
d. 能夠幫助提高安全,例如提供更細粒度的權限控制。如銀行資金轉移。
f. 服務器能夠換成存儲過程的執行計劃。
g. 由於是在服務器端部署的,所以備份、維護均可以在服務器端完成。
h. 它能夠在應用程序和數據庫開發人員之間更好的分工。
存儲代碼的缺點:
a. MySQL自己沒有提供好用的開發和調試工具。
b. 較之應用程序的代碼,存儲代碼效率要稍微差些。 例如:函數有限,沒法編寫複雜字符串維護功能。
c. 存儲代碼可能會給應用程序代碼的部署帶來額外的複雜性。
d. 可能有安全隱患,最好加密。
e. 存儲過程會給數據庫服務器增長額外的壓力,而數據庫服務器的擴展性相比應用服務器要差不少。
f. MySQL沒有什麼選項能夠控制存儲過程的資源消耗,因此在存儲過程當中的一個小錯誤,可能直接把服務器拖死。
g. 調試MySQL的存儲過程是一件很困難的事情。
4.1 存儲過程和函數:咱們一般會但願程序越小、越簡單越好。但願將更復雜的處理邏輯交給上層的應用實現,一般這樣會使代碼更易讀、易維護,也會更靈活。這樣作也會讓你擁有更多的計算資源,潛在的
還會讓你擁有更多的緩存資源。 不過,對於某些操做,存儲過程比其餘的實現要快的多--特別是一個存儲過程調用能夠代替不少小查詢的時候。若是查詢很小,相比這個查詢執行的成本,解析和網絡開銷
就變得很是明顯。
4.2 觸發器:觸發器可讓你在執行INSERT、UPDATE或者DELETE的時候,執行一些特定的操做。能夠在MySQL中指定是在SQL語句執行前觸發仍是在執行後觸發。觸發器自己沒有返回值,不過它們能夠讀取
或者改變觸發SQL語句所影響的數據。因此,可使用觸發器實現一些強制限制,或者而某些業務邏輯,不然,就須要在應用程序中實現這些邏輯。
1). 由於使用觸發器能夠減小客戶端和服務器之間的通訊,因此觸發器能夠簡化應用邏輯,還能夠提升性能。另外,還能夠用於自動更新反範式化數據或者彙總表數據。
2). MySQL觸發器實現很是簡單,因此功能也有限。特別須要注意一下幾點:
a. 對應每個表的每個事件,最多隻能定義一個觸發器(換句話說,不能在AFTER INSERT上定義兩個觸發器)
b. MySQL只支持"基於行的觸發" -- 也就是說,觸發器始終是針對一條記錄的,而不是針對整個SQL語句的。若是變動的數據集很是大的話,效率會很低。
觸發器自己的限制:
a. 觸發器能夠掩蓋服務器背後的工做。例如SQL影響的記錄數翻一倍。
b. 觸發器的問題很難排除
c. 觸發器可能致使死鎖或所等待。
3). 在InnoDB表上的觸發器是在同一個事務中完成的,因此它們執行的操做是原子的,原子操做和觸發器操做會同時失敗或者成功。不過,若是在InnoDB表上的觸發器去檢查數據的一致性,須要特別當心
MVCC,稍不當心,可能會得到錯誤的結果。
4). 可使用觸發器記錄數據變動日誌。
4.3 MySQL 5.1 引入,相似於Linux的定時任務,不過是徹底在MySQL內部實現的。你能夠建立事件,執行MySQL在某個時候執行一段SQL代碼,或者每隔一段時間執行一段SQL代碼。一般,咱們會把複雜
的SQL都封裝到一個存儲過程當中,這樣事件在執行的時候只須要作一個簡單的CALL調用。
1). 若是一個定時事件執行須要很長的時間,那麼有可能會出現這樣的狀況,即前面一個事件還未執行完成,下一個時間點的事件又開始了。MySQL自己不會防止這種併發,因此須要用戶在本身編寫這種
狀況下防併發代碼。你可使用函數GET_LOCK()來確保當前老是有一個事件在被執行。
2). 雖然事件的執行是和鏈接無關的,可是它仍然是線程級別的。MySQL中有一個事件調度線程,必須在MySQL配置文件中設置,或者使用下面的命令來設置。SET GLOBAL event_scheduler :=1
4.4 在存儲過程當中保留註釋:/*! xxxxxxxxxxxxxxxxxxxx */
4.5 遊標:MySQL在服務器端提供只讀的、單向的遊標,並且只能在存儲過程或者更底層的客戶端API中使用。由於MySQL遊標中指向的對象都是存儲在臨時表中而不是實際查詢到的數據,因此MySQL遊標總
是隻讀的。它能夠逐行指向查詢結果,而後讓程序作進一步處理。
5. 綁定變量:當查詢語句的解析和執行計劃生成消耗了主要時間,那麼綁定變量能夠在必定程度上解決問題。由於只須要解析一次,對於大量重複類型的查詢語句,性能會有很大的提升。另外,執行計劃的緩存
和傳輸使用的二進制協議,這都使得綁定變量的方式比普通SQL語句執行的方式要更快。
1). 當建立一個綁定變量SQL時,客戶端向服務器發送一個SQL語句地原型。服務器端收到這個SQL語句框架後,解析並存儲這個SQL語句的部分執行計劃,返回給客戶端一個SQL語句處理句柄。之後每次
執行這類查詢,客戶端都指定使用這個句柄。
2). 綁定變量的SQL,使用問好標記能夠接受參數的位置,當真正須要執行具體的查詢的時候,則使用具體值代替這些問號。
3). 由於以下緣由,MySQL在使用綁定變量的時候能夠更高效地執行大量重複語句:
a. 在服務器端只須要解析一次SQL語句。
b. 在服務器端某些優化器的工做只須要執行一次,由於它會緩存一部分的執行計劃。
c. 使用參數的方式只發送參數和句柄,比起每次都發送ASCII碼文本效率更高。
d. 僅僅是參數 -- 而不是整個查詢語句 -- 須要發送到服務器端,因此網絡開銷會更小。
e. MySQL在存儲參數的時候,直接將其放到緩存中,再也不須要在內存中屢次複製
4). 綁定變量相對也更安全。無需在應用程序中處理轉義,一則更簡單,二則也大大減小了SQL注入和攻擊的風險。
5). 綁定變量的限制:
a. 綁定變量是會話級別的,因此鏈接之間不能共用綁定變量句柄。一樣地,一旦鏈接斷開,則原理的句柄也不能再使用。(鏈接池和持久化鏈接能夠在必定程度上緩解這個問題。)
b. 在MySQL 5.1版本以前,綁定變量的SQL是不能使用查詢緩存的。
c. 並非全部時候的綁定變量都能得到更好的性能。若是隻執行一次SQL,會帶來額外的消耗。
d. 當前版本下,還不能在存儲函數中使用綁定變量(可是存儲過程當中可使用)
e. 若是老是忘記釋放綁定變量資源,則在服務器端很容易發生資源"泄露"。綁定變量SQL總數的限制是一個全侷限制,因此某一個地方的錯誤可能會對其餘的線程都產生影響。
f. 有些操做,如BEGIN,沒法在綁定變量中完成。
6. 插件:使用C或者C++編寫的插件可讓你最大程度的擴展MySQL功能。插件功能很是強大。
7. 字符集:字符集是一種字節到字符之間的映射,而校對規則是一個字符集的排序方法。不少人使用Latin1(默認字符集,對英語和某些歐洲語言有效)或者UTF-8。若是使用的是UTF-8,那麼在使用臨時表和緩衝區
的時候須要注意:MySQL會按照每一個字符三個字節的最大佔用空間來分配存儲空間,這可能消耗更多的內存或者磁盤空間。
注意讓字符集和MySQL字符集配置相符,不然可能會因爲字符集轉換讓某些索引沒法使用。
7.1 MySQL如何使用字符集:只有基於字符的值才真正的"有"字符集的概念。
MySQL的設置能夠分爲兩類:建立對象時的默認值、在服務器和客戶端通訊時的設置
1). 建立對象時的默認設置:
MySQL服務器有默認的字符集和校對規則,每一個數據庫也有本身的默認值,每一個表也有本身的默認值。這是一個逐層繼承的默認設置,最終要靠底層的默認值設置將影響你建立的對象。
這些默認值,至上而下地告訴MySQL應該使用什麼樣的字符集來存儲某個列。
在這個"階梯"的每一層,你均可以指定一個特殊的字符集或者讓服務器使用它的默認值:
a. 建立數據庫的時候,將根據服務器上的character_set_server設置來設定該數據庫的默認字符集。
b. 建立表的時候,將根據數據庫的字符集設置指定這個表的字符集設置。
c. 建立列的時候,將根據表的設置指定列的字符集設置。
須要記住的是,真正放數據的是列,因此更高"階梯"的設置只是指定默認值。一個表的默認字符集設置沒法影響存儲在這個表中某個列的值。只有當建立列而沒有爲該列指定字符集的時候,若是沒有指定字符集,
表的默認字符集纔有用。
2). 服務器和客戶端通訊時的設置
當服務器端和客戶端通訊時的時候,它們可能使用不一樣的字符集。這時,服務器端將進行必要的翻譯轉換工做:
a. 服務器端老是假設客戶端是按照character_set_client設置的字符來傳輸數據和SQL語句的。
b. 當服務器端收到客戶端的SQL語句時,它先將其轉換成字符集character_set_connection。它還使用這個設置來決定如何將數據轉換成字符串。
c. 當服務器返回數據或者錯誤信息給客戶端時,它會將其轉換爲character_set_result.
3). MySQL如何比較兩個字符串的大小:若是兩個字符串的字符集不一樣,MySQL會先將其轉換成同一個字符集再進行比較,若是兩個字符集不兼容的話,則會拋出錯誤。
7.2 選擇字符集和校對規則:
1). 正確的作法是,最要好先爲服務器(或者數據庫) 選擇一個合理的字符集,而後根據不一樣的實際狀況,讓某些列選擇合適的字符集。
2). 對於校驗規則一般須要考慮的一個問題是,是否以大小寫敏感的方式比較字符串,或者是以字符串編碼的二進制值來比較大小。它們對應的校驗規則的前綴分別是_cs、_ci和_bin,根據須要很容易選擇。大小寫
敏感和二進制校對規則的不一樣之處在於,二進制校對規則直接使用字符的字節進行比較,而大小寫敏感的校對規則在多字節字符集時,若是有德語,有更復雜的比較規則。
7.3 字符集和校對規則如何影響查詢:某些字符集和校對規則可能會須要更多的CPU操做,可能會消耗更多的內存和存儲空間,甚至還會影響索引的正常使用。因此在選擇字符集的時候,也有一些須要注意的地方。
1). 不一樣的字符集和校對規則之間的轉換可能會帶來額外的系統開銷。
2). 只有排序查詢的字符集與服務器數據的字符集相同的時候,才能使用索引進行排序。
3). 爲了可以適應各類字符集,包括客戶端字符集、在查詢中顯式指定的字符集,MySQL會在須要的時候進行字符集轉換。若是你不肯定是否進行了轉換,能夠在EXPLAIN EXTENDED後使用SHOW WARNINGS
來查看MySQL是如何處理的。
4). 在多字節字符集中,一個字符再也不是一個字節。因此,MySQL中有兩個函數LENGTH()和CHAR_LENGTH()來計算字符串的長度。
8. 全文索引:在5.6版本以前只有MyISAM支持全文索引,不過聽說5.6開始,InnoDB也將支持全文索引。MyISAM由於在鎖粒度和崩潰恢復上的缺點,使得在大型全文索引場景中基本沒法使用。這時,咱們一般
幫助客戶構建和使用Sphinx來解決全文索引問題。
9. XA(分佈式)事務:不多有人使用MySQL的XA事務特性。除非你真正明白參數innodb_support_xa的意義,不然不要修改這個參數的值,並非只有顯式使用XA事務時才須要設置這個參數。InnoDB和二進制日誌也是
須要使用XA事務來作協調的,從而確保在系統崩潰的時候,數據可以一致地恢復。
1). 存儲引擎的事務特徵可以保證事務在存儲引擎級別實現ACID,而分佈式事務則讓存儲引擎級別的ACID能夠擴展到數據庫層面,甚至能夠擴展到多個數據庫之間。這須要兩個階段提交實現。XA事務中須要一個事務
協調器來保證全部的事務參與者都完成了準備工做(第一階段)。若是協調器收到全部的參與者都準備好的消息,就會告訴全部的事務能夠提交了,這是第二階段。MySQL在這個XA事務過程當中扮演了一個參與者的
角色,而不是協調者。
2). 實際上,在MySQL中有兩種XA事務。一方面,MySQL能夠參與到外部的分佈式事務中;另外一方面,還能夠經過XA事務來協調存儲引擎和二進制日誌。
3). 內部XA事務:若是將MySQL記錄二進制日誌操做看做一個獨立的"存儲引擎",就不難理解爲何即便是一個存儲引擎參與的事務仍然須要XA事務了。在存儲引擎提交的同時,須要將"提交"的信息寫入二進制日誌,
這就是一個分佈式事務,只不過二進制日誌的參與者是MySQL自己。XA事務會爲MySQL帶來巨大的性能降低,惟一避免這個問題的辦法是關閉二進制日誌,但這樣設置很是不安全。
4). 外部XA事務:MySQL可以做爲一個參與者完成一個外部的分佈式事務。但它對XA協議支持並不完整。
a. 由於通訊延遲或者參與者自己可能失敗,因此外部XA事務比內部消耗更大。
b. 一般,還可使用別的方式實現高性能的分佈式事務。例如,能夠在本地寫入數據,並將其放入隊列,而後在一個更小、更快的事務中自動分發。還可使用MySQL自己的複製機制來發送數據。徹底能夠避免
使用分佈式事務。
c. 也就是說,XA事務是一種在多個服務器之間同步數據的方法。若是因爲某些緣由不能使用MySQL自己的複製,或者性能並非瓶頸的時候,能夠嘗試使用。
10. 查詢緩存:徹底相同的查詢在重複執行的時候,查詢緩存可用當即返回結果,而無須在數據庫從新執行一次。根據咱們的經驗,在高併發壓力環境中查詢緩存會致使系統性能的降低,甚至僵死。若是你必定要使用
查詢緩存,那麼不要設置太大的內存,並且只有在明確收益的時候才使用。那該如何判斷是否應該使用查詢緩存呢?建議使用Percona Server,觀察更細緻的日誌,並作一些簡單的計算。還能夠查看緩存命中率(
並非老是有用)、"INSERTS和SELECT比率"(這個參數也並不直觀)或者"命中和寫入比率"(這個參考意義較大)。查詢緩存時一個很是方便的緩存,可對應用程序徹底透明,無需任何額外的編碼,可是,若是但願
有更高的緩存效率,咱們建議使用memcached或者其餘相似的解決方案。
1). 不少數據庫產品都可以緩存查詢的執行計劃,對於相同類型的SQL就能夠跳過SQL解析和執行計劃生成階段。MySQL在某些場景下也能夠實現,可是MySQL還另一種不一樣的緩存類型:緩存完整的SELECT查詢結果,
也就是"查詢緩存"。
2). MySQL查詢緩存保存查詢返回的完整結果。當查詢命中該緩存,MySQL會馬上返回結果,跳過了解析、優化和執行階段。
3). 查詢緩存系統會跟蹤查詢中涉及的每一個表,若是這些表發生變化,那麼和這個表相關的全部緩存數據都將消失。
4). 查詢緩存對應用程序是透明的。
5). 隨着如今通用服務器愈來愈大,查詢緩存被發現是一個影響服務器擴展性的因素。
10.1 MySQL如何判斷緩存命中:MySQL判斷緩存命中的方法很簡單:緩存存放在一個引用表中,經過一個哈希值引用,這個哈希值包括以下因素,即查詢自己、當前要查詢的數據庫、客戶端協議的版本等。
1). 任何字符的不一樣,例如:空格、註釋,都會致使緩存不命中。
2). 當查詢語句中有一些不肯定的數據時,則不會被緩存。例如包括函數:NOW()或者CURRENT DATE()。
3). MySQL的查詢緩存在不少時候能夠提高查詢性能,在使用的時候。有一些問題須要特別注意。首先打開查詢緩存對讀和寫操做會帶來額外的消耗:
a. 讀查詢開始以前必須先檢查是否命中緩存。
b. 將查詢結果存入到查詢緩存,這會帶來額外的系統消耗。
c. 對寫入操做也有影響,若是向某個表寫入數據時,必須將對應的表的查詢緩存設置爲失效。
d. 若是查詢緩存使用了大量的內存,緩存失效操做就可能成爲一個很是嚴重的問題瓶頸。
10.2 查詢緩存如何使用內存:查詢緩存徹底存儲在內存中,除了查詢結果以外,須要緩存的還有不少別的維護相關的數據。查詢緩存的內存被分爲不少小塊,供每一個查詢緩存使用。
10.3 什麼狀況下查詢緩存能發揮做用:
1). 對於那些須要消耗大量資源的查詢一般都是很是適合緩存的。例如一些彙總計算查詢,具體的如COUNT()等。總的來講,對於複雜的SELECT語句均可以使用查詢緩存。
2). 須要進行不少次相同查詢的
3). 推薦另一個指標:"命中和寫入"的比率,即Qcache_hits和Qcache_inserts的比值。根據經驗來看,當這個比值大於3:1時一般查詢緩存時有效的,不過這個比率最好可以達到10:1。
10.4 如何配置和維護查詢緩存:
1). 可供配置的參數:
a. query_cache_type:是否打開查詢緩存。能夠設置成OFF、ON或DEMAND。DEMAND表示只有在查詢語句中明確寫明SQL_CACHE的語句才放入查詢緩存。這個變量能夠是會話級別的,也能夠是全局級別的。
b. query_cache_size:查詢緩存使用的總內存空間,但爲時字節。這是值必須是1024的整數倍,不然MySQL實際分配的數據和你指定的略有不一樣。
c. query_cache_min_res_unit:在查詢緩存中分配內存時的最小單位。
d. query_cache_limit:可以緩存的最大查詢結果。若是你事先知道有不少這樣的狀況發生,那麼建議在查詢語句中加入SQL_NO_CACHE來避免查詢緩存帶來的額外消耗。
e. query_cache_wlock_oinvalidate:若是某個數據表被其餘的鏈接鎖住,是否仍然從查詢緩存中返回結果。通常無需注意。
2). 減小碎片:
1). 沒有什麼方法可以徹底避免碎片,可是選擇合適的query_cache_min_res_unit能夠幫你減小由碎片致使的內存空間浪費。設置合適的值能夠平衡每一個數據塊的大小和每次存儲結果時內存塊申請的次數。
這個參數的大小和應用程序的查詢結果的平均值大小直接相關。
2). 可使用命令FLUSH_QUEYR_CACHE完成碎片整理。這個命令會將全部的查詢緩存從新排序,並將全部的空閒空間都彙集到查詢緩存的一塊區域上。不過須要注意,這個命令並不會將查詢緩存清空,
清空查詢緩存由命令RESET_QUERY_CACHE完成。
3). 提升查詢緩存的使用率:找到命中率很低的緣由,是不是由於內存過小?是不是由於查詢緩存無效(能夠經過將query_cache_size設置爲0,來關閉查詢緩存)?
10.5 InnoDB和查詢緩存:由於InnoDB有本身的MVCC機制,因此相比其餘存儲引擎,InnoDB的查詢緩存更復雜。InnoDB會控制在一個事務中是否可使用查詢緩存,InooDB會同時控制查詢緩存的讀和寫操做。
事務是否能夠訪問查詢緩存取決於當前事務ID,以及對應數據表上是否有鎖。每一個InnoDB表的內存數據字典都保存了一個事務ID號,若是當前事務ID小於該事務ID,則沒法訪問查詢緩存。
10.6 通用查詢緩存優化:
1). 除了前文介紹的,還有如下幾點:
a. 用多個小表代替一個大表對查詢緩存有好處。
b. 批量寫入時只須要一次緩存失效,因此相比單條寫入效率更好。
c. 爲防止緩存空間太大,致使服務器僵死,能夠控制緩存空間的大小
d. 沒法在數據庫或者表級別控制查詢緩存,可是能夠經過SQL_CACHE和SQL_NO_CACHE來控制SELECT語句是否進行緩存。
e. 對於寫密集型應用來講,直接禁用查詢緩存可能會提升系統性能。
f. 由於對互斥信號量的競爭,有時直接關閉查詢緩存對讀密集型的應用也會有好處。若是你但願提升系統併發,那麼最好作一個相關的測試,對比打開和關閉查詢緩存時候的性能差別。
2). 若是不想全部的查詢都進入查詢緩存,可是又但願某些查詢走查詢緩存,那麼能夠將query_cache_type設置成DEMAND,而後在但願緩存的查詢中加上SQL_CACHE。
10.7 查詢緩存的替代方案:客戶端緩存(分佈式緩存?)