1. 程式人生 > >高效能mysql第三版讀書筆記

高效能mysql第三版讀書筆記

第1章 MySQL 架構與歷史

MySQL最重要、最與眾不同的特性是它的儲存引擎架構,這種架構的設計將查詢處理(Query Processing)以及其他系統任務(Server Task)和資料的儲存/提取相分離。這種處理和儲存分離的設計可以在使用時根據效能、特性,以及其他需求來選擇資料儲存的方式。

1.1 MySQL邏輯架構

image

1.1.1 連線管理與安全性

每個客戶端連線都會在伺服器程序中擁有一個執行緒,這個連線的查詢只會在這個單獨的執行緒中執行。伺服器會負責快取執行緒,因此不需要為每一個新建的連線建立或者銷燬執行緒(也可以使用執行緒池)。當客戶端(應用)連線到MySQL伺服器時,伺服器需要對其進行認證。

1.1.2 優化與執行

MySQL會解析查詢,並建立內部資料結構(解析樹)然後對其進行各種優化,包括重寫查詢、決定表的讀取順序,以及選擇合適的索引等。對於select語句,在解析查詢之前,伺服器會先檢查查詢快取,如果有則直接返回查詢快取中的結果集。

1.2 併發控制

MySQL在兩個層面控制併發:伺服器層與儲存引擎層。

每種MySQL儲存引擎都可以實現自己的鎖策略和鎖粒度。

1.2.1 讀寫鎖

在處理併發讀或者寫時,可以通過實現一個由兩種型別的鎖組成的鎖系統來解決問題。這兩種型別的鎖通常被稱為共享鎖和排它鎖,也叫讀鎖和寫鎖。讀鎖是共享的,或者說是互相不阻塞的。寫鎖則是排他的,也就是說一個寫鎖會阻塞其他的讀鎖和寫鎖。

1.2.2 鎖粒度

表鎖:表鎖是MySQL中最基本的鎖策略,並且是開銷最小的策略。伺服器會為諸如ALTER TABLE之類的語句使用表鎖,而忽略儲存引擎的鎖機制。

行級鎖:行級鎖可以最大程度地支援併發處理(同時也帶來了最大的鎖開銷)。InnoDB和XtraDB,以及其他一些儲存引擎中實現了行級鎖。行級鎖只在儲存引擎層實現,而MySQL伺服器層沒有實現。伺服器層完全不瞭解儲存引擎中的鎖實現。

1.3 事務

事務就是一組原子性的SQL查詢,或者說一個獨立的工作單元。事務內的語句,要麼全部執行成功,要麼全部執行失敗。

ACID表示原子性(atomicity)、一致性(consistency)、隔離性(isolation)和永續性(durability)。

image

1.3.1 隔離級別

image

image

image

1.3.2 死鎖

資料庫系統實現了各種死鎖檢測和死鎖超時機制。死鎖發生以後,只有部分或者完全回滾其中一個事務,才能打破死鎖。對於事務型的系統,這是無法避免的,所以應用程式在設計時必須考慮如何處理死鎖。

1.3.3 事務日誌

事務日誌持久以後,記憶體中被修改的資料在後臺可以慢慢地刷回到磁碟。目前大多數儲存引擎都是這樣實現的,我們通常稱之為預寫式日誌,修改資料需要寫兩次磁碟。

1.3.4 MySQL中的事務

MySQL提供了兩種事務型的儲存引擎:InnoDB和NDB Cluster。

MySQL預設採用自動提交模式。也就是說,如果不顯示地開始一個事務,則每個查詢都被當作一個事務執行提交操作。

修改AUTOCOMMIT對非事務型的表,比如MyISAM或者記憶體表,不會有任何影響。對這類表來說,沒有COMMIT或者ROLLBACK的概念。

MySQL伺服器層不管理事務,事務是由下層的儲存引擎實現的。如果在事務中混合使用了事務型和非事務型的表(例如InnoDB和MyISAM表),在正常提交的情況下不會有什麼問題,但是如果該事務需要回滾,非事務型的表上的變更就無法撤銷。

1.4 多版本併發控制

MySQL的大多數事務型儲存引擎實現的都不是簡單的行級鎖。基於提升併發效能的考慮,它們一般都同時實現了多版本併發控制(MVCC)。

InnoDB的MVCC,是通過在每行記錄後面儲存兩個隱藏的列來實現的。這兩個列一個儲存了行的建立時間,一個儲存了行的過期時間(或刪除時間)。當然儲存的並不是實際的時間值,而是系統版本號。每開始一個新的事務,系統版本號都會自動遞增。事務開始時刻的版本號會作為事務的版本號,用來和查詢到的每行記錄的版本號進行比較。下面看一下再REPEATABLE READ隔離級別下,MVCC具體是如何操作的:

image

MVCC只在REPEATABLE READ和READ COMMITTED兩個隔離級別下工作。

1.5 MySQL的儲存引擎

1.5.1 InnoDB儲存引擎

是MySQL的預設事務型引擎,最廣泛使用的引擎。被設計來處理大量的短期事務,短期事務大部分情況下是正常提交的,很少會被回滾。InnoDB的效能和自動崩潰恢復特性,使得它再非事務型的需求中也很流行。

InnoDB的資料儲存在表空間中,表空間是由InnoDB管理的一個黑盒子。

InnoDB採用MVCC來支援高併發,並且實現了四個標準的隔離級別。其預設級別是REPEATABLE READ(可重複讀),並且通過間隙鎖策略防止幻讀的出現。間隙鎖使得InnoDB不僅僅鎖定查詢涉及的行,還會對索引中的間隙進行鎖定,以防止幻影行的插入。

InnoDB表是基於聚簇索引建立的。不過它的二級索引中必須包含主鍵列,所以如果主鍵列很大的話,其他的所有索引都會很大。

1.5.2 MyISAM儲存引擎

MyISAM提供了大量的特性,包括全文索引、壓縮、空間函式等,但它不支援事務和行級鎖,而且有一個毫無疑問的缺陷就是崩潰後無法安全恢復。

MyISAM對整張表加鎖,而不是針對行。

延遲更新索引鍵策略,不會立刻將修改的索引資料寫入磁碟,而是先存在記憶體中的鍵緩衝區。

MyISAM壓縮表:壓縮表不能修改(除非先解壓縮),壓縮表的索引也是隻讀的。

1.5.3 MySQL內建的其他儲存引擎

見書中。

1.5.6 轉換表的引擎

第一種方法:

ALTER TABLE mytable ENGINE = InnoDB;

缺點是執行時間長,會按行將資料從原表複製到一張新的表中,消耗很大的I/O,且會對原表上讀鎖。

第二種方法:使用mysqldump工具將資料匯出到檔案,然後修改檔案中CREATE TABLE語句的儲存引擎選項。

第三種方法:建立一個新表,然後使用INSETR…SELECT語法匯入資料

CREATE TABLE innodb_table LIKE myisam_table;
ALTER TABLE innodb_table ENGINE = InnoDB;
INSERT INTO innodb_table SELECT * FROM myisam_table

如果原表資料量很大,可以使用事務分批匯入,也可以對原表加鎖,確保資料一致。

第2章 MySQL 基準測試

2.4基準測試工具

2.4.1整合式測試工具

ab、http_load、JMeter

2.4.2單元件式測試工具

mysqlslap、MySQL Benchmark Suite(sql-bench)、Percona’s TPCC-MySQL Toll、sysbench

以後如果有需要測試時,再檢視此章

第3章 伺服器效能剖析

3.1 效能優化簡介

我們將效能定義為完成某件任務所需要的時間度量,換句話說,效能即響應時間。我們假設效能優化就是在一定的工作負載下儘可能地降低響應時間。

3.3 剖析MySQL查詢

3.3.2 剖析單條查詢

使用SHOW PROFILE

預設是禁用的,但可以通過伺服器變數在會話(連線)級別動態地修改。

mysql>SET profiling = 1;

然後,在伺服器上執行的所有語句,都會測量其耗費的時間和其他一些查詢執行狀態變更相關的資料。使用示例:

mysql> SHOW PROFILES;

上面語句會列出查詢的響應時間,如果要查詢某一條查詢的細粒度的資訊,可以這樣:

mysql> SHOW PROFILE FOR QUERY 1;

如果想按照消耗時間排序,可以這樣:

set @query_id = 1;
SELECT STATE,SUM(DURATION) AS Total_R,
    ROUND(
        100 * SUM(DURATION) /
        (SELECT SUM(DURATION)
        FROM INFORMATION_SCHEMA.PROFILING
        WHERE QUERY_ID = @query_id
       ),2) AS Pct_R,
    COUNT(*) AS Calls,
    SUM(DURATION) / COUNT(*) AS "R/Call"
FROM INFORMATION_SCHEMA.PROFILING
WHERE QUERY_ID = @query_id
GROUP BY STATE
ORDER BY Total_R DESC;
SHOW STATUS

如果執行SHOW GLOBAL STATUS,則可以檢視伺服器級別的從伺服器啟動時開始計算的查詢次數統計。

SHOW STATUS的大部分結果都只是一個計數器,可以顯示某些活動如讀索引的頻繁程度,但無法給出消耗了多少時間。

示例如下:

FLUSH STATUS;
SELECT * FROM mytable;
SHOW STATUS WHERE Variable_name LIKE 'Handler%' OR Variable_name LIKE 'Created%';

EXPLAIN是通過估計得到的結果,而通過計數器則是實際的測量結果。

3.4 診斷間歇性問題

間歇性問題比如系統偶爾停頓或者慢查詢,很難診斷。

3.4.1 單條查詢問題還是伺服器問題

使用SHOW GLOBAL STATUS

這個方法實際就是以較高的頻率比如每秒一次SHOW GLOBAL STATUS命令捕獲資料,問題出現時,可以通過某些計數器(比如Threads_running、Threads_connected、Questions和Queries)的“尖刺”或“凹陷”來發現。

使用SHOW PROCESSLIST

這個方法是通過不停地捕獲SHOW PROCESSLIST的輸出,來觀察是否有大量執行緒處於不正常的狀態或者有其他不正常的特徵。

這裡只做簡單的記錄,真正需要的時候,可以再到書裡檢視一下相關指令碼的策略等。

第4章 Schema 與資料型別優化

4.1 選擇優化的資料型別

更小的通常更好

一般情況下,應該儘量使用可以正確儲存資料的最小資料型別。更小的資料型別通常更快,因為它們佔用更少的磁碟、記憶體和CPU快取,並且處理時需要的CPU週期也更少。

簡單就好

簡單資料型別的操作通常需要更少的CPU週期

儘量避免NULL

通常情況下最好指定列為NOT NULL,除非真的需要儲存NULL值

4.1.1 整數型別

有兩種型別的數字:整數和實數。如果儲存整數,可以使用這幾種整數型別:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT。分別使用8,16,24,32,64位儲存空間。整數型別有可選的UNSIGNED屬性,表示不允許負值,這大致可以使正數的上限提高一倍。
你的選擇決定MySQL是怎麼在記憶體和磁碟中儲存資料的。然而,整數計算一般使用64位的BIGINT整數,即使在32位環境也是如此。
MySQL可以為整數型別指定寬度,例如INT(11),對大多數應用這是沒有意義的:它不會限制值的合法範圍,只是規定了MySQL的一些互動工具用來顯示字元的個數。對於儲存和計算來說,INT(1)和INT(20)是相同的。

4.1.2 實數型別

實數是帶有小數部分的數字。FLOAT和DOUBLE型別支援使用標準的浮點運算進行近似計算。DECIMAL型別用於儲存精確的小數。
浮點和DECIMAL型別都可以指定精度。對於DECIMAL列,可以指定小數點前後所允許的最大位數。
因為需要額外的空間和計算開銷,所以應該儘量只在對小數進行精確計算時才使用DECIMAL——例如儲存財務資料。但在資料量比較大的時候,可以考慮使用BIGINT代替DECIMAL,將需要儲存的貨幣單位根據小數的位數乘以相應的倍數即可。

4.1.3 字串型別

VARCHAR和CHAR是兩種最主要的字串型別,下面的描述假設使用的儲存引擎是InnoDB或MyISAM。

VARCHAR

VARCHAR型別用於儲存可變長字串,是最常見的字串資料型別。它比定長型別更節省空間,因為它僅使用必要的空間(例如越短的字串使用空間越少)。但是VARCHAR需要使用1或2個額外位元組記錄字串的長度。
VARCHAR節省了儲存空間,所以對效能也有幫助。但是由於行是變長的,在UPDATE時可能使行變得比原來更長,這就導致需要做額外的工作。
下面這些情況使用VARCHAR是合適的:字串列的最大長度比平均長度大很多;列的更新很少,所以碎片不是問題;使用了像UTF-8這樣複雜的字符集,每個字元都使用不同的位元組數進行儲存。

CHAR

CHAR型別是定長的:MySQL總是根據定義的字串長度分配足夠的空間。CHAR適合儲存很短的字串,或者所有值都接近同一個長度。對於經常變更的資料,CHAR也比VARCHAR更好,因為定長的CHAR型別不容易產生碎片。對於非常短的列,CHAR比VARCHAR在儲存空間上也更有效率(不需要額外記錄字串長度)。

使用VARCHAR(5)和VARCHAR(200)儲存’hello’的空間開銷是一樣的,但是更長的列會消耗更多的記憶體,因為MySQL通常會分配固定大小的記憶體塊來儲存內部值。尤其是使用記憶體臨時表進行排序或操作時會特別糟糕。在利用磁碟臨時表進行排序時也同樣糟糕。

BINARY和VARBINARY

與CHAR和VARCHAR類似的型別還有BINARY和VARBINARY,它們儲存的是二進位制字串,儲存的是位元組碼而不是字元。二進位制比較的優勢並不僅僅體現在大小寫敏感上,MySQL比較BINARY字串時,每次按一個位元組,並且根據該位元組的數值進行比較。因此,二進位制比較比字元比較簡單很多,所以也就更快。

BLOB和TEXT型別

BLOG和TEXT都是為儲存很大的資料而設計的字串資料型別,分別採用二進位制和字元方式儲存。實際上,它們分別屬於兩組不同的資料型別家族:字元型別是TINYTEXT,SMALLTEXT,TEXT,MEDIUMTEXT,LONGTEXT;對應的二進位制型別是TINYBLOB,SMALLBLOB,BLOB,MEDIUMBLOB,LONGBLOB。BLOB是SMALLBLOB的同義詞,TEXT是SMALLTEXT的同義詞。與其他型別不同,MysQL把每個BLOB和TEXT值當做一個獨立的物件處理。

使用列舉(ENUM)代替字串型別

有時候可以使用列舉列代替常用的字串型別。MySQL在內部會將每個值再列表中的位置儲存為整數,並且在表的.frm檔案中儲存“數字-字串”對映關係的“查詢表”。列舉欄位是按照內部儲存的整數而不是定義的字串進行排序的。列舉最不好的地方是,字串列表是固定的,新增或刪除字串必須使用ALTER TABLE。

4.1.4 日期和時間型別

MySQL可以使用許多型別來儲存日期和時間值,例如YEAR和DATE。MySQL能儲存的最小時間粒度為秒。

DATETIME

這個型別能儲存大範圍的值,從1001年到9999年,精度為秒,它使用8個位元組的儲存空間。

TIMESTAMP

TIMESTAMP型別儲存了從1970年1月1日午夜(格林尼治標準時間)以來的秒數,它和UNIX時間戳相同。TIMESTAMP只使用4個位元組的儲存空間,因此它的範圍比DATETIME小得多:只能表示從1970年到2038年。

如果需要儲存比秒更小粒度的日期和時間值怎麼辦?MySQL目前沒有提供合適的資料型別,但是可以使用自己的儲存格式:可以使用BIGINT型別儲存微妙級別的時間戳,或使用DOUBLE儲存秒之後的小數部分。

4.1.5 位資料型別

BIT

可以使用BIT列在一列中儲存一個或多個true/false值。MySQL把BIT當做字串處理,而不是數字型別。然而,在數字上下文的場景中檢索時,結果將是位字串轉換成的數字。比如一個值b’00111001’在數字上下文得到的是二進位制值的57,而在直接檢索時得到的是ASCII碼為57的字元“9”。令人費解,所以,對於大部分應用,最好避免使用這種型別。

SET

如果需要儲存很多true/false值,可以考慮合併這些列到一個SET資料型別,它在MySQL內部是以一系列打包的位的集合來表示的。

在整數列上進行按位操作

一種替代SET的方式是使用一個整數包裝一系列的位。一個包裝位的應用的例子是儲存許可權的訪問控制列表(ACL)。

4.1.6 選擇識別符號

選擇標識列的型別時,不僅僅需要考慮儲存型別,還需要考慮MySQL對這種型別怎麼執行計算和比較。一旦選定了一種型別,要確保在所有關聯表中都使用同樣的型別。型別之間需要精確匹配,包括像UNSIGNED這樣的屬性。混用不同資料型別可能導致效能問題,即使沒有效能影響,在比較操作時隱式型別轉換也可能導致很難發現的錯誤。在可以滿足值的範圍的需求,並且預留未來增長空間的前提下,應該選擇最小的資料型別。

整數型別

整數通常是標識列最好的選擇,因為它們很快並且可以使用AUTO_INCREMENT

ENUM和SET型別

對於標識列來說,ENUM和SET型別通常是一個糟糕的選擇。ENUM和SET列適合儲存固定資訊,例如有序的狀態、產品型別、人的性別。

字串型別

如果可能,應該避免使用字串型別作為標識列,因為它們很消耗空間,並且通常比數字型別慢。

4.1.7 特殊型別資料

某些型別的資料並不直接與內建型別一樣。低於秒級精度的時間戳就是一個例子。另一個例子是IPv4地址,應該使用無符號整數儲存IP地址。使用INET_ATON()和INET_NTOA()函式在字串和數字兩種表示方法之間轉換

4.2 MySQL schema設計中的陷阱

太多的列

MySQL的儲存引擎API工作時需要在伺服器層和儲存引擎層之間通過行緩衝格式拷貝資料,然後在伺服器層將緩衝內容解碼成各個列。從行緩衝中將編碼過的列轉換成行資料結構的操作代價是非常高的。

太多的關聯

一個粗略的經驗法則,如果希望查詢執行得快且併發性好,單個查詢最好在12個表以內做關聯。

列舉

注意防止過度使用列舉

變相的列舉

列舉列允許在列中儲存一組定義值中的單個值,集合(SET)列則允許在列中儲存一組定義值中的一個或多個值,有時候可能容易導致混亂。

NULL值

儘量避免使用null,如果需要存空值時,可以使用某個特殊值代替。但是在確實需要時也不要害怕使用null。

4.3 正規化和反正規化

4.3.1 正規化的優缺點

優點:

正規化化的更新操作通常比反正規化化要快。
當資料較好地正規化化時,就只有很少或者沒有重複資料,所以只需要修改更少的資料。
正規化化的表通常更小,可以更好地放在記憶體裡,所以執行操作會更快。
很少有多餘的資料意味著檢索列表資料時更少需要DISTINCT或者GROUP BY語句。

缺點:正規化化設計的schema的缺點是通常需要關聯,這不但代價昂貴,也可能使一些索引策略無效。例如,正規化化可能將列存放在不同的表中,而這些列如果在一個表中本可以屬於同一個索引。

4.3.2 反正規化的優缺點

反正規化化的schema因為所有資料都在一張表中,可以很好地避免關聯。如果不需要關聯表,則對大部分查詢最差的情況——即使表沒有使用索引——是全表掃描。單獨的表也能使用更有效的索引策略。

4.3.3 混用正規化化和反正規化化

在實際應用中經常需要混用

4.4 快取表和彙總表

我們用術語“快取表”來表示儲存那些可以比較簡單地從schema其他表獲取(但是每次獲取的速度比較慢)資料的表(例如,邏輯上冗餘的資料)。而術語“彙總表”(或“累積表”)時,則儲存的是使用GROUP BY語句聚合資料的表(例如,資料不是邏輯上冗餘的)。

4.4.1 物化檢視

物化檢視實際上是預先計算並且儲存在磁碟上的表,可以通過各種各樣的策略重新整理和更新。

4.4.2 計數器表

可以用這種表快取一個使用者的朋友數、檔案下載次數等。建立一張獨立的表儲存計數器通常是個好主意,這樣可使計數器表小且快。要獲得更高的併發效能,也可以將計數器儲存在多行中,每次隨機選擇一行進行更新,查詢時聚合所有行。

4.5 加快ALTER TABLE操作的速度

MySQL的ALTER TABLE操作的效能對大表來說是個大問題。MySQL執行大部分修改表結構操作的方法是用新的結構建立一個空表,從舊錶中查出所有資料插入新表,然後刪除舊錶。一般而言,大部分ALTER TABLE操作將導致MySQL服務中斷。
常見場景中能使用的技巧只有兩種:一種是先在一臺不提供服務的機器上執行ALTER TABLE操作,然後和提供服務的主庫進行切換;另外一種技巧是“影子拷貝”,即用要求的表結構建立一張和源表無關的新表,然後通過重新命名和刪表操作交換兩張表。
但也不是所有ALTER TABLE操作都會引起表重建,例如下面例子:

ALTER TABLE mytable MODIFY COLUMN mycolumn TINYINT(3);  ## 慢,需要重建表
ALTER TABLE mytable ALTER COLUMN mycolumn TINYINT(3);   ## 快,不需要重建表,而是直接修改了.frm檔案

4.5.1 只修改.frm檔案

下面這些操作是有可能不需要重建表的:

移除(不是增加)一個列的AUTO_INCREMENT屬性
增加、移除、或更改ENUM和SET常量。如果移除的是已經有行資料用到其值的常量,查詢將返回空串。

基本的技術是為想要的表結構建立一個新的.frm檔案,然後用它替換舊的,像下面這樣:

1、建立一張有相同結構的空表,並進行所需要的修改
2、執行FLUSH TABLES WITH READ LOCK。這將會關閉所有正在使用的表,並且禁止任何表被開啟
3、交換.frm檔案
4、執行UNLOCK TABLES來釋放第2步的讀鎖

4.5.2 快速建立MyISAM索引

為了高效載入資料到MyISAM表,有個常用的技巧是先禁用索引、載入資料,再重新啟用索引:

ALTER TABLE mytable DISABLE KEYS;
-- load the data
ALTER TABLE mytable ENABLE KEYS;

這個辦法對唯一索引無效,因為DISABLE KEYS只對非唯一索引有效。MyISAM會在記憶體中構造唯一索引,並且為載入的每一行檢查唯一性。

現代版本的InnoDB中有個類似的技巧:先刪除所有的非唯一索引,然後增加新的列,最後重新建立刪除掉的索引。

用上一節修改.frm檔案的方法也可以:

1、用需要的表結構建立一張表,但是不包括索引
2、載入資料到表中以構建.MYD檔案
3、按照需要的結構建立另外一張空表,這次要包含索引。這會建立需要的.frm和.MYI檔案
4、獲取讀鎖並重新整理表
5、重新命名第二張表的.frm和.MYI檔案,讓MySQL認為是第一張表的檔案
6、釋放讀鎖
7、使用REPAIR TABLE來重建表的索引。該操作會通過排序來構建所有索引,包括唯一索引

第5章 建立高效能的索引

5.1 索引基礎

在MySQL中,儲存引擎在查詢時首先在索引中找到對應值,然後根據匹配的索引記錄找到對應的資料行。

5.1.1 索引的型別

在MySQL中,索引是在儲存引擎層而不是伺服器層實現的。

B-Tree索引

談論索引時,如果沒有特別指明型別,那多半說的是B-Tree索引,它使用B-Tree資料結構來儲存資料(InnoDB也是使用這個)。B-Tree通常意味著所有的值都是按順序儲存的,並且每一個葉子頁到根的距離相同。B-Tree對索引列是順序組織儲存的,所以很適合查詢範圍資料。

InnoDB索引的工作方式:

image

假如有表People,建立了一個索引key(last_name,first_name,dob),則索引的組織方式如下:

image

注意,索引對多個值進行排序的依據是CREATE TABLE語句中定義索引時列的順序。

使用B-Tree索引的查詢型別
全值匹配

全值匹配指的是和索引中的所有列進行匹配

匹配最左字首

前面提到的索引可用於查詢所有姓為Allen的人,即只使用索引的第一列

匹配列字首

也可以只匹配某一列的值的開頭部分。例如前面索引可用於查詢所有以J開頭的姓的人。這裡也只使用了索引的第一列。

匹配範圍值

例如前面索引可用於查詢姓在Allen和Barry之間的人。這裡也只使用了索引的第一列

精確匹配某一列並範圍匹配另外一列

例如前面索引可用於查詢姓為Allen,並且名字是以字母K開頭的人

只訪問索引的查詢

B-Tree通常可以支援“只訪問索引的查詢”,即查詢只需要訪問索引,而無須訪問資料行

因為索引樹種的節點是有序的,所以除了按值查詢以外,索引還可以用於查詢中的ORDER BY操作(按順序查詢

下面是一些關於B-Tree索引的限制:

如果不是按照索引的最左列開始查詢,則無法使用索引
查詢時不能跳過索引中的列,即不能只使用索引中的第一列和第三列
如果查詢中有某個列的範圍查詢,則其右邊所有列都無法使用索引優化查詢
雜湊索引

雜湊索引基於雜湊表實現,只有精確匹配索引所有列的查詢才有效。對於每一行資料,儲存引擎都會對所有的索引列計算一個雜湊碼。如果多個列的雜湊值相同,索引會以連結串列的方式存放多個記錄指標到同一個雜湊條目中。在MySQL中,只有Memory引擎顯式支援雜湊索引。

雜湊索引的限制:

雜湊索引只包含雜湊值和行指標,而不儲存欄位值,不能使用索引中的值避免讀取行
雜湊索引資料並不是按照索引值順序儲存的,所以也就無法用於排序
雜湊索引也不支援部分索引列匹配查詢,因為雜湊索引始終是使用索引列的全部內容來計算雜湊值的
雜湊索引只支援等值比較查詢,包括=、IN()、<=>。不支援任何範圍查詢,例如WHERE price > 100
訪問雜湊索引的資料非常快,除非有很多雜湊衝突,這個時候會遍歷連結串列
如果雜湊衝突很多的話,一些索引維護操作的代價也會很高。例如當刪除某一【索引的雜湊衝突很多的】行時,需要遍歷該連結串列來刪除相應資料

InnoDB引擎有一個特殊的功能叫做“自適應雜湊索引”。當InnoDB注意到某些索引值被使用得非常頻繁時,會在記憶體中基於B-Tree索引之上再建立一個雜湊索引。

建立自定義雜湊索引:如果儲存引擎不支援雜湊索引,則可以模擬像InnoDB一樣建立雜湊索引,思路為在B-Tree基礎上建立一個偽雜湊索引的列,查詢時帶上這個列的hash值作為條件

空間資料索引(R-Tree)

MyISAM表支援空間索引,可以用作地理資料儲存

全文索引

全文索引是一種特殊型別的索引,它查詢的是文字中的關鍵詞,而不是直接比較索引中的值。全文索引適用於MATCH AGAINST操作,而不是普通的WHERE條件操作

5.2 索引的優點

索引可以讓伺服器快速地定位到表的指定位置。

最常見的B-Tree索引,按照順序儲存資料,所以可以用來做ORDER BY和GROUP BY操作。因為索引中儲存了實際的列值,所以某些查詢只使用索引就能夠完成全部查詢。

總結下來索引有如下三個優點:

1、索引大大減少了伺服器需要掃描的資料量
2、索引可以幫助伺服器避免排序和臨時表
3、索引可以將隨機I/O變為順序I/O

對於非常小的表,大部分情況下簡單的全表掃描更高效。對於中到大型的表,索引就非常有效。但對於特大型的表,建立和使用索引的代價將隨之增長。如果表的數量特別多,可以建立一個元資料資訊表。

5.3 高效能的索引策略

5.3.1 獨立的列

“獨立的列”是指索引列不能是表示式的一部分,也不能是函式的引數。例如下面這個查詢無法使用actor_id列的索引:

SELECT actor_id FROM mytable WHERE actor_id +1 = 5;

我們應該養成簡化WHERE條件的習慣,始終將索引列單獨放在比較符號的一側。

5.3.2 字首索引和索引選擇性

有時候需要索引很長的字串,這會讓索引變得大且慢。通常可以索引開始的部分字元,這樣可以大大節約索引空間,從而提高索引效率。但這樣也會降低索引的選擇性。索引的選擇性是指,不重複的索引值(也稱為基數)和資料表的記錄總數(#T)的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高。唯一索引的選擇性是1,這是最好的索引選擇性,效能也是最好的。
為了決定字首的合適長度,需要找到最常見的值出現的次數,然後和最常見的字首列表進行比較,如果次數接近了,則選擇性會比較高。計算合適的字首長度的另一個辦法就是計算完整列的選擇性,並使字首的選擇性接近於完整列的選擇性。
下面語句演示如何建立字首索引:

ALTER TABLE mytable ADD KEY (city(7));

有時候字尾索引也有用途,MySQL原生不支援,但是可以把字串反轉後儲存,並基於此建立字首索引。

5.3.3 多列索引

在多個列上建立獨立的單獨索引大部分情況下並不能提高MySQL的查詢效能。MySQL.0和更新版本引入了一種叫“索引合併”的策略,一定程度上可以使用表上的多個單列索引來定位指定的行。

5.3.4 選擇合適的索引列順序

正確的順序依賴於使用該索引的查詢,並且同時需要考慮如何更好地滿足排序和分組的需要。當不需要考慮排序和分組時,將選擇性最高的列放在前面通常是很好的。

5.3.5 聚簇索引

聚簇索引並不是一種單獨的索引型別,而是一種資料儲存方式。InnoDB的聚簇索引實際上在同一個結構中儲存了B-Tree索引和資料行。當表有聚簇索引時,它的資料行實際上存放在索引的葉子頁中。因為無法同時把資料行存放在兩個不同的地方,所以一個表只能有一個聚簇索引。
InnoDB通過主鍵聚集資料。如果沒有定義主鍵,InnoDB會選擇一個唯一的非空索引代替。如果沒有這樣的索引,會隱式定義一個主鍵來作為聚簇索引。

聚簇索引優點:

可以把相關資料儲存在一起
資料訪問更快
使用覆蓋索引掃描的查詢可以直接使用頁節點中的主鍵值

聚簇索引的缺點:

聚簇資料最大限度地提高了I/O密集型應用的效能,但如果全部資料在記憶體中,訪問順序就不那麼重要了,聚簇索引也就沒什麼優勢了
插入速度嚴重依賴於插入順序
更新聚簇索引列的代價很高,因為會強制InnoDB將每個被更新的行移動到新的位置
基於聚簇索引的表在插入新行,或者主鍵被更新需要移動的時候,可能面臨“頁分裂”的問題
聚簇索引可能導致全表掃描變慢,尤其是行比較稀疏,或者由於頁分裂導致資料儲存不連貫的時候
二級索引可能比想象中更大,因為在二級索引的葉子節點包含了引用行的主鍵列
二級索引訪問需要兩次索引查詢,而不是一次

下面是聚簇和非聚簇表儲存方式對比:

image

5.3.6 覆蓋索引

如果一個索引包含(或者說覆蓋)所有需要查詢的欄位的值,我們就稱之為“覆蓋索引”。
不是所有型別的索引都可以成為覆蓋索引。覆蓋索引必須要儲存索引列的值,而雜湊索引、空間索引和全文索引等都不儲存索引列的值,所以MySQL只能使用B-Tree索引做覆蓋索引。

5.3.7 使用索引掃描來做排序

如果索引不能覆蓋查所需的全部列,就不得不每掃描一條索引記錄就都回表查詢一次對應的行。這基本都是隨機I/O,因此按索引順序讀取資料的速度通常要比順序地全表掃描慢。
MySQL可以使用同一個索引既滿足排序,又用於查詢行。因此,如果可能,設計索引時應該儘可能地同時滿足這兩種任務。
只有當索引的列順序和ORDER BY子句的順序完全一致,並且所有列的排序方向都一樣時,MySQL才能夠使用索引來對結果做排序。如果需要關聯多張表,則只有當ORDER BY子句引用的欄位全部為第一個表時才能使用索引做排序。

5.3.8 壓縮(字首壓縮)索引

MyISAM使用字首壓縮來減小索引的大小,從而讓更多的索引可以放入記憶體,預設只壓縮字串。壓縮方式是,先完全儲存索引塊中的第一個值,然後將其他值和第一個值進行比較得到相同字首的位元組數和剩餘的不同字尾部分,把這部分儲存起來即可。因為每個值的壓縮字首都依賴前面的值,所以MyISAM查詢時無法使用二分查詢而只能從頭開始掃描。如果是倒敘掃描效率更低。

5.3.9 冗餘和重複索引

重複索引是指在相同的列上按照相同的順序建立的相同型別的索引。冗餘索引和重複索引有一些不同,如果建立了索引(A,B),再建立索引(A)就是冗餘索引,因為這只是前一個索引的字首索引。
重複索引要刪除,冗餘索引看情況,一般也要刪除,除非是出於效能方面的考慮(比如如果擴充套件已有的索引會導致其變得太大)

5.3.10 未使用的索引

這種索引建議刪除

5.3.11 索引和鎖

索引可以讓查詢鎖定更少的行。

5.4 索引案例學習

5.4.1 支援多種過濾條件

在某個例子中,將(sec,country)列作為索引。sex的選擇性很低,但是隻有兩個值,如果某個查詢不限制性別,可以通過在查詢條件中新增AND SEX IN(‘M’,’F’)來讓MySQL使用該索引。

儘可能將需要做範圍查詢的列放到索引的後面,以便優化器能使用盡可能多的索引列。

5.4.2 避免多個範圍條件

如果有多個範圍查詢,我們可以將其中的一個範圍查詢轉換為一個簡單的等值比較。

5.4.3 優化排序

下面這個分頁查詢會很慢:

SELECT cols FROM mytable WHERE sex='m' ORDER BY rating LIMIT 100000, 10;

優化這類索引的一個比較好的策略是使用延遲關聯,通過使用覆蓋索引查詢返回需要的主鍵,再跟進這些主鍵關聯原表獲得需要的行,這樣可以減少MySQL掃描那些需要丟棄的行數,示例如下:

SELECT cols FROM mytable INNER JOIN(
    SELECT <primary key cols> FROM mytable
    WHERE x.sex='m' ORDER BY rating LIMIT 100000, 10
) AS x USING(<primary key cols>);

5.5 維護索引和表

5.5.1 找到並修復損壞的表

CHECK TABLE 檢查是否發生了表損壞
REPAIR TABLE 命令來修復損壞的表
ALTER TABLE innodb_tb1 ENGINE=INNODB; 可以用來重建表

5.5.2 更新索引統計資訊

ANALYZE TABLE 重新生成統計資訊
使用SHOW TABLE STATUS和SHOW INDEX都會觸發索引統計資訊的更新

5.5.3 減少索引和資料的碎片

OPTIMIZE TABLE或者匯出再匯入的方式可以重新整理資料
ALTER TABLE innodb_tb1 ENGINE=INNODB; 可以用來重建表

第6章 查詢效能優化

6.2 慢查詢基礎:優化資料訪問

6.2.1 是否向資料庫請求了不需要的資料

例如不使用limit,多表關聯時返回全部列,總是使用SELECT *,重複查詢相同的資料(其實可以取一次後放在快取中)等

6.2.2 MySQL是否在掃描額外的記錄

可以從響應時間、掃描的行數、返回的行數評估一下

6.3 重構查詢的方式

6.3.1 一個複雜查詢還是多個簡單查詢

設計查詢的時候一個需要考慮的重要問題是,是否需要將一個複雜的查詢分成多個簡單的查詢。MySQL從設計上讓連線和斷開連線都很輕量級,在返回一個小的查詢結果方面很高效。

6.3.2 切分查詢

將大查詢切分成小查詢,每個查詢功能一樣,只返回一部分查詢結果

6.3.3 分解關聯查詢

很多高效能的應用都會對關聯查詢進行分解。簡單的,可以對每一個表進行一次單表查詢,然後將結果在應用程式中進行關聯。

6.4 查詢執行的基礎

MySQl執行一個查詢時的過程如下:

image

6.4.1 MySQL客戶端/伺服器通訊協議

實際上是MySQL向客戶端推送資料。

6.4.2 查詢快取

查詢快取是通過一個隊大小寫敏感的雜湊查詢實現的。如果明知了查詢快取,還會檢查使用者許可權。

6.4.3 查詢優化處理

語法解析器和預處理

語法解析器會將sql語句進行解析,生成一顆對應的“解析樹”。它會驗證是否使用錯誤的關鍵字,關鍵字順序是否正確等。
前處理器則根據一些MySQL規則進一步檢查解析樹是否合法。例如,資料表和資料列是否存在,是否有許可權等。

查詢優化器

MySQL使用基於成本的優化器,它將嘗試預測一個查詢使用某種執行計劃的成本,並選擇其中成本最小的一個。它不考慮併發查詢的成本,也不考慮使用者自定義函式的成本。
優化策略簡單的分為兩種:靜態優化,它直接對解析樹進行分析並完成優化,例如通過一些簡單的代數變換將WHERE條件轉換成另一種等價形式;動態優化則和查詢的上下文等因素有關,在每次查詢的時候都重新評估。

下面是一些mysql可處理的優化型別:

重新定義關聯表的順序
將外連線轉化成內連線
使用等價變換規則
優化COUNT()、MIN()、MAX()等
預估並轉化為常數表示式
覆蓋索引掃描
子查詢優化(轉化)
提前終止查詢(例如LIMIT)
等值傳播
列表IN()的比較:MySQL先將IN()列表中的資料進行排序,然後通過二分查詢的方式確定是否滿足條件,而不是像其他資料庫將其轉化成多個OR
資料和索引的統計資訊

統計資訊由儲存引擎實現

MySQL如何執行關聯查詢

通過巢狀迴圈的方式實現,即“巢狀迴圈關聯”

執行計劃

MySQL並不會生成位元組碼來執行查詢,而是生成一個指令樹,然後通過儲存引擎執行完成這顆指令樹並返回結果,如圖:

image

關聯查詢優化器

MySQL優化器最重要的一部分就是關聯查詢優化,它決定了多個表關聯時的順序。關聯優化器通過評估不同順序時的成本來選擇一個代價最小的關聯順序。

排序優化

不能使用索引排序的時候,MySQL需要自己排序。如果資料量小則在記憶體中排序,如果資料量大則使用磁碟。這個過程統一稱為檔案排序(filesort)

6.4.4 查詢執行引擎

查詢執行引擎根據執行計劃來完成整個查詢

6.4.5 返回結果給客戶端

MySQL將結果集返回客戶端是一個增量、逐步返回的過程。只要開始產生第一條結果,就可以向客戶端逐步返回結果集了。

6.5 MySQL查詢優化器的侷限性

6.5.1關聯子查詢

MySQL的子查詢實現得非常糟糕。最糟糕的一類查詢是WHERE條件中包含IN()的子查詢語句。

一旦使用了DISTINCT和GROUP BY,那麼在查詢的執行過程中,通常要產生臨時中間表。

6.5.2 UNION的限制

如果希望UNION的子句都能夠根據LIMIT只取部分結果集,或者希望能夠先排好序再合併結果集的話,就需要在UNION的各個子句中分別使用這些句子。

6.5.3 索引合併優化

當WHERE子句中包含多個複雜條件的時候,MySQL能夠訪問單個表的多個索引以合併和交叉過濾的方式來定位需要查詢的行。

6.5.4 等值傳遞

等值傳遞有時候會帶來意想不到的額外開銷。例如,有一個非常大的IN()列表,而MySQL優化器發現存在WHERE、ON或者USING的子句,將這個列表的值和另一個表的某個列相關聯,那麼優化器會將IN()列表都複製應用到關聯的各個表中,如果這個IN()列表非常大,則會導致優化和執行都會變慢。

6.5.5 並行執行

MySQL無法利用多核特性來並行執行查詢。

6.5.6 雜湊關聯

在本書寫作的時候,MySQL並不支援雜湊關聯——MySQL的所有關聯都是巢狀迴圈關聯。

6.5.7 鬆散索引掃描

由於歷史原因,MySQL並不支援鬆散索引掃描,也就無法按照不連續的方式掃描一個索引,在MySQL 5.6之後的版本,關於鬆散索引掃描的一些限制將會通過“索引條件下推”的方式解決。

MySQL全表掃描(SELECT … FORM table WHERE b BETWEEN 2 AND 3):

image

鬆散索引掃描(SELECT … FORM table WHERE b BETWEEN 2 AND 3):

image

6.5.8 最大值和最小值優化

對於MIN()和MAX()查詢,MySQL的優化做得並不好。

6.5.9 在同一個表上查詢和更新

MySQL不允許對同一張表同時進行查詢和更新。

6.6 查詢優化器的提示(hint)

如果對優化器選擇的執行計劃不滿意,可以使用優化器提供的幾個提示(hint)來控制最終的執行計劃。建議直接閱讀MySQL官方手冊,有些提示和版本有直接關係。

HIGH_PRIORITY和LOW_PRIORITY

HIGH_PRIORITY用於SELECT語句,會將此SELECT語句重新排程到所有正在等待表鎖以便修改資料的語句之前。還可以用於INSERT語句,其效果只是簡單地抵消了全域性LOW_PRIORITY設定對該語句的影響。

LOW_PRIORITY則正好相反:它會讓該語句一直處於等待狀態,只要佇列中還有需要訪問同一個表的語句——即使是那些比該語句還晚提交到伺服器的語句。LOW_PRIORITY在SELECT、INSERT、UPDATE和DELETE中都可以使用。

千萬不要在InnoDB或者其他有細粒度鎖機制和併發控制的引擎中使用。

DELAYED

這個提示對INSERT和REPLACE有效。MySQL會將使用該提示的語句立即返回給客戶端,並將插入的行資料放入到緩衝區,然後在表空閒時批量將資料寫入。

STRAIGHT_JOIN

這個提示可以放在SELECT語句的SELECT關鍵詞之後,也可以放置在任何兩個關聯表的名字之間。第一個用法是讓查詢中所有的表按照在語句中出現的順序進行關聯。第二個用法則是固定其前後兩個表的關聯順序。

SQL_SMALL_RESULT和SQL_BIG_RESULT

這兩個提示只對SELECT語句有效。他們告訴優化器對GROUP BY或者DISTINCT查詢如何使用臨時表及排序。SQL_SMALL_RESULT告訴優化器結果集會很小,可以將結果集放在記憶體中的索引臨時表,以避免排序操作。如果是SQL_BIG_RESULT,則告訴優化器結果集可能會非常大,建議使用磁碟臨時表做排序。

SQL_BUFFER_RESULT

這個提示告訴優化器將查詢結果放入到一個臨時表,然後儘可能快地釋放表鎖

SQL_CACHE和SQL_NO_CACHE

這個提示告訴MySQL這個結果集是否應該快取在查詢快取中

SQL_CALC_FOUND_ROWS

嚴格來說這個不是優化器提示。查詢中加上該提示MySQL會計算除去LIMIT子句後這個查詢要返回的結果集的總數

FOR UPDATE和LOCK IN SHARE MODE

這也不是真正的優化器提示。這兩個提示主要控制SELECT語句的鎖機制,但只對實現了行級鎖的儲存引擎有效。使用該提示會對符合查詢條件的資料行加鎖。

USE INDEX、IGNORE INDEX和FORCE INDEX

這幾個提示會告訴優化器使用或者不使用哪些索引來查詢記錄

optimizer_search_depth

這個引數控制優化器在窮舉執行計劃時的限度

optimizer_prune_level

該引數預設開啟,這讓優化器會根據需要掃描的行數來決定是否跳過某些執行計劃

optimizer_switch

這個變數包含了一些開啟/關閉優化器特性的標誌位

6.7 優化特定型別的查詢

6.7.1 優化COUNT()查詢

COUNT()是一個特殊的函式,有兩種非常不同的作用:它可以統計某個列值的數量,也可以統計行數。在統計列值時要求列值是非空的(不統計NULL)。如果在COUNT()的括號中指定了列或者列的表示式,則統計的就是這個表示式有值的結果數

COUNT()的另一個作用是統計結果集的行數

6.7.2 優化關聯查詢

確保ON或者USING子句中的列上有索引。確保任何的GROUP BY和ORDER BY中的表示式只涉及到一個表中的列,這樣MySQL才有可能使用索引來優化這個過程。當升級MySQL的時候需要注意:關聯語法、運算子優先順序等其他可能會發生變化的地方。

6.7.3 優化子查詢

儘可能使用關聯查詢代替

6.7.4 優化GROUP BY和DISTINCT

MySQL都使用同樣的辦法優化這兩種查詢,事實上,MySQL優化器會在內部處理的時候相互轉化這兩類查詢。它們都可以使用索引來優化,這也是最有效的優化辦法。在MySQL中,當無法使用索引的時候,GROUP BY使用兩種策略來完成:使用臨時表或者檔案排序來做分組。

6.7.5 優化LIMIT分頁

進行分頁操作的時候,我們通常會使用LIMIT加偏移量(OFFSET)的辦法實現,同時加上合適的ORDER BY子句。但是在偏移量非常大的時候,例如LIMIT 1000, 20這樣的查詢,這時MySQL需要查詢10020條記錄然後只返回最後20條。可以將LIMIT查詢轉換為已知位置的查詢,用BETWEEN 10000 AND 10020。或者可以使用一個書籤記錄上次取資料的位置,下一次從該位置開始掃描。

6.7.6 優化SQL_CALC_FOUND_ROWS

加上這個提示後,MySQL會掃描所有滿足條件的行,代價可能非常高,最好不使用

6.7.7 優化UNION查詢

MySQL總是通過建立並填充臨時表的方式來執行UNION查詢。因此很多優化策略在UNION查詢中都沒法很好的使用。經常需要手工地將WHERE、LIMIT、ORDER BY等子句“下推”到UNION的各個子查詢中,以便優化器可以充分利用這些條件進行優化(例如,直接將這些子句冗餘地寫一份到各個子查詢中)

6.7.8 靜態查詢分析

可以使用工具解析查詢日誌、分析查詢模式,給出潛在問題

6.7.9 使用者自定義變數

使用者自定義變數的限制:

使用自定義變數的查詢,無法使用查詢快取
不能在使用常量或識別符號的地方使用自定義變數,例如表名、列名和LIMIT子句中
使用者自定義變數的生命週期是在一個連線中有效,所以不能用它們來做連線間的通訊
如果使用連線池或者持久化連線,自定義變數可能讓看起來毫無關係的程式碼發生互動
在5.0之前的版本,是大小寫敏感的,所以在不同版本間要注意相容問題
不能顯示地宣告自定義變數的型別,所以最好初始化時指定一個初識值以確定型別
MySQL優化器在某些場景下可能會將這些變數優化掉,這可能導致程式碼不按預想的方式執行
賦值的順序和賦值的時間點並不總是固定的,這依賴於優化器的決定
賦值符號:=的優先順序非常低,所以需要注意,賦值表示式應該使用明確的括號
使用未定義變數不會產生任何語法錯誤,如果沒有意識到這一點,非常容易犯錯

6.8 案例學習

6.8.3 使用使用者自定義函式

當SQL語句已經無法高效地完成某些任務的時候,可以寫一個使用者自定義函式(UDFs)

第7章 MySQL高階特性

7.1 分割槽表

對使用者來說,分割槽表是一個獨立的邏輯表,但是底層由多個物理子表組成,索引頁是按照分割槽的子表定義的,而沒有全域性索引。MySQL在建立表時使用PARTITION BY子句定義每個分割槽存放的資料。分割槽的一個主要目的是將資料按照一個較粗的粒度分在不同的表中,這樣做可以將相關的資料存放在一起,另外,如果想一次批量刪除整個分割槽的資料也會變得很方便。

分割槽表的一些限制:

一個表最多隻能有1024個分割槽。
在MySQL 5.1中,分割槽表示式必須是整數,或者是返回整數的表示式。在MySQL 5.5中,某些場景中可以直接使用列來進行分割槽。
如果分割槽欄位中有主鍵或者唯一索引的列,那麼所有主鍵列和唯一索引列都必須包含進來。
分割槽表中無法使用外來鍵約束

7.1.1 分割槽表的原理

儲存引擎管理分割槽的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的儲存引擎),分割槽表的索引只是在各個底層表上各自加上一個完全相同的索引。從儲存引擎的角度來看,底層表和一個普通表沒有任何不同。每個操作都會“先開啟並鎖住所有的底層表”,但這並不是說分割槽表會在處理過程中是鎖住全表的。

7.1.2 分割槽表的型別

MySQL支援多種分割槽表。我們看到最多的是根據範圍進行分割槽,其他的分割槽技術還有根據鍵值進行分割槽,使用數學模函式來進行分割槽(以便將資料輪詢放入不同的分割槽)。

7.1.3 如何使用分割槽表

全量掃描資料,不要任何索引:只要能夠使用WHERE條件將需要的資料限制在少數分割槽中。

索引資料,並分離熱點。可以將熱資料放到一個分割槽中,這樣可以有機會將他們都快取到記憶體中個,並使用索引。

7.1.4 什麼情況下會出問題

一些可能遇到的問題:

NULL值會使分割槽過濾無效。(可以人為建立一個“無用”的分割槽)
分割槽列和索引列不匹配,會導致查詢無法進行分割槽過濾。
選擇分割槽的成本可能很高。
開啟並鎖住所有底層表的成本可能很高。
維護分割槽的成本可能很高。

其他的一些限制:

所有分割槽都必須使用相同的儲存引擎
分割槽函式中可以使用的函式和表示式也有一些限制
某些儲存引擎不支援分割槽
對於MyISAM的分割槽表,不能再使用LOAD_INDEX_INTO_CACHE操作
對於MyISAM表,使用分割槽表時需要開啟更多的檔案描述符

7.1.5 查詢優化

分割槽最大的優點就是優化器可以根據分割槽函式來過濾一些分割槽,很重要的一點是要在WHERE條件中帶入分割槽列。即便在建立分割槽時可以使用表示式,但在查詢時卻只能根據列來過濾分割槽。

7.1.5 合併表

合併表是一種早期的、簡單的分割槽實現,和分割槽表相比有一些不同的限制,並缺乏優化。分割槽表嚴格來說是一個邏輯上的概念,使用者無法訪問底層的各個分割槽,對使用者來說分割槽是透明的。但是合併表允許使用者單獨訪問各個子表,合併表是一種即將被淘汰的技術。

7.2 檢視

檢視本身是一個虛擬表,不存放任何資料。訪問檢視的時候,它返回的資料是MySQL從其他表中生成的。檢視和表是在同一個名稱空間,MySQL在很多地方對於檢視和表是同樣對待的。MySQL可以使用兩種辦法來處理檢視:合併演算法和臨時表演算法。檢視的實現演算法是檢視本身的屬性,和作用在檢視上的查詢語句無關。如果可能,會盡可能地使用合併演算法,圖例如下:

image

7.2.1 可更新檢視

可更新檢視是指可以通過更新這個檢視來更新檢視涉及的相關表。如果檢視定義中包含了GROUP BY、UNION、聚合函式,以及其他一些特殊情況,就不能被更新了。所有使用臨時表演算法實現的檢視都無法被更新。

7.2.2 檢視對效能的影響

多數人認為檢視不能提升效能,事實上,MySQL中某些情況下檢視也可以幫助提升效能,例如在重構schema的時候可以使用檢視,使得在修改檢視底層表結構的時候,應用程式碼還可能繼續不報錯的執行。外層查詢的WHERE條件無法“下推”到構建檢視的臨時表的查詢中,臨時表也無法建立索引。

7.2.3 檢視的限制

MySQL還不支援物化檢視(物化檢視是指將檢視結果資料存放在一個可以檢視的表中,並定期從原始表中重新整理資料到這個表中)。

7.3 外來鍵約束

InnoDB是目前MySQL中唯一支援外來鍵的內建儲存引擎。有時可以使用觸發器來代替外來鍵。

7.4 在MySQL內部儲存程式碼

MySQL允許通過觸發器、儲存過程、函式的形式來儲存程式碼。有如下優點:

它在伺服器內部執行,離資料最近,還能節省頻寬和網路延遲。
這是一種程式碼重用,可以方便地統一業務規則,保證某些行為總是一致,所以也可以為應用提供一定的安全性。
它可以簡化程式碼的維護和版本更新。
它可以幫助提升安全,比如提供更細粒度的許可權控制。一個常見的例子是銀行用於轉移資金的儲存過程:這個儲存過程可以在一個事務中完成資金轉移和記錄用於審計的日誌。
伺服器端可以快取儲存過程的執行計劃,這對於需要反覆呼叫的過程,會大大降低消耗。
因為是在伺服器端部署的,所以備份、維護都可以在伺服器端完成。

儲存程式碼也有如下缺點:

較之引用程式的程式碼,儲存程式碼效率差些,難以實現太複雜的邏輯。
儲存程式碼可能會給應用程式程式碼的部署帶來額外的複雜性。
因為儲存程式都部署在伺服器內,所以可能有安全隱患。
儲存過程會給資料庫伺服器增加額外的壓力,而資料庫伺服器的擴充套件性相比應用伺服器要差很多。
儲存過程的一個小錯誤,可以直接拖死伺服