MySQL索引总结
索引
类似于书目,用于快速检索
优点:
提高数据检索效率;
提高表间的join效率;
利用唯一性索引,保证数据的一致性;
提高排序和分组效率;
缺点:
消耗更多的物理存储;
数据变更时,索引也需要更新,降低更新效率
二叉树、B数、B+数、hash索引
二叉树
二叉树的每个节点至多只有两颗子树,二叉树的子树有左右有序之分,次序不能颠倒
不适合做数据库索引:
1、当数据量大的时候,树的高度会比较高,查询会比较慢;
2、每个节点只存储一个记录,可能导致一次查询有很多次磁盘io;
B树
![](https://img2018.cnblogs.com/blog/1063516/201906/1063516-20190610225012160-843483246.png)
B+树是B树的变体, 仍是多叉搜索树,在B树的基础上,做了一些改进:
1、非叶子节点不再存储数据,数据只存储在同一层的叶子节点上;
2、叶子之间,增加了链表,获取所有节点,不再需要中序遍历;
3、非叶子节点,不存储实际记录,而只存储记录的KEY的话,那么在相同内存的情况下,B+树能够存储更多索引;
4、在MySQL中,为了方便,直接写成BTREE
哈希索引:
建立在哈希表的基础上,它只对使用了索引中的每个值的精确查找有用
对于每一行,存储引擎计算出了被索引的哈希码,他是一个较小的值,并且有可能和其他行的哈希码相同
把哈希码保存在索引中,并且保证了一个指向哈希表中的每一行的指针
![](https://img2018.cnblogs.com/blog/1063516/201906/1063516-20190610225048386-146676851.png)
B+树索引和哈希索引的比较
大量唯一值的等值查询,哈希索引效率通常比B+tree高
hash索引不支持模糊查找
hash索引不支持联合索引中的最左匹配原则
hash索引不支持排序
hash索引不支持范围查询
hash索引只能显示应用于memory、NDB表
索引使用建议
1、经常检索的列;
2、经常用于表连接的列;
3、经常排序、分组的列;
索引不使用建议
1、基数很低的列;
2、更新频繁但检索不频繁的列;
3、BLOB、TEXT等长内容列;
4、很少用于检索的列;
数据库设计索引的原因
1、用于提升数据库的查找速度
2、提高聚合函数效率
3、提高排序效率,order by asc、desc
4、有时可以避免回表
5、减少多表关联时扫描行数
6、列定义为default null 时,null值也会有索引,存放在索引数的最前端部分,因此尽量不要定义允许null
聚集索引
innodb表,只能够有一个,因为数据行在物理磁盘上只能有一份聚集存储。
innodb中,表即聚集索引,聚集索引即表
mysiam没有聚集索引的概念
聚集索引优先选择的列:
1、int/bigint
2、数据连续(单调)递增或自增
不建议的聚集索引:
1、修改频繁的列;
2、新增数据太过离散随机
不管innodb有没有主键,它都会有聚集索引,因为innodb是基于聚集索引的索引组织表
主键一定是聚集索引,聚集索引不一定是主键
主键索引
innodb的主键索引与行记录是存储在一起的,故叫做聚集索引
因为这个特性,innodb的表必须要有聚集索引:
(1)如果表定义了PK,则PK就是聚集索引;
(2)如果表没有定义PK,则第一个非空unique列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
主键由表中的一个或多个字段组成,它的值用于唯一的标识表中的某一条记录
create table row_id(a int not null,b int null,c int not null,d int not null,unique key(b),unique key(d),unique key(c));
insert into row_id select 1,2,3,4;
insert into row_id select 5,6,7,8
insert into row_id select 9,10,11,12;
select a,b,c,d,_rowid from row_id;
另外_rowid只能查看单列为主键的情况,对于多列组成的主键就显得无能为力了
作用:
1、保证数据的完整性;
2、加快数据的操作速度;
3、主键值不能重复,也不能包含null;
主键选择建议:
对业务无意义,不必受限于业务变化的影响;
很少修改和删除;一般都是自增的
不建议使用较长的列做主键,例如char(64),因为所有的普通索引都会存储主键,会导致普通索引过于庞大;
建议使用趋势递增的key做主键,由于数据行与索引一体,这样不至于插入记录时,有大量索引分裂,行记录移动;
例如:select * from t where name='lisi';
会先通过name辅助索引定位到B+树的叶子节点得到id=5,再通过聚集索引定位到行记录。所以,其实扫了2遍索引树。
innodb主键特点:
定义索引时,不管有无显示包含主键,实际都会存储主键值;
在MySQL5.6.9版本后,优化器已能自动识别索引末尾的主键值,之前版本则需要显示加上主键列才可以被识别(经过测试,老版本也支持此特征)
例如:
仍然遵循最左前缀原则:
唯一索引
不允许具有索引值相同的行,从而禁止重复的索引或键值(在唯一约束上,和主键一样)
唯一索引允许有空值(NULL);
一个表只能有一个主键,但可以有多个唯一索引;
innodb表中主键必须是唯一索引,但唯一索引不一定是主键;
联合索引
多列组成的索引
适合where条件中的多列组合
可以避免回表(覆盖索引)
支持多列不同的排序规则(8.0开始支持倒序索引)
联合索引建议:
where条件中,经常同时出现的列放在联合索引中;
把选择性大的列放在联合索引最左边
覆盖索引
通过索引数据结构即可完成查询返回数据,不需要回表
执行计划中,Extra为关键字 using index;
desc select name from t1 where name like '%zyq%';
前缀索引
使用的原因:
char、varcahr列太长,全部创建索引的话,效率太差,存在浪费;
或者blob、text类型不能整列作为索引列,因此需要使用前缀索引
部分索引选择建议:
1、统计平均值;
2、满足10%-30%的覆盖度就可以
缺点:
无法利用前缀索引完成排序
与全部索引对比:
索引的最大长度767bytes启用innodb_lagrge_prefix,增加到3072bytes,只针对dynamic,compressed格式管用对于redundant、compact格式,最大索引长度还是767bytesmysiam表索引最大长度是1000bytes最大排序长度默认是1024(max_sort_length)
Copy Table方式这是InnoDB最早支持的创建索引的方式。创建索引是通过临时表拷贝的方式实现的。1. 新建一个带有新索引的临时表。2. 然后锁原表,禁止DML操作,允许读操作。3. 将原表数据全部拷贝到临时表(无排序,一行行拷贝)。4. 然后Rename,升级字典锁,禁止读写。5. 完成创建索引的操作。这种copy方式的效率没有inplace好 ,因为copy需要记录undo和redo log,而且因为临时占用buffer pool引起短时间内性能受影响。Inplace方式这是原生MySQL 5.5,以及innodb_plugin中提供的创建索引的方式。所谓Inplace,也就是索引创建在原表上直接进行,不会拷贝临时表。1. 新建一个带有新索引的临时表。2. 然后锁原表,禁止DML操作,允许读操作。3. 读取聚集索引,构造新的索引项,排序并插入新索引。4. 然后Rename,升级字典锁,禁止读写。5. 完成创建索引的操作。可以避免重建表带来的IO和CPU消耗,保证DDL期间依然有良好的性能和并发。Inplace方式创建索引,创建过程中,原表同样可读的,但是不可写。Online方式这是MySQL5.6.7中提供的创建索引的方式。无论是CopyTable方式,还是Inplace方式,创建索引的过程中,原表只能允许读取,不可写。对应用有较大的限制,因此MySQL最新版本中,InnoDB支持了所谓的Online方式创建索引。InnoDB的Online Add Index,首先是Inplace方式创建索引,无需使用临时表。在遍历聚簇索引,收集记录并插入到新索引的过程中,原表记录可修改。而修改的记录保存在Row Log中。当聚簇索引遍历完毕,并全部插入到新索引之后,重放Row Log中的记录修改,使得新索引与聚簇索引记录达到一致状态。与Copy Table方式相比,Online Add Index采用的是Inplace方式,无需Copy Table,减少了空间开销;与此同时,Online Add Index只有在重放Row Log最后一个Block时锁表,减少了锁表的时间。与Inplace方式相比,Online Add Index吸收了Inplace方式的优势,却减少了锁表的时间。help create index;CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name[index_type]ON tbl_name (index_col_name,...)[index_option][algorithm_option | lock_option] ...index_col_name:col_name [(length)] [ASC | DESC]index_option:KEY_BLOCK_SIZE [=] value| index_type| WITH PARSER parser_name| COMMENT 'string'index_type:USING {BTREE | HASH}create index idx_c on sbtest1(c(20)) ALGORITHM=DEFAULT;create index idx_c on sbtest1(c(20)) ALGORITHM=INPLACE;写不会阻塞,会先写到缓存,然后完成之后同步数据;copy的时候会阻塞写操作create index idx_c on sbtest1(c(20)) ALGORITHM=COPY;innodb_online_alter_log_max_size如果DDL执行时间很长,期间又产生了大量的dml操作,以至于超过了innodb_online_alter_log_max_size变量所指定的大小,会引起DB_ONLINE_LOG_TOO_BIG错误。默认为128M,特别对于需要拷贝大表的alter操作,考虑临时加大该值,以此获得更大的日志缓存空间。
指定InnoDB表的联机DDL操作期间使用的临时日志文件大小的上限(以字节为单位)。每个正在创建的索引或要更改的表都有一个这样的日志文件。此日志文件存储在DDL操作期间在表中插入,更新或删除的数据。临时日志文件在需要时由innodb_sort_buffer_size的值扩展,最大为innodb_online_alter_log_max_size指定的最大值。如果临时日志文件超出大小上限,则ALTER TABLE操作将失败,并且将回滚所有未提交的并发DML操作。因此,此选项的较大值允许在联机DDL操作期间发生更多DML,但在表被锁定以应用日志中的数据时,还会延长DDL操作结束时的时间段。冗余索引
根据最左匹配原则,一个索引是另一个索引的子集;使用pt-duplicate-key-checker检查select * from sys.schema_redundant_indexes;无用索引:几乎从未被使用过的索引pt-index-usage检查低利用率索引,提供删除建议select * from sys.schema_unused_indexes;全表扫描select * from sys.schema_tables_with_full_table_scans;使用索引1、让MySQL自动选择select ... from t where ...2、建议选择:select .. from t use index(idx_name) where ...3、强制索引select ... from t force index(idex_name) where...索引统计表统计信息
show table status ;select * from information_schema.tables;select * from mysql.innodb_table_stats;索引统计信息show index from table;select * from information_schema.STATISTICS;select * from mysql.innodb_index_stats;
innodb_stats_auto_recalc默认开启,当修改数据量大于10%,自动更新统计信息;innodb_stats_persistent默认开启,统计信息持久化存储;当关闭时,统计信息不持久化,每次动态采集,存储在内存中,重启实例(需要重新统计),不推荐innodb_stats_persistent_sample_pages统计信息持久化存储时,默认每次采集20个page页innodb_stats_on_metadata默认禁用,访问元数据时更新统计信息;iinnodb_stats_transient_sample_pages动态采集page,默认8个MySQL -A登录不会去更新统计信息不接-A的话当表或者分区表比较多的时候登录会比较慢use database是也需要更新统计信息,所以有时候很慢执行计划typeALL 扫描全表数据index 遍历索引range 索引范围查找ref 使用非唯一索引查找数据fulltext 使用全文索引const 使用主键或者唯一索引,且匹配的结果只有一条记录。system const 连接类型的特例,查询的表为系统表。possible_keys可能使用的索引,但不一定会使用。查询涉及到的字段上若存在索引,则该索引将被列出来。当该列为NULL时就需考虑当前的SQL是否需要优化。key显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。查询中若使用了覆盖索引(覆盖索引:索引的数据覆盖了需要查询的所有数据),则该索引仅出现在key列表中extraextra的信息非常丰富,常见的有: 1.Using index 使用覆盖索引 2.Using where 使用了用where子句来过滤结果集 3.Using filesort 使用文件排序,使用非索引列进行排序时出现,非常消耗性能,尽量优化。 4.Using temporary 使用了临时表索引不可用的情况1、通过索引扫描的记录数超过20%-30%,可能会变成全表扫描;
2、联合索引中,第一个索引列使用范围查询;(这时用的部分索引)3、联合索引中,第一个查询不是最左索引列;4、模糊查询条件列最左以通配符%开始(覆盖索引除外);5、两个独立索引,其中一个用于检索,一个用于排序(只能用到一个索引)6、join查询时,关联列数据类型(以及字符集)不一致也会导致索引不可用隐式类型转换u1='123' 不会转换u1='a' 可以正常走索引联合索引
desc select * from t7 where name='zyq';desc select * from t7 where c='abc';![]()
desc select * from t7 where c='zyq' and pad='sdfafadfasfdaf';
desc select * from t7 where c='abc' and name='zyq';