轉(zhuǎn)自:http://blog.csdn.net/lemon89/article/details/50193891
理解磁盤IO
主軸讓磁盤盤片轉(zhuǎn)動,然后傳動手臂可伸展讓讀取頭在盤片上進行讀寫操作。每個盤片有兩面,都可記錄信息,所以一張盤片對應(yīng)著兩個磁頭。
磁盤物理結(jié)構(gòu)如下圖:


扇區(qū):盤片被分為許多扇形的區(qū)域,每個區(qū)域叫一個扇區(qū),硬盤中每個扇區(qū)的大小固定為512字節(jié)
磁道:盤片表面上以盤片中心為圓心,不同半徑的同心圓環(huán)稱為磁道。

磁盤垂直視角
一個I/O請求所花費的時間=尋道時間+旋轉(zhuǎn)延遲+數(shù)據(jù)傳輸時間(約10ms)
當(dāng)需要從磁盤讀取數(shù)據(jù)時,系統(tǒng)會將數(shù)據(jù)邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數(shù)據(jù)在哪個磁道,哪個扇區(qū)。為了讀取這個扇區(qū)的數(shù)據(jù),需要將磁頭放到這個扇區(qū)上方,為了實現(xiàn)這一點,磁頭需要移動對準(zhǔn)相應(yīng)磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然后磁盤旋轉(zhuǎn)將目標(biāo)扇區(qū)旋轉(zhuǎn)到磁頭下,這個過程耗費的時間叫做旋轉(zhuǎn)時間。
尋道時間(Tseek) :
將讀寫磁頭移動至正確的磁道上所需要的時間。尋道時間越短,I/O操作越快,目前磁盤的平均尋道時間一般在3-15ms。
旋轉(zhuǎn)延遲(Trotation)
指盤片旋轉(zhuǎn)將請求數(shù)據(jù)所在的扇區(qū)移動到讀寫磁盤下方所需要的時間。旋轉(zhuǎn)延遲取決于磁盤轉(zhuǎn)速,通常用磁盤旋轉(zhuǎn)一周所需時間的1/2表示。比如:7200rpm的磁盤平均旋轉(zhuǎn)延遲大約為60*1000/7200/2 = 4.17ms,而轉(zhuǎn)速為15000rpm的磁盤其平均旋轉(zhuǎn)延遲為2ms。
數(shù)據(jù)傳輸時間(Transfer)
是指完成傳輸所請求的數(shù)據(jù)所需要的時間,它取決于數(shù)據(jù)傳輸率,其值等于數(shù)據(jù)大小除以數(shù)據(jù)傳輸率。數(shù)據(jù)傳輸時間通常遠(yuǎn)小于前兩部分消耗時間。簡單計算時可忽略。
預(yù)讀
當(dāng)一次IO時,不光把當(dāng)前磁盤地址的數(shù)據(jù),而是把相鄰的數(shù)據(jù)也都讀取到內(nèi)存緩沖區(qū)內(nèi),因為局部預(yù)讀性原理告訴我們,當(dāng)計算機訪問一個地址的數(shù)據(jù)的時候,與其相鄰的數(shù)據(jù)也會很快被訪問到。每一次IO讀取的數(shù)據(jù)我們稱之為一頁(page)。
IOPS與吞吐量
連續(xù)讀寫性能很好,但隨機讀寫性能很差
機械硬盤的連續(xù)讀寫性能很好,但隨機讀寫性能很差,這主要是因為磁頭移動到正確的磁道上需要時間,隨機讀寫時,磁頭需要不停的移動,時間都浪費在了磁頭尋址上,所以性能不高。
IOPS
IOPS(Input/Output Per Second)即指每秒內(nèi)系統(tǒng)能處理的I/O請求數(shù)量。 隨機讀寫頻繁的應(yīng)用,如小文件存儲等,關(guān)注隨機讀寫性能,IOPS是關(guān)鍵衡量指標(biāo)。
可以推算出磁盤的IOPS = 1000ms / (Tseek + Trotation + Transfer)
常見磁盤的隨機讀寫最大IOPS為:
7200rpm的磁盤 IOPS = 76 IOPS
10000rpm的磁盤IOPS = 111 IOPS
15000rpm的磁盤IOPS = 166 IOPS
磁盤吞吐量
指單位時間內(nèi)可以成功傳輸?shù)臄?shù)據(jù)數(shù)量。
磁盤陣列與服務(wù)器之間的數(shù)據(jù)通道對吞吐量影響很大。
順序讀寫頻繁的應(yīng)用,如視頻點播,關(guān)注連續(xù)讀寫性能、數(shù)據(jù)吞吐量是關(guān)鍵衡量指標(biāo)。
InnoDB索引——B+Tree索引
B+Tree,簡單來說就是一種為磁盤或者其他存儲設(shè)備而設(shè)計的一種平衡二叉樹。
由二叉樹,平衡二叉樹,BTree演化而來。
二叉樹 要保證父節(jié)點大于左子結(jié)點,小于右子節(jié)點。
平衡二叉樹 在二叉樹的基礎(chǔ)上,還要保證任一結(jié)點的兩個兒子字樹高度差不大于1。
BTree 是一種自平衡二叉樹,繼承了上述平衡二叉樹的特性,另外并保證了每個葉子結(jié)點到根節(jié)點的距離相同。
BTree vs B+Tree:
B+樹與BTree主要不同就是data的存放位置,以及葉子結(jié)點的指針構(gòu)成鏈表。
鍵值的拷貝被存儲在內(nèi)部節(jié)點(或稱非葉子結(jié)點);鍵值和記錄存儲在葉子節(jié)點;
一個葉子節(jié)點可以包含一個指針,指向另一個葉子節(jié)點以加速順序存取。
二叉樹

平衡二叉樹

BTree

B+Tree

索引為什么使用B+Tree?
每個頁的葉子結(jié)點包含較多的數(shù)據(jù),因此樹的高度較低(3~4),而樹的高度也決定了磁盤IO的次數(shù),從而影響了數(shù)據(jù)庫的性能。一般情況下,IO次數(shù)與樹的高度是一致的
對于組合索引,B+tree索引是按照索引列名進行順序排序的,因此可以將隨機IO轉(zhuǎn)換為順序IO提升IO效率;并且可以支持order by \group等排序需求;適合范圍查詢

聚集索引 與 非聚集索引
聚集索引:
InnfoBD引擎是索引組織表,所有數(shù)據(jù)都存放在聚集索引中。
準(zhǔn)確來說聚集索引并不是某種單獨的索引類型,而是一種數(shù)據(jù)存儲方式。就是指在同一個結(jié)構(gòu)中保存了B+tree索引以及數(shù)據(jù)行。InnoDB中通常主鍵就是一個聚集索引。
innoDB中,用戶如果沒有設(shè)置主鍵索引,會隨機選擇一個唯一的非空索引替代,
如果沒有這樣的索引,會隱式的定義一個主鍵作為隱式的聚集索引。
通常將主鍵設(shè)置為一個與業(yè)務(wù)無關(guān)的自增數(shù)字,這樣能保證按照主鍵順序插入數(shù)據(jù),避免頁分裂以及碎片問題。
主鍵索引的非葉子結(jié)點存放的是<.key,address.>,address就是指向下一層的指針。
主鍵索引的葉子結(jié)點保存了所有列的信息,因此通過主鍵索引可以快速獲取數(shù)據(jù)。
輔助索引
(或稱為非聚集索引、二級索引)
輔助索引的葉子結(jié)點并沒有存放數(shù)據(jù),而是存放了主鍵索引的值信息,而是<.key,address.>的形式。address用于指向?qū)?yīng)的主鍵索引的key。
因為二級索引葉子頁中存放了主鍵索引的值信息,如果主鍵索引很大的話,會導(dǎo)致所有索引都比較大。因此主鍵索引盡可能要小
也就是說使用輔助索引查詢,會通過葉子結(jié)點找到對應(yīng)的主鍵,在主鍵索引中找到最終的數(shù)據(jù)。
為什么要使用索引
- 使用索引可以大大減少服務(wù)器需要掃描的數(shù)據(jù)量。
- 使用索引可以幫助服務(wù)器避免排序或者臨時表
- 索引是隨機I\O變?yōu)?順序I\O.
索引的適用范圍
索引并不是適用于任何情況。對于中型、大型表適用。對于小型表全表掃描更高效。而對于特大型表,考慮”分區(qū)”技術(shù)。
高性能索引策略(其他類型索引:略)
以下講解使用如下表作為示例:
mysql> show create table people \G
*************************** 1. row ***************************
Table: people
Create Table: CREATE TABLE `people` (
`last_name` varchar(50) NOT NULL,
`first_name` varchar(50) NOT NULL,
`dob` date NOT NULL,
`gender` enum('m','f') NOT NULL,
KEY `last_name` (`last_name`,`first_name`,`dob`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
最左前綴匹配原則
對于KEY last_name (last_name,first_name,dob),where 后的謂詞必須包含last_name(組合索引的最左列),否則無法使用這個索引.
示例:
select ... from people where .... last_name="..."....
這個語句將使用 last_name (last_name,first_name,dob)索引。
無法跳過某個列使用后續(xù)索引列
示例:
SELECT ... FROM people WHERE last_name="..." AND dob="..."
這個語句只使用了last_name (last_name,first_name,dob)的last_name列,因為缺少first_name,所以后續(xù)dob列也無法從索引中搜索。
范圍查詢后的列無法使用索引
示例:
SELECT ... FROM people WHERE last_name > "..." AND first_name= "..." AND dob= "..."
這個語句只使用了last_name (last_name,first_name,dob)的last_name列,因為last_name 使用了范圍查詢,所以后續(xù)索引的兩個列無法使用。
什么事范圍查詢:
使用了范圍查詢的語句。范圍查詢指使用 “>” 、”<”、“between” “l(fā)ike”的查詢。注意“in”不算范圍查詢,屬于多值查詢條件。
列作為函數(shù)參數(shù)或表達(dá)式的一部分
列作為函數(shù)參數(shù)或表達(dá)式的一部分無法正常使用索引。
示例1:
SELECT ... FROM people WHERE last_name+1 = "1001"
示例2:
SELECT ... FROM people FORCE INDEX(last_name) WHERE LEFT(last_name,3) = "..."
以上示例均無法使用last_name (last_name,first_name,dob)索引。explain 展示位全表掃描。
前綴索引與索引選擇性
什么是索引的選擇性:
索引的基數(shù)(explain 中的一列:Cardinality) / 表的總記錄數(shù)(#T)
select count(Distinct columnName)/count(*) from Table
范圍從 1#T ~ 1 ,值越高查詢效率越高。唯一索引的選擇性是:1.
注:索引的基數(shù)(Cardinality)不重復(fù)的索引值。此處計算的基數(shù)(Cardinality),與SHOW INDEX 語句中的Cardinality并不一致!explain中只是預(yù)估值。
一般情況,將選擇性高的列放在左邊,選擇性高代表這個列的過濾性較好,盡可能的盡快過濾掉無用的數(shù)據(jù)。
前綴索引
對于 較大的Varchar類型、Text類型、Blob類型,需要建立索引時必須使用前綴索引,因為mysql不允許索引完整大小,而且索引字段越大效率越差。
可以索引開始的部分字符串(取代全部),大大節(jié)約索引空間,提高索引效率。但這樣會降低索引的選擇性。
所以,對比較長的 (Varchar、Text、BLOB等等數(shù)據(jù)類型)列查詢,要保證索引的選擇性,又要不能太長以節(jié)省空間。所以“前綴”需要選的恰到好處:
“前綴索引”的基數(shù)應(yīng)該接近完整的列索引的基數(shù)。
示例:前7個字符的前綴索引
mysql> select count(Distinct last_name)/count(*) from people ;
+------------------------------------+
| count(Distinct last_name)/count(*) |
+------------------------------------+
| 0.7059 |
+------------------------------------+
1 row in set (0.07 sec)
------------------------------------------------------------
mysql> select count(Distinct left(last_name,5))/count(*), count(Distinct left(la
st_name,6))/count(*) ,count(Distinct left(last_name,7))/count(*) from people \G
*************************** 1. row ***************************
count(Distinct left(last_name,5))/count(*): 0.6471
count(Distinct left(last_name,6))/count(*): 0.7059
count(Distinct left(last_name,7))/count(*): 0.7059
1 row in set (0.00 sec)
mysql> alter table people add key (last_name(6))
所以使用前6個字符即可達(dá)到完整字段的過濾性。
注意:
前綴索引是能夠使索引更小,更快的方法,但是無法使用前綴索引做 Group By\Order By,也不能用前綴索引做覆蓋查詢(Using Index)。
除了使用前綴索引的方式處理這類大字段索引的情況,還有如下方式:
偽哈希索引
step1建表語句
-- step1建表語句
CREATE TABLE `people` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`last_name` VARCHAR(50) COLLATE utf8_bin NOT NULL,
`first_name` VARCHAR(50) COLLATE utf8_bin NOT NULL,
`dob` DATE NOT NULL,
`gender` ENUM('m','f') COLLATE utf8_bin NOT NULL,
`blog_url` VARCHAR(128) COLLATE utf8_bin DEFAULT NULL,
`crc32_url` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `last_name` (`last_name`,`first_name`,`dob`),
KEY `crc32_url` (`crc32_url`)
) ENGINE=INNODB
因為blog_url是一個較長的字符串,所以直接將blog_url作為索引列會影響索引的整體效率?,F(xiàn)在,嘗試用一個偽hash值做一個偽hash索引。
step2 建立觸發(fā)器,用于每次插入\更新維護hash值
-- step2 建立觸發(fā)器,用于維護hash值
DELIMITER //
CREATE TRIGGER pseudohash_crc32_ins BEFORE INSERT
ON people FOR EACH ROW
BEGIN
SET New.crc32_url = CRC32(New.blog_url);
END //
CREATE TRIGGER pseudohash_crc32_upd BEFORE UPDATE
ON people FOR EACH ROW
BEGIN
SET New.crc32_url = CRC32(New.blog_url);
END //
DELIMITER;
效果如下,非常明顯:
SELECT COUNT(1) FROM people ;
/**
636480
*/
SELECT blog_url FROM people WHERE blog_url="http://www.-588141732.com" AND crc32_url=1790086969
/**
sql執(zhí)行時間:
執(zhí)行耗時 : 0.004 sec
傳送時間 : 0 sec
總耗時 : 0.004 sec
explain:
"id" "select_type" "table" "partitions" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"
"1" "SIMPLE" "people" \N "ref" "crc32_url" "crc32_url" "9" "const" "1" "10.00" "Using where"
*/
EXPLAIN SELECT blog_url FROM people WHERE blog_url="http://www.-588141732.com"
/**
sql執(zhí)行時間:
執(zhí)行耗時 : 0.413 sec
傳送時間 : 0 sec
總耗時 : 0.413 sec
explain:
"id" "select_type" "table" "partitions" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"
"1" "SIMPLE" "people" \N "ALL" \N \N \N \N "630928" "10.00" "Using where"
*/
crc函數(shù)當(dāng)數(shù)據(jù)量達(dá)到93000時,會產(chǎn)生1%的沖突。
如果要避免hash沖突的概率可以使用MD5()截取的方式取代crc()。
如:
SELECT CONV(RIGHT(MD5("http://www.-aqq3feaeaff41732fff.com"),16),16,10) AS hashCode;
三星索引(關(guān)系型數(shù)據(jù)庫索引設(shè)計及優(yōu)化)
- 一星 使用where后的謂詞列,按照選擇性構(gòu)造索引。
- 二星 如果語句中有排序操作,使用索引自帶的順序的排序(消除fileSort)。
- 三星 如果可以的話,將select后的還不在索引中的列名放到索引后邊,可以覆蓋索引(using index),而不需要讀取表數(shù)據(jù)。
如下這個sql就是一個三星索引。
SELECT last_name,first_name,dob FROM people WHERE last_name=”101899” AND first_name=”10189900” AND dob=”2017-08-03” ORDER BY dob
在實際應(yīng)用中,無法保證三個星每個星都滿足。需要權(quán)衡取舍。
冗余與重復(fù)索引
重復(fù)索引:相同列上按照相同順序創(chuàng)建的相同類型的索引。
冗余索引:已有索引(A,B),現(xiàn)在 創(chuàng)建索引 (A)就是一個冗余索引,因為,索引(A)完全可以被 (A,B)替代。然而,(B,A)、(B) 并不是 (A,B)的冗余索引。
另外當(dāng)Id列是主鍵,(A,Id)是冗余索引,因為二級緩存的葉子節(jié)點包含了主鍵值。直接使用(A)作為索引即可。
未使用的索引 也是累贅。建議刪除。
Explain output \Profile
關(guān)于explain的詳細(xì)解釋,請參考:
explain 詳解
或者查看官網(wǎng)文檔
explain-output
除了explain,還可以查看sql耗時分布情況。
Show Profiles \Show profile queryId
SHOW PROFILES;
/**
"Query_ID" "Duration" "Query"
...
"19" "0.00007350" "select ...."
"20" "0.00026150" "select state, round(sum(duration),5) ....."
...
*/
SHOW PROFILE FOR QUERY 24
/**
"Status" "Duration"
"Creating sort index" "0.748448"
"freeing items" "0.001355"
"starting" "0.000029"
"cleaning up" "0.000019"
"init" "0.000015"
"statistics" "0.000012"
"end" "0.000008"
"preparing" "0.000007"
"Opening tables" "0.000007"
"closing tables" "0.000006"
"Sending data" "0.000005"
"query end" "0.000005"
"optimizing" "0.000004"
"System lock" "0.000004"
"Sorting result" "0.000003"
"checking permissions" "0.000001"
"checking permissions" "0.000001"
"executing" "0.000001"
*/
高性能SQL
理解sql執(zhí)行過程
Step1:客戶端向Mysql服務(wù)器發(fā)送SQL語句。
使用”半雙工”通信方式,客戶端或服務(wù)端在一個連接上同一時刻只允許一方進行數(shù)據(jù)傳輸,并且直到數(shù)據(jù)傳輸完成,另一方才能執(zhí)行傳輸。
當(dāng)語句太長,超過 max_allowed_packet ,服務(wù)端會拒絕接收。
通常建議加上limit,可以減少不必要的數(shù)據(jù)從服務(wù)端發(fā)送到客戶端。
Step2:服務(wù)器收到后先查詢”查詢緩存“,如果命中,從緩存中直接返回sql執(zhí)行的結(jié)果集。否則,進入Step3。
這個緩存通過一個對大小寫敏感的hash算法實現(xiàn),及時只有一個字節(jié)不匹配,那也無法命中。
Step3:服務(wù)器解析、預(yù)處理、優(yōu)化sql執(zhí)行計劃,然后將處理好的sql放入查詢的執(zhí)行計劃中。
在這個階段,sql會被轉(zhuǎn)換為一個執(zhí)行計劃,使用這個執(zhí)行計劃于具體的存儲引擎進行交換。這個階段包括,解析、預(yù)處理、優(yōu)化sql執(zhí)行計劃這三個子任務(wù)。
Step4:執(zhí)行引擎通過調(diào)用”存儲引擎”(如,innodb、myisam等)提供的API去執(zhí)行這個計劃。
Step5:服務(wù)器返回結(jié)果給客戶端
這里寫圖片描述
慢SQL優(yōu)化步驟
Step1:explain查看 (show profile可以查看耗時分布)
Step2:確認(rèn)優(yōu)化目標(biāo)\方向,對于復(fù)雜sql需要理清執(zhí)行步驟
目標(biāo)1. type是否能夠按照 const>eq_reg>ref>range>index>ALL的順序優(yōu)化,最差也要達(dá)到range級別。
目標(biāo)2. 避免filesort的出現(xiàn)、避免rows數(shù)據(jù)量太大等負(fù)面字段、索引選擇性是否足夠、對于關(guān)聯(lián)查詢盡量保證關(guān)聯(lián)字段在第二張表上有可用索引(原因:NestLoop)。
Step3:遵照SQL索引原則增加或調(diào)整SQL,常見如下(可參考上文去理解)
保證where后的謂詞盡可能出現(xiàn)在索引中,并且組合索引按選擇性順序排序,范圍查詢條件盡量放在后邊
(如果sql中有排序語句)是否能夠通過索引解決排序問題
是否能使用use index,全部通過索引獲取數(shù)據(jù)
NestLoop
除了full Join,其他所有類型的查詢SQL,都以類似的方式執(zhí)行。
NestLoop (內(nèi)嵌套循環(huán))算法,簡單來說就是逐行查詢處理,或者內(nèi)嵌逐行查詢。對于高版本的使用join buffer對上層表數(shù)據(jù)緩存,無需多次遍歷上層表,下層表直接使用(Block NestLoop)。
以下以兩個示例詳細(xì)說明執(zhí)行計劃,其他join以及單表查詢原理也是類似的!
Join執(zhí)行順序偽代碼演示
示例1:內(nèi)關(guān)聯(lián)inner join
SELECT
people.id,user.id
FROM
user
INNER JOIN
people ON user.name = people.name
WHERE
user.enumType = 'orange'
AND people.enumType = 'orange';
示例1對應(yīng)偽代碼:
//先掃描先執(zhí)行的表,優(yōu)化器通常選擇關(guān)聯(lián)的較小的表,explain第一行。
people_iterator=people_table.iterator();
//逐行遍歷,并且丟棄where篩選不通過的行
while(people_iterator.hashNext()){
people_item=people_iterator.next();
if(people_item.enumType=='orange'){
//篩選通過后,在進入第二個嵌套
user_iterator=user_table.iterator()
//逐行遍歷第二個表
while(user_iterator.hashNext()){
user_item=user_iterator.next();
//過濾:on 的條件匹配以及當(dāng)前表的where條件
if(user_item.name==people_item.name&&user_item.enumType=='orange' ){
output(people.id,user.id);
}
}
}
}
示例2:非內(nèi)關(guān)聯(lián)
SELECT
people.id,user.id
FROM
user
LEFT JOIN
people ON user.name = people.name
WHERE
user.enumType = 'orange'
AND people.enumType = 'orange';
示例2偽代碼
//先掃描先執(zhí)行的表,優(yōu)化器通常選擇關(guān)聯(lián)的較小的表,explain第一行。
people_iterator=people_table.iterator();
//逐行遍歷,并且丟棄where篩選不通過的行
while(people_iterator.hashNext()){
people_item=people_iterator.next();
if(people_item.enumType=='orange'){
//篩選通過后,在進入第二個嵌套
user_iterator=user_table.iterator()
//逐行遍歷第二個表
while(user_iterator.hashNext()){
user_item=user_iterator.next();
//過濾:on 的條件匹配以及當(dāng)前表的where條件
if(user_item.name==people_item.name&&user_item.enumType=='orange' ){
output(people.id,user.id);
}
//與innerjoin不同的,leftJoin需要即使on條件不成立,也要保留左邊數(shù)據(jù)
else if(!is_innerJoin){
output(people.id,null);//保留左邊數(shù)據(jù)
}
}
}
}
注意:盡量保證關(guān)聯(lián)字段在第二張表上有可用索引。
(因為第一張表示全表掃描,然后會對第二張表用關(guān)聯(lián)字段查詢,詳情請看NestLoop理解關(guān)聯(lián)過程)
SQL使用常用策略
1.通常情況下,使用一個性能好的sql去做更多的事情,而不是使用多個sql。
除非這個sql過長效率低下或者對于delete這種語句,過長的delete會導(dǎo)致太多的數(shù)據(jù)被鎖定,耗盡資源,阻塞其他sql。
2.分解關(guān)聯(lián)查詢。
將關(guān)聯(lián)(** join……)放在應(yīng)用中處理,執(zhí)行小而簡單的sql,好處是:
分解后的sql通常由于簡單固定,能更好的使用mysql緩存。
執(zhí)行拆分后的sql,可以減少鎖的競爭。
程序具備更強的擴展性
關(guān)聯(lián)sql使用的是內(nèi)嵌循環(huán)算法nestloop,而應(yīng)用中可以使用hashmap等結(jié)構(gòu)處理數(shù)據(jù),效率更高
關(guān)于Count()
count()函數(shù)有兩種含義:統(tǒng)計行數(shù)、統(tǒng)計列數(shù)。
比如:count(*)代表統(tǒng)計的行數(shù);count(talbe.cloumn)代表統(tǒng)計的是這個列不為null的數(shù)量。
關(guān)于Limit
在使用Limit 1000,20這種操作的時候,mysql會掃描偏移量(1000條無效查詢)數(shù)據(jù),而只取后20條,盡量避免這種寫法,想辦法規(guī)避。
關(guān)于Union
需要將where、order by、limit 這些限制放入到每個子查詢,才能重分提升效率。另外如非必須,盡量使用Union all,因為union會給每個子查詢的臨時表加入distinct,對每個臨時表做唯一性檢查,效率較差。