引言:MySQL之因此能成爲經典,不是沒有道理的,B+樹足矣!html
InnoDB引擎支持三種常見的索引:B+樹索引,全文索引和(自適應)哈希索引。B+樹索引是傳統意義上的索引,構造相似二叉樹,從平衡二叉樹演化而來,在InnoDB中使用較多,即通常意義上的索引構建都是B+樹,因此這裏主要介紹B+樹索引。mysql
索引是 應用程序設計和開發一個很是重要的方面。通常狀況下,索引的添加能夠提升查詢性能,但也不是索引建立得越多越好,多了也會對性能形成必定的影響。因此找到一個平衡點也是關鍵。sql
數據庫中的B+樹索引能夠分爲彙集索引(clustered index)和輔助索引(secondary index)。咱們經常使用的主鍵索引默認就是彙集索引,彙集索引只能是一個,因此其餘建立的索引都是輔助索引,輔助索引數量理論上沒限制;同時輔助索引也叫作非彙集索引。數據庫
InnoDB通常索引都是B+樹,通常樹的高度都在2-4層。從書上描述看,不論是100萬行仍是1000萬行樹的高度也在這個範圍。從B+樹的構造原理看,應該沒毛病。那麼查找一個鍵值的行最多隻需2-4次IO,即0.02-0.04秒,速度至關快。dom
上面已經講到,不論是彙集索引仍是輔助索引,它們的構造都是B+樹;B+樹還有一個特色,葉子節點存放全部索引的數據,非葉子節點存放部分數據,通常是部分索引的某個字段。這是它們的相同點,那麼他們的區別呢?除了上述說的主鍵外,還有哪些區別?工具
最主要的區別是:性能
彙集索引的葉子節點存放的是整行數據,也就是說若是有彙集索引,那麼彙集索引的整棵樹存放了全部數據。測試
而輔助索引的葉子節點存放的只是一行的部分數據,就是說只存放了非彙集索引定義的那一列或者幾列的信息。spa
固然還有其餘區別,構造的原則或者說約束等,但我想不是最重要的,在這裏就不鋪開敘述了。設計
當咱們建立一個普通的索引存在多列時,就是聯合索引。
這裏單獨拿出來說一下,是由於有個很是值得注意的事項。
例如建立以下一條索引,有三列,即聯合索引。
create index DevInfoIndex on FilesInfo (CamID, SliceStartTime, SliceStopTime);
當用單個CamID做爲條件進行查詢時,沒有問題,有用到索引。
可是若是用單個SliceStartTime做爲條件進行查詢時,用explain工具會發現根本沒有用到這個索引DevInfoIndex !
聯合索引有最左匹配原則,具體能夠分解成如下三條:
a.若是不是按照最左開始查找,沒法使用索引
b.不能跳過中間列
c.某列使用範圍查詢,後面的列不能使用索引
以上測試的屬於a類狀況。
若是咱們將上例的第二條語句的*改一下:
能夠發現此次用了索引。type再也不是ALL全表掃描了。這就是覆蓋索引。
定義就是,查詢能夠從輔助索引中得到,而不須要查詢彙集索引中的記錄。
那麼咱們在索引設計時應儘可能覆蓋咱們所需的或者常常用到的字段。而咱們查詢語句應儘可能不用*,儘可能只用咱們索引定義的字段。
熟悉了上述原理後,我準備在本地測試下在線添加索引。據書中描述MySQL從5.6開始支持在線索引添加OnlineDDL,我機器上版本5.6.27。
1. 首先咱們看下文件列表,注意大小;而當前時間3月6日的上午將近10點。
[root@localhost mysql]# pwd
/var/lib/mysql
[root@localhost mysql]# ll
總用量 197452
-rw-rw---- 1 mysql mysql 56 2月 27 13:28 auto.cnf
-rw-rw---- 1 mysql mysql 79691776 3月 6 09:40 ibdata1
-rw-rw---- 1 mysql mysql 50331648 3月 6 09:40 ib_logfile0
-rw-rw---- 1 mysql mysql 50331648 3月 2 13:20 ib_logfile1
-rw-r----- 1 mysql mysql 29436 2月 27 14:24 localhost.localdomain.err
-rw-rw---- 1 mysql mysql 5 3月 4 17:29 localhost.localdomain.pid
drwx--x--x 3 mysql mysql 4096 3月 2 13:30 mysql
srwxrwxrwx 1 mysql mysql 0 3月 4 17:29 mysql.sock
drwx------ 2 mysql mysql 4096 3月 2 11:37 NVRRecordFiles
drwx------ 2 mysql mysql 4096 2月 27 15:15 performance_schema
[root@localhost mysql]#
[root@localhost mysql]#
[root@localhost mysql]# ll NVRRecordFiles/
總用量 1376
-rw-rw---- 1 mysql mysql 8790 3月 2 11:37 BadFiles.frm
-rw-rw---- 1 mysql mysql 98304 3月 6 09:40 BadFiles.ibd
-rw-rw---- 1 mysql mysql 61 3月 2 11:37 db.opt
-rw-rw---- 1 mysql mysql 9250 3月 2 11:37 FilesInfo.frm
-rw-rw---- 1 mysql mysql 950272 3月 6 09:40 FilesInfo.ibd
.....
2. 咱們進入mysql下查測試數據庫NVRRecordFiles和測試表FilesInfo的信息。
查表的索引,除主鍵目前只有一條索引包括三列:
mysql> show index from NVRRecordFiles.FilesInfo;
+-----------+------------+--------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+--------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| FilesInfo | 0 | PRIMARY | 1 | FileId | A | 1569 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 1 | CamID | A | 60 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 2 | SliceStartTime | A | 1569 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 3 | SliceStopTime | A | 1569 | NULL | NULL | | BTREE | | |
+-----------+------------+--------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.01 sec)
mysql>
而後咱們在查下當前表的長度和索引的長度。
mysql> show table status;
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| BadFiles | InnoDB | 10 | Compact | 20 | 819 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| ContinueTransInfo | InnoDB | 10 | Compact | 0 | 0 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| FilesInfo | InnoDB | 10 | Compact | 1569 | 240 | 376832 | 0 | 196608 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
......
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
5 rows in set (0.00 sec)
mysql>
3. 發現運行沒問題,從2020-3-2到如今數據和索引長度基本沒有變化,由於我測試程序已經進入穩定運行的階段。
此時,咱們在線添加一條索引,此時約11點。
mysql> ALTER TABLE FilesInfo ADD INDEX FileNameIndex (FileStartTime, CamID, DiskID, DiskPath, NVRIP, CamID, FileType);
此次我加多了些字段。也能夠看出咱們的字段遠遠不止三個。
而後咱們查下索引,已經出現了。
mysql> show index from FilesInfo;
+-----------+------------+---------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| FilesInfo | 0 | PRIMARY | 1 | FileId | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 1 | CamID | A | 58 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 2 | SliceStartTime | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | DevInfoIndex | 3 | SliceStopTime | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 1 | FileStartTime | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 2 | DiskID | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 3 | DiskPath | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 4 | NVRIP | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 5 | CamID | A | 1508 | NULL | NULL | | BTREE | | |
| FilesInfo | 1 | FileNameIndex | 6 | FileType | A | 1508 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
10 rows in set (0.00 sec)
mysql>
咱們再來一句查詢命令:
mysql> explain select FileStartTime, DiskID from FilesInfo where FileStartTime < 1583446613;
+----+-------------+-----------+-------+---------------+---------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+-------+---------------+---------------+---------+------+------+--------------------------+
| 1 | SIMPLE | FilesInfo | range | FileNameIndex | FileNameIndex | 4 | NULL | 927 | Using where; Using index |
+----+-------------+-----------+-------+---------------+---------------+---------+------+------+--------------------------+
1 row in set (0.02 sec)
mysql>
新加的索引生效了。
那麼有多大呢?
此時約11:30
mysql> show table status;
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| BadFiles | InnoDB | 10 | Compact | 20 | 819 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| ContinueTransInfo | InnoDB | 10 | Compact | 0 | 0 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| FilesInfo | InnoDB | 10 | Compact | 1569 | 240 | 376832 | 0 | 196608 | 0 | NULL | 2020-03-06 11:04:00 | NULL | NULL | latin1_swedish_ci | NULL | | |
......
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
5 rows in set (0.00 sec)
mysql>
數據沒變化,那是能夠理解的,爲啥索引仍是沒變化呢?不是生效了嗎? 嗯。。按書中描述,先放入緩衝,再寫到文件中的。並且咱們能夠注意到,時間已經在更新了!再等等。
4. 中午午休起來,約13:05,再查:
mysql> show table status;
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
| BadFiles | InnoDB | 10 | Compact | 20 | 819 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| ContinueTransInfo | InnoDB | 10 | Compact | 0 | 0 | 16384 | 0 | 0 | 0 | NULL | 2020-03-02 11:37:59 | NULL | NULL | latin1_swedish_ci | NULL | | |
| FilesInfo | InnoDB | 10 | Compact | 1507 | 239 | 360448 | 0 | 393216 | 0 | NULL | 2020-03-06 11:04:00 | NULL | NULL | latin1_swedish_ci | NULL | | |
......
+-------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+----------------+---------+
5 rows in set (0.00 sec)
mysql>
此次咱們能夠看到,時間沒變了,添加的索引就是11:04分。而大小變了,但是,怎麼比全表還大,並且全表怎麼稍微變少了。
回答第一個問題:咱們從上面看出,新加的索引有6個字段,能夠說原來第一個輔助索引的兩倍,而196608 *3=589824,目前的393216 仍是少於這個值。固然兩個表的字段加起來小於全表的字段,可是表的存儲就不是1+1=2了,還有其餘一些信息。
回答第二個問題:全表變少,能夠看它的前兩項一個數據,1507,也是變小了,這是表示行數。也就是將近減小了60行數據。因此表也相應變小了。
這兩個問題偏偏說明了,在線索引建立是須要時間的,測試是真實的數據。
這是在線索引添加的整個過程,但願對你理解索引的原理有所幫助;有問題歡迎討論。
參考書《MySQL技術內幕InnoDB存儲引擎》(第二版)姜承堯著。
====================add on 2020.07.31====================
索引及數據類型的使用建議:
聯合索引:優於多列獨立索引
索引順序:選擇性高的在前面
覆蓋索引:Key裏面包含要查詢的數據
索引排序:索引同時知足查詢和排序
數據庫字符集使用utf8mb4;
VARCHAR按實際須要分配長度;
文本字段建議使用VARCHAR;
時間字段建議使用long;
bool字段建議使用tinyint;
枚舉字段建議使用tinyint;
交易金額建議使用long;
禁止使用「%」前導的查詢;
禁止在索引列進行數學運算,會致使索引失效;
select * from t1 where id+1 >1121 不會使用索引
select * from t1 where id >1121 - 1 會使用索引
表必須有主鍵,建議使用業務主鍵;
單張表中索引數量不超過5個;
單個索引字段數不超過5個;
字符串索引使用前綴索引,前綴長度不超過10個字符;