1.索引的簡介
正確合理的利用索引是提升數據庫查詢性能的方式之一,索引就想目錄一樣,能幫我們快速的定位數據,mysql索引種類有B樹索引,hash索引,全文索引,空間索引等。一般我們只重點關注B樹索引。
一般從幾個方面評價索引的優劣,
1星索引:可以通過索引掃描數據
2星索引:在1星的基礎上,可以使用覆蓋索引,無需再回表查詢
3星索引:在二星的基礎上,可以利用索引完成排序。
mysql索引的原理:點擊查看,我們這裏不再複述。
https://blog.csdn.net/lucky_ly/category_7563195.html
2.索引的創建
先看索引的sql定義語句如下,可以在表定義時,創建也可以在後來手動添加。
2.1 創建表時指定索引
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
(create_definition,...)
[table_options]
[partition_options]
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
[(create_definition,...)]
[table_options]
[partition_options]
[IGNORE | REPLACE]
[AS] query_expression
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
{ LIKE old_tbl_name | (LIKE old_tbl_name) }
create_definition:
col_name column_definition
| {INDEX|KEY} [index_name] [index_type] (key_part,...)
[index_option] ...
| {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] PRIMARY KEY
[index_type] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
[index_name] [index_type] (key_part,...)
[index_option] ...
| [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name,...)
reference_definition
| CHECK (expr)
示例如下:
CREATE TABLE `film` (
`film_id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`description` text,
`release_year` year(4) DEFAULT NULL,
`language_id` tinyint(3) unsigned NOT NULL,
`original_language_id` tinyint(3) unsigned DEFAULT NULL,
`rental_duration` tinyint(3) unsigned NOT NULL DEFAULT '3',
`rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
`length` smallint(5) unsigned DEFAULT NULL,
`replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
`rating` enum('G','PG','PG-13','R','NC-17') DEFAULT 'G',
`special_features` set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`film_id`),
KEY `idx_title` (`title`),
KEY `idx_fk_language_id` (`language_id`),
KEY `idx_fk_original_language_id` (`original_language_id`),
KEY `idx_film_length` (`length`),
CONSTRAINT `fk_film_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `fk_film_language_original` FOREIGN KEY (`original_language_id`) REFERENCES `language` (`language_id`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8;
key和index都可以定義索引,index_type可選btree和hash索引,默認是Btree索引,
2.2 直接創建索引
CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
[index_type]
ON tbl_name (key_part,...)
[index_option]
[algorithm_option | lock_option] ...
key_part:
col_name [(length)] [ASC | DESC]
index_option:
KEY_BLOCK_SIZE [=] value
| index_type
| WITH PARSER parser_name
| COMMENT 'string'
index_type:
USING {BTREE | HASH}
algorithm_option:
ALGORITHM [=] {DEFAULT | INPLACE | COPY}
lock_option:
LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}
示例:
create index idx_film_length on film(length desc);
一般索引名以idx開頭,下劃線分割,然後解表名,和列名。
2.3 查看錶的索引信息:
show index from 表名
3. 索引優化
1 innodb會爲主鍵自動創建聚簇索引,主鍵最好選擇自增長id,或遞增的,有序的區分度較高的鍵。否則可能在插入數據時,會導致索引頻繁的裂變,如果鍵值有序的,數據可以直接追加在後頭,如果鍵無序,例如使用uuid當主鍵,要會先找到正確索引位置,在插入數據,中間會導致數據的移動,嚴重影響性能。而且會產生大量的碎片。
2. 在邏輯上符合的話,儘量創建唯一索引。
3. 表的連接列和一些區分度較高的where條件列可以創建索引。
4. 索引列不能用是表達式的一部分,不能是函數的參數,
5. like模糊匹配時,開頭不能是%,例如, select * from film where title like %toutiao%
6. 不要爲盲目的爲多個列創建單個索引,當有多個列時,可以創建多列索引,列的區分度儘量由高到低,多列索引的遵循最左匹配原則。
例如有學生表如下
CREATE TABLE `student` (
`s_id` int(11) NOT NULL AUTO_INCREMENT,
`sno` int(11) DEFAULT NULL,
`sname` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`sex` char(1) DEFAULT NULL,
`remark` varchar(500) DEFAULT NULL,
PRIMARY KEY (`s_id`),
UNIQUE KEY `uk_sno` (`sno`)
) ENGINE=InnoDB AUTO_INCREMENT=1DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
如果我們需要根據sname,age,sex條件去查詢,我們可以在此基礎上創建一個多列索引。
create index idx_student_sname_age_sex on student(sname,age,sex);
遵循最左匹配原則
1.使用左側第一列可以走索引
2.使用左側第一列和左側第二列也可以使用索引
3.使用第二列或第三列,即時在數據區分度很高的情況下,也只能走全表掃描
7.當爲字符類型的字段建立索引時,如果區分度合適的情況下,可以創建前綴索引,這樣可以減少索引佔用的空間,但是創建了前綴索引,就無法使用覆蓋索引了,使用時需要權衡利弊。
前綴索引的創建方式
如下爲sname創建一個前綴索引,保留前綴5個字符,
create index idx_student_sname on student(sname(5));
如何知道多少個保留多少個字符,區分度才較爲合適呢,我們比較該字段去重之後佔總數據的百分比和保留n個字符前綴佔總數據的百分比,
如下,保留到10字符時,比值和原來相等,基本就可以了。
對於text,blob數據類型,如果使用b-tree索引的話,必須指定前綴索引,對於唯一索引來所,前綴索引可能會引起報錯,因爲保留前綴可能無法區別唯一性。
8.利用索引字段的有序性,避免排序。這可以極大的提高性能。
對於多個排序字段排序時,如果要在這些字段上有組合索引也要遵循最左匹配原則,
例如,我們有一張學生表,有(birth_date,age,sex)組合索引
select * from student where birth_date = '2020-01-05'
order by age,sex
前導列出現在where條件,也可以利用索引排序。
9.避免多個索引列的範圍查詢,mysql無法同時利用2個索引列進行範圍查詢。
4.總結
正確的使用索引是提高數據庫的性能的方式之一,但是是否決定走索引還是走全表,還是由優化器決定的,現在的主流數據庫都是使用基於成本的優化器,理解數據庫的原理,才能寫出最優的sql。