explain
使用explain
用於分析sql語句的性能。案例分析:
mysql> show create table employees;
CREATE TABLE `employees` (
`id` int(11) NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) DEFAULT NULL,
`hire_date` date NOT NULL,
`card_no` char(18) NOT NULL,
PRIMARY KEY (`id`),
KEY `first_name` (`first_name`),
KEY `card_no` (`card_no`),
KEY `first_name2` (`first_name`,`last_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
正常格式
mysql> EXPLAIN SELECT * FROM employees WHERE id = 1;
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | employees | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
JSON格式
mysql> EXPLAIN FORMAT=JSON SELECT * FROM employees WHERE id = 1;
{
"query_block": {
"select_id": 1,
"cost_info": {
"query_cost": "1.00"
},
"table": {
"table_name": "employees",
"access_type": "const",
"possible_keys": [
"PRIMARY"
],
"key": "PRIMARY",
"used_key_parts": [
"id"
],
"key_length": "4",
"ref": [
"const"
],
"rows_examined_per_scan": 1,
"rows_produced_per_join": 1,
"filtered": "100.00",
"cost_info": {
"read_cost": "0.00",
"eval_cost": "0.20",
"prefix_cost": "0.00",
"data_read_per_join": "160"
},
"used_columns": [
"id",
"first_name",
"last_name",
"hire_date",
"card_no"
]
}
}
}
輸出對照表
以下數據來自官網:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
Column | JSON Name | Meaning |
---|---|---|
id | select_id | The SELECT identifier |
select_type | None | The SELECT type |
table | table_name | The table for the output row |
partitions | partitions | The matching partitions |
type | access_type | The join type |
possible_keys | possible_keys | The possible indexes to choose |
key | key | The index actually chosen |
key_len | key_length | The length of the chosen key |
ref | ref | The columns compared to the index |
rows | rows | Estimate of rows to be examined |
filtered | filtered | Percentage of rows filtered by table condition |
Extra | None | Additional information |
JSON properties which are NULL are not displayed in JSON-formatted EXPLAIN output.
type(JSON Name: access_type)
當前查詢的連接類型。性能從最佳到最差
- system:當前表中只有一條,const中的一種特例
- const:主鍵或唯一索引
- eq_ref:兩表通過主鍵或唯一鍵關聯
-- 官網案例
SELECT * FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
-- 注意細節:結果項需要有索引纔是eq_ref,否則是ALL
SELECT ref_table.key_column FROM ref_table,other_table
WHERE ref_table.key_column=other_table.column;
- ref:使用非唯一索引掃描
- fulltext:全文索引
- ref_or_null:使用非唯一索引或null掃描
- index_merge:合併索引
- unique_subquery:
- index_subquery
- range
- index
- ALL
MySql自帶的全文索引只能用於數據庫引擎爲MYISAM的數據表,如果是其他數據引擎,則全文索引不會生效。
possible_keys(JSON Name: possible_keys)
可能使用的索引。
出現possible_keys有值,key顯示NULL的情況,這種情況是因爲表中數據不多,mysql認爲索引對此查詢幫助不大,選擇了全表查詢。要查看錶具有哪些索引,請使用。 SHOW INDEX FROM tbl_name
key(JSON Name:key)
當前查詢使用的索引。
key_len(JSON Name: key_length)
索引長度。
字段允許爲NULL加1字節,變長數據類型加2字節。
- 字符串
長度和字符集有關,gbk(一個字符=2個字節),utf8(一個字符=3個字節),utf8mb4(一個字符=4個字節)
char(n):(2n/3n/4n)[+1] 字節
varchar(n):(2n/3n/n4)+2[+1] 字節
- 數值類型
tinyint:1字節
smallint:2字節
int:4字節
bigint:8字節
- 時間類型
date:3字節
timestamp:4字節
datetime:8字節
索引最大長度是768字節,當字符串過長時,mysql會做一個類似左前綴索引的處理,將前半 部分的字符提取出來做索引。
ref(JSON Name: ref)
在key列記錄的索引中,表查找值所用到的列或常量,常見的有:const,字段名
rows(JSON Name: rows)
MySQL認爲執行查詢必須檢查的行數。
filtered(JSON Name: filtered)
該filtered列指示將被表條件過濾的錶行的估計百分比。
最大值爲100,這表示未過濾行。值從100減小表示過濾量增加。 rows顯示檢查的估計行數,rows × filtered顯示將與下表連接的行數。例如,如果 rows爲1000且 filtered爲50.00(50%),則與下表連接的行數爲1000×50%= 500。
Extra (JSON Name:無)
這一列展示的是額外信息。常見的重要值如下:
- Using index:使用覆蓋索引
- Using where:使用where語句來處理結果,查詢的列未被索引覆蓋
- Using index condition:查詢的列不完全被索引覆蓋,where條件中是一個前導列的範圍;
- Using temporary:mysql需要創建一張臨時表來處理查詢。
出現這種情況一般是要進行優化的,首先是想到用索引來優化。
- Using filesort:將用外部排序而不是索引排序,數據較小時從內存排序,否則需要在磁盤 完成排序。
這種情況下一般也是要考慮使用索引來優化的。
- Select tables optimized away:使用某些聚合函數(比如max、min)來訪問存在索引的某個字段
索引
關於索引介紹的內容參考博客: https://www.cnblogs.com/yuanrw/p/10225659.html
數據結構
B+Tree
下圖爲B+Tree存儲的數據結構圖:
- 非葉子節點不存放數據,只有葉子節點存放數據
- 每個葉子節點有一個指針指向下一個節點,把所有的葉子節點串在了一起
BTree
B+樹和B樹的區別是:
- B樹的節點中沒有重複元素,B+樹有。
- B樹的中間節點會存儲數據指針信息,而B+樹只有葉子節點才存儲。
- B+樹的每個葉子節點有一個指針指向下一個節點,把所有的葉子節點串在了一起。
哈希
哈希索引,只有精確匹配索引所有列的查詢纔有效。對於每一行數據,存儲引擎都會對所有的索引列計算一個哈希碼。哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。如果多個列的哈希值相同,索引會以鏈表的方式存放多個指針記錄到同一個哈希條目中。
因爲索引自身只存儲對應的哈希值,所以索引的結構十分緊湊,哈希索引查找的速度非常快。但是哈希索引也有它的限制:
- 哈希索引不是按照索引順序存儲的,無法用於排序。
- 不支持部分索引列匹配查找。
- 不支持範圍查找。
索引類型
以下內容來自CSDN-傑哥一號號-mysql中innodb和myisam對比及索引原理區別
InnoDB索引實現
CREATE TABLE `employees` (
`Col1` int(11) NOT NULL,
`Col2` int(11) NOT NULL,
`Col3` varchar(16) NOT NULL,
PRIMARY KEY (`Col1`),
KEY `Col3` (`Col3`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8
主鍵索引(聚簇索引)
二級索引(輔助索引)
索引選擇技巧
- 它應該是UNIQUE和NOT NULL
- 選擇最小的可能鍵,因爲所有二級索引都會存儲主鍵。如果主鍵很大,整個索引也會佔用更多的空間
- 選擇一個單調遞增的值。物理行是根據主鍵進行排序的。如果選擇一個隨機鍵,需要做多次的行重排,這會導致新跟那個下降。
- 最好選擇一個主鍵。如果找不到任務主鍵,請添加一個AUTO_INCREMENT列。如果你不選擇任何內容,InnoDB會在內部生成一個帶有6字節行ID的隱藏主鍵索引。
MyISAM索引實現(選讀)
CREATE TABLE `employees` (
`Col1` int(11) NOT NULL,
`Col2` int(11) NOT NULL,
`Col3` varchar(16) NOT NULL,
PRIMARY KEY (`Col1`),
KEY `Col2` (`Col2`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8
主鍵索引(聚簇索引)
二級索引(輔助索引)
數據類型
表佔用的存儲空間越小,有以下三個優點:
- 向磁盤寫入或讀取的數據就越少,查詢起來就越快
- 在處理查詢時,磁盤上的內容會被加載到主內存中。所以,表越小,佔用的主存空間就越小
- 被索引佔用的空間就越小
建議操作
- 如果要存儲員工編號,而其可能的最大值爲500000,則最佳數據類型爲mediumint(3個字節),如果將它存儲爲4個字節的int類型,則每一行都浪費了一個字節。
- 如果要存儲員工姓名,由於其長度不等,可能的最大長度爲20個字符,最好將其聲明爲varchar(20)類型。如果講員工姓名定義爲char(20)類型,但是隻有幾個人的姓名長爲20個字符,其餘的長度不到10個字符,就會浪費10個字符的空間。
- 在聲明varchar類型時,要考慮其長度。儘快varchar類型在磁盤上進行了優化,但這個類型的數據被加載到內存時會佔用全部的長度空間。
例如:將col存儲在類型爲varchar(255)的字段中,並且實際長度爲10,則它在磁盤上佔用10+1(用於存儲長度)個字節,但是在內存中會佔用全部的255個字節。
- 類型爲varchar的字段,需要用2個字節來存儲內容的實際長度。
在磁盤中如果實際長度小於255則使用1個字節來存放長度,超過255則使用2個字節。
- 如果不允許爲空,則設置爲NOT NULL。這樣做可以避免了測試每個值是否爲空的開銷,並且還節省了存儲空間-每列能節省一個字節。
- 如果字符串的長度是固定的,請存儲爲char而非varchar類型(因爲varchar需要一個或兩個字節來存儲字符串的長度)
- 如果這些值是固定的,則使用ENUM而非varchar類型。只需要使用1或2個字節
- 優先選擇整數類型而非字符串類型
- 優先利用前綴索引
10.嘗試利用InnoDB壓縮
警告排查
使用 SHOW WARNINGS;