參考:
https://blog.csdn.net/kqqkqq123/article/details/98057744
https://www.cnblogs.com/ljl150/p/12934071.html
https://blog.csdn.net/weixin_43268933/article/details/108505662
https://www.cnblogs.com/gomysql/p/3720123.html
https://www.cnblogs.com/jjpbk/p/11734690.html
目錄:
最左前綴匹配原則
EXPLAIN 命令詳解
數據庫與數據倉庫的區別
結構化數據庫與非結構化數據庫
最左前綴原則
MySQL中的索引可以以一定順序引用多列,這種索引叫作聯合索引。如User表的name和city加聯合索引就是(name,city),而最左前綴原則指的是,如果查詢的時候查詢條件精確匹配索引的左邊連續一列或幾列,則此列就可以被用到。如下:
select * from user where name=xx and city=xx ; //可以命中索引 select * from user where name=xx ; // 可以命中索引 select * from user where city=xx ; // 無法命中索引
這里需要注意的是,查詢的時候如果兩個條件都用上了,但是順序不同,如 city= xx and name =xx
,那么現在的查詢引擎會自動優化為匹配聯合索引的順序,這樣是能夠命中索引的。
由於最左前綴原則,在創建聯合索引時,索引字段的順序需要考慮字段值去重之后的個數,較多的放前面。ORDER BY子句也遵循此規則。
面試中常被提到的最左前綴匹配原則
最左前綴匹配原則:在MySQL建立聯合索引時會遵守最左前綴匹配原則,即最左優先,在檢索數據時從聯合索引的最左邊開始匹配。
要想理解聯合索引的最左匹配原則,先來理解下索引的底層原理。索引的底層是一顆B+樹,那么聯合索引的底層也就是一顆B+樹,只不過聯合索引的B+樹節點中存儲的是鍵值。由於構建一棵B+樹只能根據一個值來確定索引關系,所以數據庫依賴聯合索引最左的字段來構建。
舉例:創建一個(a,b)的聯合索引,那么它的索引樹就是下圖的樣子。
可以看到a的值是有順序的,1,1,2,2,3,3,而b的值是沒有順序的1,2,1,4,1,2。但是我們又可發現a在等值的情況下,b值又是按順序排列的,但是這種順序是相對的。這是因為MySQL創建聯合索引的規則是首先會對聯合索引的最左邊第一個字段排序,在第一個字段的排序基礎上,然后在對第二個字段進行排序。所以b=2這種查詢條件沒有辦法利用索引。
由於整個過程是基於explain結果分析的,那接下來在了解下explain中的type字段和key_lef字段。
1.type:聯接類型。下面給出各種聯接類型,按照從最佳類型到最壞類型進行排序:(重點看ref,rang,index)
system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,可以忽略不計
const:表示通過索引一次就找到了,const用於比較primary key 或者 unique索引。因為只需匹配一行數據,所有很快。如果將主鍵置於where列表中,mysql就能將該查詢轉換為一個const
eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵 或 唯一索引掃描。
注意:ALL全表掃描的表記錄最少的表如t1表
ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。本質是也是一種索引訪問,它返回所有匹配某個單獨值的行,然而他可能會找到多個符合條件的行,所以它應該屬於查找和掃描的混合體。
range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了那個索引。一般就是在where語句中出現了bettween、<、>、in等的查詢。這種索引列上的范圍掃描比全索引掃描要好。只需要開始於某個點,結束於另一個點,不用掃描全部索引。
index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹。這通常為ALL塊,應為索引文件通常比數據文件小。(Index與ALL雖然都是讀全表,但index是從索引中讀取,而ALL是從硬盤讀取)
ALL:Full Table Scan,遍歷全表以找到匹配的行
2.key_len:顯示MySQL實際決定使用的索引的長度。如果索引是NULL,則長度為NULL。如果不是NULL,則為使用的索引的長度。所以通過此字段就可推斷出使用了那個索引。
計算規則:
1.定長字段,int占用4個字節,date占用3個字節,char(n)占用n個字符。
2.變長字段varchar(n),則占用n個字符+兩個字節。
3.不同的字符集,一個字符占用的字節數是不同的。Latin1編碼的,一個字符占用一個字節,gdk編碼的,一個字符占用兩個字節,utf-8編碼的,一個字符占用三個字節。
(由於我數據庫使用的是Latin1編碼的格式,所以在后面的計算中,一個字符按一個字節算)
4.對於所有的索引字段,如果設置為NULL,則還需要1個字節。
接下來進入正題!!!
示例:
首先創建一個表
該表中對id列.name列.age列建立了一個聯合索引 id_name_age_index,實際上相當於建立了三個索引(id)(id_name)(id_name_age)。
下面介紹下可能會使用到該索引的幾種情況:
1.全值匹配查詢時
通過觀察上面的結果圖可知,where后面的查詢條件,不論是使用(id,age,name)(name,id,age)還是(age,name,id)順序,在查詢時都使用到了聯合索引,可能有同學會疑惑,為什么底下兩個的搜索條件明明沒有按照聯合索引從左到右進行匹配,卻也使用到了聯合索引? 這是因為MySQL中有查詢優化器explain,所以sql語句中字段的順序不需要和聯合索引定義的字段順序相同,查詢優化器會判斷糾正這條SQL語句以什么樣的順序執行效率高,最后才能生成真正的執行計划,所以不論以何種順序都可使用到聯合索引。另外通過觀察上面三個圖中的key_len字段,也可說明在搜索時使用的聯合索引中的(id_name_age)索引,因為id為int型,允許null,所以占5個字節,name為char(10),允許null,又使用的是latin1編碼,所以占11個字節,age為int型允許null,所以也占用5個字節,所以該索引長度為21(5+11+5),而上面key_len的值也正好為21,可證明使用的(id_name_age)索引。
2.匹配最左邊的列時
該搜索是遵循最左匹配原則的,通過key字段也可知,在搜索過程中使用到了聯合索引,且使用的是聯合索引中的(id)索引,因為key_len字段值為5,而id索引的長度正好為5(因為id為int型,允許null,所以占5個字節)。
由於id到name是從左邊依次往右邊匹配,這兩個字段中的值都是有序的,所以也遵循最左匹配原則,通過key字段可知,在搜索過程中也使用到了聯合索引,但使用的是聯合索引中的(id_name)索引,因為key_len字段值為16,而(id_name)索引的長度正好為16(因為id為int型,允許null,所以占5個字節,name為char(10),允許null,又使用的是latin1編碼,所以占11個字節)。
由於上面三個搜索都是從最左邊id依次向右開始匹配的,所以都用到了id_name_age_index聯合索引。
那如果不是依次匹配呢?
通過key字段可知,在搜索過程中也使用到了聯合索引,但使用的是聯合索引中的(id)索引,從key_len字段也可知。因為聯合索引樹是按照id字段創建的,但age相對於id來說是無序的,只有id只有序的,所以他只能使用聯合索引中的id索引。
通過觀察發現上面key字段發現在搜索中也使用了id_name_age_index索引,可能許多同學就會疑惑它並沒有遵守最左匹配原則,按道理會索引失效,為什么也使用到了聯合索引?因為沒有從id開始匹配,且name單獨來說是無序的,所以它確實不遵循最左匹配原則,然而從type字段可知,它雖然使用了聯合索引,但是它是對整個索引樹進行了掃描,正好匹配到該索引,與最左匹配原則無關,一般只要是某聯合索引的一部分,但又不遵循最左匹配原則時,都可能會采用index類型的方式掃描,但它的效率遠不如最做匹配原則的查詢效率高,index類型類型的掃描方式是從索引第一個字段一個一個的查找,直到找到符合的某個索引,與all不同的是,index是對所有索引樹進行掃描,而all是對整個磁盤的數據進行全表掃描。
這兩個結果跟上面的是同樣的道理,由於它們都沒有從最左邊開始匹配,所以沒有用到聯合索引,使用的都是index全索引掃描。
3.匹配列前綴
如果id是字符型,那么前綴匹配用的是索引,中墜和后綴用的是全表掃描。
select * from staffs where id like 'A%';//前綴都是排好序的,使用的都是聯合索引 select * from staffs where id like '%A%';//全表查詢 select * from staffs where id like '%A';//全表查詢
4.匹配范圍值
在匹配的過程中遇到<>=號,就會停止匹配,但id本身就是有序的,所以通過possible_keys字段和key_len 字段可知,在該搜索過程中使用了聯合索引的id索引(因為id為int型,允許null,所以占5個字節),且進行的是rang范圍查詢。
由於不遵循最左匹配原則,且在id<4的范圍中,age是無序的,所以使用的是index全索引掃描。
不遵循最左匹配原則,但在數據庫中id<2的只有一條(id),所以在id<2的范圍中,age是有序的,所以使用的是rang范圍查詢。
不遵循最左匹配原則,而age又是無序的,所以進行的全索引掃描。
5.准確匹配第一列並范圍匹配其他某一列
由於搜索中有id=1,所以在id范圍內age是無序的,所以只使用了聯合索引中的id索引。
最左匹配原則的底層原理
什么是最左匹配原則
顧名思義:最左優先,以最左邊的為起點任何連續的索引都能匹配上。同時遇到范圍查詢(>、<、between、like)就會停止匹配。
例如:如果建立(a,b)順序的索引,我們的條件只有b=xxx,是匹配不到(a,b)索引的;但是如果查詢條件是a = 1 and b = 2或者b=2 and a=1就可以,因為優化器會自動調整a,b的順序,並不需要嚴格按照索引的順序來;再比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,因為c字段是一個范圍查詢,它之后的字段會停止匹配
為什么會形成最左匹配原則
首先要知道,最左匹配原則都是針對聯合索引來說的,所以我們有必要了解一下聯合索引的原理。了解了聯合索引,那么為什么會有最左匹配原則這種說法也就理解了。
我們都知道索引的底層是一顆B+樹,那么聯合索引當然還是一顆B+樹,只不過聯合索引的健值數量不是一個,而是多個。構建一顆B+樹只能根據一個值來構建,因此數據庫依據聯合索引最左的字段來構建B+樹。
例子:假如創建一個(a,b)的聯合索引,那么它的索引樹是這樣的
可以看到a的值是有順序的,1,1,2,2,3,3,而b的值是沒有順序的1,2,1,4,1,2。所以b = 2這種查詢條件沒有辦法利用索引,因為聯合索引首先是按a排序的,b是無序的。
同時我們還可以發現在a值相等的情況下,b值又是按順序排列的,但是這種順序是相對的。所以最左匹配原則遇上范圍查詢就會停止,剩下的字段都無法使用索引。例如a = 1 and b = 2 a,b字段都可以使用索引,因為在a值確定的情況下b是相對有序的,而a>1and b=2,a字段可以匹配上索引,但b值不可以,因為a的值是一個范圍,在這個范圍中b是無序的。
EXPLAIN 命令詳解
在工作中,我們用於捕捉性能問題最常用的就是打開慢查詢,定位執行效率差的SQL,那么當我們定位到一個SQL以后還不算完事,我們還需要知道該SQL的執行計划,比如是全表掃描,還是索引掃描,這些都需要通過EXPLAIN去完成。EXPLAIN命令是查看優化器如何決定執行查詢的主要方法。可以幫助我們深入了解MySQL的基於開銷的優化器,還可以獲得很多可能被優化器考慮到的訪問策略的細節,以及當運行SQL語句時哪種策略預計會被優化器采用。需要注意的是,生成的QEP並不確定,它可能會根據很多因素發生改變。MySQL不會將一個QEP和某個給定查詢綁定,QEP將由SQL語句每次執行時的實際情況確定,即便使用存儲過程也是如此。盡管在存儲過程中SQL語句都是預先解析過的,但QEP仍然會在每次調用存儲過程的時候才被確定。
通過執行計划可以知道什么?
(root@yayun-mysql-server) [test]>explain select d1.age, t2.id from (select age,name from t1 where id in (1,2))d1, t2 where d1.age=t2.age group by d1.age, t2.id order by t2.id; +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ | 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 2 | Using temporary; Using filesort | | 1 | PRIMARY | t2 | ref | age | age | 5 | d1.age | 1 | Using where; Using index | | 2 | DERIVED | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+------------+-------+---------------+---------+---------+--------+------+---------------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
MySQL執行計划調用方式
1.EXPLAIN SELECT ……
變體:
2.EXPLAIN EXTENDED SELECT ……
將執行計划"反編譯"成SELECT語句,運行SHOW WARNINGS
可得到被MySQL優化器優化后的查詢語句
3.EXPLAIN PARTITIONS SELECT ……
用於分區表的EXPLAIN生成QEP的信息
執行計划包含的信息
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1. id:
包含一組數字,表示查詢中執行select子句或操作表的順序
Example(id相同,執行順序由上至下)
(root@yayun-mysql-server) [test]>explain select t2.* from t1, t2, t3 where t1.id=t2.id and t1.id=t3.id and t1.name=''; +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ | 1 | SIMPLE | t1 | ref | PRIMARY,name | name | 63 | const | 1 | Using where; Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+--------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
Example (如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行)
(root@yayun-mysql-server) [test]>explain select t2.* from t2 where id = (select id from t1 where id = (select t3.id from t3 where t3.name='')); +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ | 1 | PRIMARY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE noticed after reading const tables | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | no matching row in const table | | 3 | SUBQUERY | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
Example(id如果相同,可以認為是一組,從上往下順序執行;在所有組中,id值越大,優先級越高,越先執行)
(root@yayun-mysql-server) [test]>explain select t2.* from (select t3.id from t3 where t3.name='')s1, t2 where s1.id=t2.id; +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | t2 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | DERIVED | t3 | ref | name | name | 63 | | 1 | Using where; Using index | +----+-------------+------------+--------+---------------+---------+---------+-------+------+--------------------------+ 3 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
2. select_type
示查詢中每個select子句的類型(簡單OR復雜)
a. SIMPLE:查詢中不包含子查詢或者UNION
b. 查詢中若包含任何復雜的子部分,最外層查詢則被標記為:PRIMARY
c. 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為:SUBQUERY
d. 在FROM列表中包含的子查詢被標記為:DERIVED(衍生)用來表示包含在from子句中的子查詢的select,mysql會遞歸執行並將結果放到一個臨時表中。服務器內部稱為"派生表",因為該臨時表是從子查詢中派生出來的
e. 若第二個SELECT出現在UNION之后,則被標記為UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記為:DERIVED
f. 從UNION表獲取結果的SELECT被標記為:UNION RESULT
SUBQUERY和UNION還可以被標記為DEPENDENT和UNCACHEABLE。
DEPENDENT意味着select依賴於外層查詢中發現的數據。
UNCACHEABLE意味着select中的某些 特性阻止結果被緩存於一個item_cache中。
Example
(root@yayun-mysql-server) [test]>explain select d1.name, ( select id from t3) d2 from (select id,name from t1 where name='')d1 union (select name,id from t2); +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ | 1 | PRIMARY | <derived3> | system | NULL | NULL | NULL | NULL | 0 | const row not found | | 3 | DERIVED | t1 | ref | name | name | 63 | | 1 | Using where; Using index | | 2 | SUBQUERY | t3 | index | NULL | age | 5 | NULL | 6 | Using index | | 4 | UNION | t2 | index | NULL | name | 63 | NULL | 4 | Using index | | NULL | UNION RESULT | <union1,4> | ALL | NULL | NULL | NULL | NULL | NULL | | +----+--------------+------------+--------+---------------+------+---------+------+------+--------------------------+ 5 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
第一行:id列為1,表示第一個select,select_type列的primary表 示該查詢為外層查詢,table列被標記為<derived3>,表示查詢結果來自一個衍生表,其中3代表該查詢衍生自第三個select查詢,即id為3的select。
第二行:id為3,表示該查詢的執行次序為2( 4 => 3),是整個查詢中第三個select的一部分。因查詢包含在from中,所以為derived。
第三行:select列表中的子查詢,select_type為subquery,為整個查詢中的第二個select。
第四行:select_type為union,說明第四個select是union里的第二個select,最先執行。
第五行:代表從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結果進行union操作。
3. type
表示MySQL在表中找到所需行的方式,又稱“訪問類型”,常見類型如下:
ALL, index, range, ref, eq_ref, const, system, NULL
從左到右,性能從最差到最好
Example
a. ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
(root@yayun-mysql-server) [test]>explain select * from t1 where email=''; +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using where | +----+-------------+-------+------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
b. index:Full Index Scan,index與ALL區別為index類型只遍歷索引樹
(root@yayun-mysql-server) [test]>explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
c. range:索引范圍掃描,對索引的掃描開始於某一點,返回匹配值域的行。顯而易見的索引范圍掃描是帶有between或者where子句里帶有<, >查詢。當mysql使用索引去查找一系列值時,例如IN()和OR列表,也會顯示range(范圍掃描),當然性能上面是有差異的。
(root@yayun-mysql-server) [test]>explain select * from t1 where id in (1,4); +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id between 1 and 4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id=1 or id=4; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.01 sec) (root@yayun-mysql-server) [test]>explain select * from t1 where id > 1; +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 3 | Using where | +----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
d. ref:使用非唯一索引掃描或者唯一索引的前綴掃描,返回匹配某個單獨值的記錄行
(root@yayun-mysql-server) [test]>explain select * from t1 where name='yayun'; +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ | 1 | SIMPLE | t1 | ref | name | name | 63 | const | 1 | Using where | +----+-------------+-------+------+---------------+------+---------+-------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
e. eq_ref:類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連接中使用primary key或者 unique key作為關聯條件
(root@yayun-mysql-server) [test]>explain select t1.name from t1, t2 where t1.id=t2.id; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | test.t1.id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
f. const、system:當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些類型訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量
(root@yayun-mysql-server) [test]>explain select * from ( select * from t1 where id=1)b1; +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 2 | DERIVED | t1 | const | PRIMARY | PRIMARY | 4 | | 1 | | +----+-------------+------------+--------+---------------+---------+---------+------+------+-------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
注:system是const類型的特例,當查詢的表只有一行的情況下,使用system
g. NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列里選取最小值可以通過單獨索引查找完成。
(root@yayun-mysql-server) [test]>explain select * from t1 where id = (select min(id) from t2); +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ | 1 | PRIMARY | t1 | const | PRIMARY | PRIMARY | 4 | const | 1 | | | 2 | SUBQUERY | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+-------+---------------+---------+---------+-------+------+------------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
4. possible_keys
指出MySQL能使用哪個索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用
5. key
顯示MySQL在查詢中實際使用的索引,若沒有使用索引,顯示為NULL
Example
(root@yayun-mysql-server) [test]>explain select id,age from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
6. key_len
表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度(key_len顯示的值為索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的)
7. ref
表示上述表的連接匹配條件,即哪些列或常量被用於查找索引列上的值
8. rows
表示MySQL根據表統計信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數
Example
(root@yayun-mysql-server) [test]>explain select * from t1 , t2 where t1.id=t2.id and t2.name='atlas'; +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ | 1 | SIMPLE | t2 | ref | PRIMARY,name | name | 63 | const | 1 | Using where | | 1 | SIMPLE | t1 | eq_ref | PRIMARY | PRIMARY | 4 | test.t2.id | 1 | | +----+-------------+-------+--------+---------------+---------+---------+------------+------+-------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
9. Extra
包含不適合在其他列中顯示但十分重要的額外信息
a. Using index
該值表示相應的select操作中使用了覆蓋索引(Covering Index)
Example
(root@yayun-mysql-server) [test]>explain select id from t1; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
覆蓋索引(Covering Index)
MySQL可以利用索引返回select列表中的字段,而不必根據索引再次讀取數據文件
包含所有滿足查詢需要的數據的索引稱為覆蓋索引(Covering Index)
注意:如果要使用覆蓋索引,一定要注意select列表中只取出需要的列,不可select *,因為如果將所有字段一起做索引會導致索引文件過大,查詢性能下降
b. Using where
表示mysql服務器將在存儲引擎檢索行后再進行過濾。許多where條件里涉及索引中的列,當(並且如果)它讀取索引時,就能被存儲引擎檢驗,因此不是所有帶where字句的查詢都會顯示"Using where"。有時"Using where"的出現就是一個暗示:查詢可受益與不同的索引。
Example
(root@yayun-mysql-server) [test]>explain select id,name from t1 where id<4; +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ | 1 | SIMPLE | t1 | index | PRIMARY | name | 63 | NULL | 4 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+--------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
c. Using temporary
表示MySQL需要使用臨時表來存儲結果集,常見於排序和分組查詢
這個值表示使用了內部臨時(基於內存的)表。一個查詢可能用到多個臨時表。有很多原因都會導致MySQL在執行查詢期間創建臨時表。兩個常見的原因是在來自不同表的上使用了DISTINCT,或者使用了不同的ORDER BY和GROUP BY列。可以強制指定一個臨時表使用基於磁盤的MyISAM存儲引擎。這樣做的原因主要有兩個:
1)內部臨時表占用的空間超過min(tmp_table_size,max_heap_table_size)系統變量的限制
2)使用了TEXT/BLOB 列
Example
(root@yayun-mysql-server) [test]>explain select id from t1 where id in (1,2) group by age,name; +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | NULL | 2 | Using where; Using temporary; Using filesort | +----+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
d. Using filesort
MySQL中無法利用索引完成的排序操作稱為“文件排序”
Example
(root@yayun-mysql-server) [test]>explain select id,age from t1 order by name; +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | Using filesort | +----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>explain select id,age from t1 order by age; +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ | 1 | SIMPLE | t1 | index | NULL | age | 5 | NULL | 4 | Using index | +----+-------------+-------+-------+---------------+------+---------+------+------+-------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
e. Using join buffer
改值強調了在獲取連接條件時沒有使用索引,並且需要連接緩沖區來存儲中間結果。如果出現了這個值,那應該注意,根據查詢的具體情況可能需要添加索引來改進能。
Example
(root@yayun-mysql-server) [test]>explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ | 1 | SIMPLE | t1 | index | name | name | 63 | NULL | 4 | Using index | | 1 | SIMPLE | t2 | ref | name | name | 63 | test.t1.name | 2 | Using where; Using index | +----+-------------+-------+-------+---------------+------+---------+--------------+------+--------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>alter table t1 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 (root@yayun-mysql-server) [test]>alter table t2 drop key name; Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 (root@yayun-mysql-server) [test]>explain select t1.name from t1 inner join t2 on t1.name=t2.name; +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ | 1 | SIMPLE | t1 | ALL | NULL | NULL | NULL | NULL | 4 | | | 1 | SIMPLE | t2 | ALL | NULL | NULL | NULL | NULL | 4 | Using where; Using join buffer | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ 2 rows in set (0.00 sec) (root@yayun-mysql-server) [test]>
f. Impossible where
這個值強調了where語句會導致沒有符合條件的行。
Example
(root@yayun-mysql-server) [test]>EXPLAIN SELECT * FROM t1 WHERE 1=2; +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE | +----+-------------+-------+------+---------------+------+---------+------+------+------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
h. Select tables optimized away
這個值意味着僅通過使用索引,優化器可能僅從聚合函數結果中返回一行.
Example
(root@yayun-mysql-server) [test]>explain select max(id) from t1; +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ | 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away | +----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+ 1 row in set (0.00 sec) (root@yayun-mysql-server) [test]>
I. Index merges
當MySQL 決定要在一個給定的表上使用超過一個索引的時候,就會出現以下格式中的一個,詳細說明使用的索引以及合並的類型。
Using sort_union(...)
Using union(...)
Using intersect(...)
總結:
• EXPLAIN不會告訴你關於觸發器、存儲過程的信息或用戶自定義函數對查詢的影響情況
• EXPLAIN不考慮各種Cache
• EXPLAIN不能顯示MySQL在執行查詢時所作的優化工作
• 部分統計信息是估算的,並非精確值
• EXPALIN只能解釋SELECT操作,其他操作要重寫為SELECT后查看執行計划。
數據庫與數據倉庫的區別
數據庫與數據倉庫的區別實際講的是OLTP與OLAP的區別。
操作性處理,叫聯機事務處理OLTP(On-Line Transaction Processing),也可以稱面向交易的處理系統,他是針對具體業務在數據庫聯機的日常操作,通常對少數記錄進行查詢,修改。用戶較為關心操作的響應時間,數據的安全性,完整性和並發支持的用戶數等問題。傳統的數據庫系統作為數據管理的主要手段,主要用於操作性處理。
分析性處理,叫聯機分析處理OLAP(On-Line Analytical Processing),一般針對某些主題的歷史數據進行分析,支持管理決策。
數據倉庫的出現並不是要取代數據庫。
- 數據庫是面向事務的設計,數據倉庫是面向主題設計的。
- 數據一般存儲業務數據,數據倉庫存儲的一般是歷史數據。
- 數據庫設一是盡量避免冗余,一般針對某一業務應用進行設計,比如一張簡單的User表,記錄用戶名,密碼等簡單數據即可,符合業務應用,但是不符合分析。數據倉庫在設計時有意引入冗余,依照分析需求,分析維度,分析指標進行設計。
- 數據庫是為捕獲數據而設計,數據倉庫是為分析數據而設計。
以銀行業務為例。數據庫是事務系統的數據平台,客戶在銀行做的每筆交易都會寫入數據庫,被記錄下來,這里,可以簡單地理解為用數據庫記賬。數據倉庫是分析系統的數據平台,它從事務系統獲取數據,並做匯總、加工,為決策者提供決策的依據。比如,某銀行某分行一個月發生多少交易,該分行當前存款余額是多少。如果存款又多,消費交易又多,那么該地區就有必要設立ATM了。
顯然,銀行的交易量是巨大的,通常以百萬甚至千萬次來計算。事務系統是實時的,這就要求時效性,客戶存一筆錢需要幾十秒是無法忍受的,這就要求數據庫只能存儲很短一段時間的數據。而分析系統是事后的,它要提供關注時間段內所有的有效數據。這些數據是海量的,匯總計算起來也要慢一些,但是,只要能夠提供有效的分析數據就達到目的了。
數據倉庫,是在數據庫已經大量存在的情況下,為了進一步挖掘數據資源、為了決策需要而產生的,它決不是所謂的“大型數據庫。
https://news.west.cn/58960.html
結構化數據庫與非結構化數據庫
在信息社會,信息可以划分為兩大類。一類信息能夠用數據或統一的結構加以表示,我們稱之為結構化數據,如數字、符號;而另一類信息無法用數字或統一的結構表示,如文本、圖像、聲音、網頁等,我們稱之為非結構化數據。結構化數據屬於非結構化數據,是非結構化數據的特例。
隨着網絡技術的發展,特別是Internet和Intranet技術的飛快發展,使得非結構化數據的數量日趨增大。這時,主要用於管理結構化數據的關系數據庫的局限性暴露地越來越明顯。因而,數據庫技術相應地進入了“后關系數據庫時代”,發展進入基於網絡應用的非結構化數據庫時代。所謂非結構化數據庫,是指數據庫的變長紀錄由若干不可重復和可重復的字段組成,而每個字段又可由若干不可重復和可重復的子字段組成。簡單地說,非結構化數據庫就是字段可變的數據庫。
目前有兩大類型的數據庫,一種是結構化SQL數據庫,一種非結構化NOSQL數據庫。
比拼1:數據的組織形式
SQL, 顧名思義是結構化查詢語言。它的數據都是結構化的,這個需要在最初創建數據庫的時候要做好設計,這個設計一旦定型以后,再修改的話就會比較麻煩。當然如果設計做得好的話,也就無需再修改了。所以結構化數據最大的一個工作就是表的設計。這是在使用這種數據庫的時候,開發工作中的重中之重。
結構化數據的另一個體現就是各種數據之間的關系,比如說1對1的關系,一對多的關系,多對多的關系。另一個體現就是數據的定義嚴格,在一個表中只能存放一種表數據,也就是說,你的每一行的數據都要遵循這個表的的定義。這個表里的每行的數據都遵循這個表內定義好的數據類型,不能夠存放一些所謂非定義的數據,否則出錯。
而NOSQL數據庫不需要結構化的數據設計,這樣它的容錯性就很強,也不存在太嚴格的設計,以后的擴展和修改都比較容易。
NOSQL數據庫里面不存在關系這個概念,如果你想實現關系,比如說1對1,一對多,多對多,你需要用程序來實現,而不是用數據庫本身來實現。另外一個是一個表中可以存放不同的數據類型, 簡單的說就是每一行的數據可以不遵循統一的定義。
比拼2:原子操作
所謂原子操作,就是指一個操作要么成功,要么失敗,沒有半途而終的。假設說一個處理訂單的操作中存在5個步驟,你處理一個訂單,提交訂單,開始計算數據,隨后寫入數據庫五個表然后,才返回成功,如果有一個失敗,那就返回失敗。返回失敗就意味着撤回之前所有的操作。
這種原子操作在SQL數據庫中非常容易實現,它本身就存在這樣的機制叫做事務處理機制。這也是我們選擇SQL數據庫的一個重要參考指標。只要我們在處理數據的過程中存在這樣的操作,要么成功,要么失敗,那么我們首先要選擇的就是SQL數據庫。
然而在NOSQL數據庫中不存在這樣的機制。但是這里追求數據的統一性,比如說你有很多個數據集,這里不稱之為數據表了。一旦有一部分修改,你必須更新所有的包含這類數據數據集。
比拼3:效率方面。
結構化數據庫有很多方式可以提高數據的處理效率。比如說創建索引,使用存儲程序Stored Procedure, 一些架構如entity framework, hibernate。但是因為結構化數據庫天然的追求數據的完整性,所以它在效率方面還是存在一些瓶頸的。
然而NOSQL非結構化數據庫就不存在這樣的問題。因為它關心的就是快速的寫入數據,查詢數據。雖然有一些數據的冗余,但是它的寫入和查詢速度都非常快,尤其是在處理巨量數據的時候,這個優勢特別明顯。但是如果數據集之間的耦合性非常強的話,因為要做到數據的統一,你需要不停的寫多個相關的數據集,這樣也會大大降低效率。
比拼4:擴展潛力
橫向擴展和縱向擴展的區別。
橫向擴展是指用多台服務器服務一個數據庫,這種擴展的好處就是沒有極限。這個對於結構化數據庫來說,幾乎是不可能的。非結構化數據庫就可以做到橫向擴展。
縱向擴展是指通過提高硬件性能軟件性能來提高整體服務器的性能。這種擴展的劣勢就是總會達到極限。當然這種擴展對於結構化數據庫和非結構化數據庫都是適用的。
小結:那么哪個更好呢?
說實在的,一下子很難說,是結構化數據庫更好還是非結構化數據庫更好?因為這兩種數據庫的設計初衷是不一樣的。
結構化數據庫的目標是追求數據操作的完整性,但是對單機服務器的性能要求比較高。非結構化數據庫的設計,追求的是讀寫的效率和可擴展性,可以實現多機的協作。但是又不注重數據操作的完整性。同時會產生大量的冗余數據。
選擇
目前許多大型互聯網都會選用MySql+NoSql的組合方案,因為SQL和NoSql都有各自的優缺點。
關系型數據庫適合存儲結構化數據,比如:用戶的賬號、地址:
(1)這些數據通常需要做結構化查詢,比如說Join,這個時候,關系型數據庫就要勝出一籌。
(2)這些數據的規模、增長的速度通常是可以預期的。
(3)事務性、一致性,適合存儲比較復雜的數據。
NoSql適合存儲非結構化數據,比如:文章、評論:
(1)這些數據通常用於模糊處理,例如全文搜索、機器學習,適合存儲較為簡單的數據。
(2)這些數據是海量的,並且增長的速度是難以預期的。
(3)按照key獲取數據效率很高,但是對於join或其他結構化查詢的支持就比較差。