華子目錄
- 索引概述
- 優缺點
- 索引的原理
- 索引的設計原則
- 索引結構
- B-tree(多路平衡查找樹)
- B+tree
- Hash
- 為什么InnoDB存儲引擎選擇B+tree?
- 索引分類
- 聚集索引選取規則
- 單列索引和多列索引
- 前綴索引
- 創建索引
- 1.創建表時創建索引
- 2.在已經存在的表上創建索引
- 3.使用alter table語句創建索引
- 使用計劃查詢SQL使用索引情況(==explain==)
- 查看索引
- 刪除索引
- 案例
索引概述
- 索引(index) 是幫助MySQL高效獲取數據的數據結構(有序)。在數據之外,數據庫系統還維護著滿足特定查找算法的數據結構,這些數據結構以某種方式指向數據,這樣就可以在這些數據結構上實現高級查找算法,這種數據結構就是索引
- 無索引時,需要一條一條查找每一條數據,有索引時就不需要逐一查詢數據
- 在數據庫中用來加速對表的查詢;通過使用快速路徑訪問方法快速定位數據,減少了磁盤的I/O;與表獨立存放,但不能獨立存在,必須屬于某個表;由數據庫自動維護,表被刪除時,該表上的索引自動被刪除;
- 索引的作用類似于書的目錄,幾乎沒有一本書沒有目錄,因此幾乎沒有一張表沒有索引。
優缺點
索引的原理
就是把無序的數據變成有序的查詢
- 把創建的索引的列的內容進行排序
- 對排序結果生成倒排表
- 在倒排表內容上拼上數據地址鏈
- 在查詢的時候,先拿到倒排表內容,再取出數據地址鏈,從而拿到具體數據
索引的設計原則
為了使索引的使用效率更高,在創建索引的時候必須考慮在哪些字段上創建索引和創建什么類型的索引。
- 選擇惟一性索引
- 為經常需要排序、分組和聯合操作的字段建立索引
- 為常作為查詢條件的字段建立索引
- 限制索引的數目
- 盡量使用數據量少的索引
- 盡量使用前綴來索引
- 刪除不再使用或者很少使用的索引
索引結構
MySQL的索引是在存儲引擎層實現的,不同的存儲引擎有不同的結構,主要包含以下幾種:
索引結構 | 描述 |
---|---|
B+tree索引 | 最常見的索引類型,大部分引擎都支持B+tree索引 |
Hash索引 | 底層數據結構是用哈希表實現的,只有精確匹配索引列的查詢才有效,通常使用較少 |
R-tree(空間索引) | 空間索引是MyISAM引擎的一個特殊索引類型,主要用于地理空間數據類型,通常使用較少 |
Full-text(全文索引) | 是一種通過建立倒排索引,快速匹配文檔的方式。類似于Lucene,Solr,ES |
B-tree(多路平衡查找樹)
B+tree
Hash
為什么InnoDB存儲引擎選擇B+tree?
索引分類
分類 | 含義 | 特點 | 關鍵字 |
---|---|---|---|
主鍵索引 | 針對于表中主鍵創建的索引 | 默認自動創建,只能存在一個 | primary |
唯一索引 | 避免同一個表中某數據列中的值重復 | 可以有多個 | unique |
常規索引 | 快速定位特定數據 | 可以有多個 | 無 |
全文索引 | 全文索引查找的是文本中的關鍵字,而不是比較索引中的值 | 可以有多個 | fulltext |
在InnoDB存儲引擎中,根據索引的存儲形式,又可以分為以下兩種:
分類 | 含義 | 特點 |
---|---|---|
聚集索引 | 將數據存儲與索引放到了一塊,索引結構的葉子節點保存了完整的行數據 | 必須存在,且只有一個 |
非聚集索引(二級索引) | 將數據與索引分開存儲,索引結構的葉子節點關聯的是對應的主鍵 | 可以存在多個 |
聚集索引選取規則
- 如果存在主鍵,主鍵索引就是聚集索引
- 如果不存在主鍵,將使用第一個唯一(unique)索引作為聚集索引
- 如果沒有主鍵,也沒有合適的唯一索引,則InnoDB會自動生成一個rowid作為隱藏的聚集索引
- 第一個執行效率高:因為第一個查的是主鍵索引,只查一張表。第二個先查name字段的索引,再根據name字段的索引值進行回表查詢,查兩張表。
單列索引和多列索引
MySQL中的單列索引和多列索引都是用于提高數據庫查詢效率的工具,它們有一些不同之處。
-
單列索引:
- 單列索引是針對表中的單個列創建的索引。
- 它可以加速針對該列的查找、排序和過濾操作。
- 適用于單列條件查詢,例如:
SELECT * FROM table WHERE column = value;
- 單列索引可以包括在多列查詢中,但只有第一列索引將被用于加速查找。
- 創建單列索引的語法示例:
CREATE INDEX index_name ON table_name (column_name);
-
多列索引:
- 多列索引是針對表中多個列組合而成的索引。
- 它可以加速涉及這些列組合的查詢,例如聯合查詢或者多列條件查詢。
- 當查詢涉及到多個列時,多列索引通常比單列索引更有效。
- 多列索引的列順序非常重要,因為只有查詢中使用的列的左側前綴才會被索引所利用。
- 創建多列索引的語法示例:
CREATE INDEX index_name ON table_name (column1, column2, ...);
前綴索引
在 MySQL 中,你可以創建前綴索引來提高查詢效率。前綴索引是指只對列值的一部分進行索引,而不是整個列值。這在某些情況下可以減少索引的大小,并提高查詢性能,尤其是對于較大的列類型(如 TEXT 或 VARCHAR)。
要在 MySQL 中創建前綴索引,你可以使用以下語法:
CREATE INDEX index_name ON table_name (column_name(prefix_length));
在這里,prefix_length
是你希望索引的列值的前綴長度。以下是一個示例:
CREATE TABLE my_table (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100)
);CREATE INDEX idx_name_prefix ON my_table (name(10));
在這個示例中,我們為 name
列創建了一個前綴長度為 10 的索引。這意味著索引將僅包含 name
列值的前 10 個字符。你可以根據你的需求調整 prefix_length
的值。
請注意,使用前綴索引時需要注意選擇適當的前綴長度。如果前綴長度太短,可能會導致索引失效,而如果太長,可能會增加索引的大小并降低性能提升效果。因此,需要根據你的數據和查詢模式來選擇合適的前綴長度。
創建索引
- 創建索引是指在某個表的一列或多列上建立一個索引,以便提高對表的訪問速度。
- 創建索引有三種方式,這三種方式分別是創建表時創建索引、在已經存在的表上創建索引和使用alter table語句來創建索引。
1.創建表時創建索引
創建表的時候可以直接創建索引,這種方式最簡單、方便。其基本形式如下:
mysql> create table 表名(-> 列名 數據類型 約束,-> 列名 數據類型 約束,--------------------> 列名 數據類型 約束,-> [unique/fulltext] index 索引名(列名 [asc/desc])-> );
mysql> create table mytable(-> id int auto_increment primary key,-> name varchar(20),-> age int(3),-> phone int unique,-> unique index index_mytable_phone(phone),#創建唯一索引-> index index_mytable_name(name(3)),#創建前綴索引-> index index_mytable_age(age)#創建常規索引-> );
Query OK, 0 rows affected, 2 warnings (0.07 sec)mysql> show index from mytable;
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| mytable | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| mytable | 0 | phone | 1 | phone | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
| mytable | 0 | index_mytable_phone | 1 | phone | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
| mytable | 1 | index_mytable_name | 1 | name | A | 0 | 3 | NULL | YES | BTREE | | | YES | NULL |
| mytable | 1 | index_mytable_age | 1 | age | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
mysql> create table mytable(-> id int auto_increment primary key,-> name varchar(20),-> age int(3),-> phone int unique,-> index index_mytable_name(name(3)),-> index index_mytable_age(age)-> );
Query OK, 0 rows affected, 1 warning (0.05 sec)mysql> show index from mytable;
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| mytable | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| mytable | 0 | phone | 1 | phone | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
| mytable | 1 | index_mytable_name | 1 | name | A | 0 | 3 | NULL | YES | BTREE | | | YES | NULL |
| mytable | 1 | index_mytable_age | 1 | age | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
注:MySQL8會為主鍵和唯一和外鍵自動創建索引
2.在已經存在的表上創建索引
前提是:該表上無索引,需要手動添加索引
mysql> create [unique/fulltext] index 索引名 on 表名(列名); #創建單列索引
mysql> create [unique/fulltext] index 索引名 on 表名(列名1,列名2...); #創建多列索引
mysql> create table mytable(-> id int auto_increment primary key,-> name varchar(20)-> );mysql> create unique index index_mytable_name on mytable(name(3));mysql> show index from mytable;
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| mytable | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| mytable | 0 | index_mytable_name | 1 | name | A | 0 | 3 | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3.使用alter table語句創建索引
mysql> alter table 表名 add [unique/fulltext] index 索引名(列名);
mysql> create table mytable(-> id int auto_increment primary key,-> name varchar(20)-> );#通過添加索引的方式添加約束
mysql> alter table mytable add unique index index_mytable_name(name); mysql> show index from mytable;
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| mytable | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | YES | NULL |
| mytable | 0 | index_mytable_name | 1 | name | A | 0 | NULL | NULL | YES | BTREE | | | YES | NULL |
+---------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
使用計劃查詢SQL使用索引情況(explain)
mysql> desc stu;
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(20) | NO | | NULL | |
| age | int | NO | | NULL | |
| classid | int | NO | MUL | NULL | |
+---------+-------------+------+-----+---------+----------------+mysql> show index from stu;
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| stu | 0 | PRIMARY | 1 | id | A | 2 | NULL | NULL | | BTREE | | | YES | NULL |
| stu | 1 | stu_classid_foreign | 1 | classid | A | 2 | NULL | NULL | | BTREE | | | YES | NULL |
+-------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+mysql> select * from stu;
+-----+--------+-----+---------+
| id | name | age | classid |
+-----+--------+-----+---------+
| 101 | 小天 | 18 | 1001 |
| 102 | 小明 | 20 | 1003 |
| 103 | 小紅 | 13 | 1002 |
+-----+--------+-----+---------+mysql> explain select * from stu where id=101;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | stu | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+mysql> explain select * from stu where id=103;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | stu | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
explain分析結果的含義:id: 每一行的編號select_type: 查詢類型。這里是 SIMPLE,表示這是一個簡單的查詢。table:這是表的名字。partitions: 所使用的分區(如果有)type:連接操作的類型,ALL、index、range、 ref、eq_ref、const、system、NULL(從左到右,性能從差到好)possible_keys:可能可以利用的索引的名字Key:它顯示了MySQL實際使用的索引的名字。如果它為空(或NULL),則MySQL不使用索引。key_len:索引中被使用部分的長度,以字節計。ref:它顯示的是列的名字(或單詞“const”),MySQL將根據這些列來選擇行rows:MySQL所認為的它在找到正確的結果之前必須掃描的記錄數。顯然,這里最理想的數字就是1filtered: 表示按表的過濾條件過濾后的結果所占百分比。Extra:這里可能出現許多不同的選項,其中大多數將對查詢產生負面影響
查看索引
mysql> show index from 表名;
刪除索引
mysql> drop index 索引名 on 表名;
案例
按照下列需求,完成索引的創建
- name字段為姓名字段,該字段的值可能會重復,為該字段創建索引(創建一個常規索引)
mysql> create index index_stu_name on stu(name);
- phone手機號字段的值是非空且唯一(primary),為該字段創建唯一索引(創建一個唯一索引)
mysql> create unique index index_stu_phone on stu(phone);
- 為profession、age、status創建聯合索引(多個常規索引)
mysql> create index index_stu_profession_age_status on stu(profession,age,status);
- 為email建立合適的索引來提升查詢效率(創建一個常規索引)
mysql> create index index_stu_email on stu(email);