MySQL索引
索引类型
数据结构
B+树索引,用于InnoDB & MyISAM
哈希索引,用于HEAP,NDB,InnoDB AHI
Fractal Tree索引,用于TokuDB;LSM,用于RocksDB;空间索引,很少用
物理存储
聚集索引(clustered index)
非聚集索引(non-clustered index)
逻辑角度
主键索引(PRIMARY KEY)
辅助索引(SECONDARY KEY)
唯一索引/非唯一索引
联合索引/单列索引:联合索引最多16列
覆盖索引
前缀索引:前缀索引的过滤性、对覆盖索引的影响
案例:怎么给字符串的字段加索引
索引特点
MyISAM:主键和辅助索引都是B+树的数据结构,只有是否唯一的区别(主键和唯一索引有唯一属性,其他辅助索引没有唯一属性。B+树叶子节点存储的都是指向行记录的row pointer)
InnoDB:主键和辅助索引都是B+树的数据结构,但叶子节点存储的键值不一样。主键的叶子节点存储整行数据,而辅助索引的叶子节点存储的是主键的键值(index extensions)
MyISAM只有索引buffer,InnoDB有数据和索引buffer
InnoDB索引长度
索引最大长度(字节)
767:row_format=REDUNDANT或COMPACT
3072:row_format=DYNAMIC或COMPRESSED,且innodb_large_prefix=1,且innodb_file_format=Barracuda
最大排序长度(字节):max_sort_length
排序算法
select 返回列的字节总长度<=max_length_for_sort_data:全字段排序,所有返回列放入sort_buffer
select 返回列的字节总长度>max_length_for_sort_data:rowid排序,order by列+主键放入sort_buffer
案例:order by是怎么工作的
索引管理
创建、删除、改名
建议/强制/忽略索引:use/force/ignore index(idx1,idx2)
索引使用率:pt-index-usage/sys.schema_index_statistics
冗余索引:pt-duplicate-key-checker/sys.schema_redundant_indexes
无用索引:pt-index-usage/sys.schema_unused_indexes
统计信息
表统计信息:show table status/information_schema.tables/mysql.innodb_table_stats
索引统计信息:show index from table/information_schema.statistics/mysql.innodb_index_stats
更新
innodb_stats_persistent=1、innodb_stats_persistent_sample_pages:默认启用,统计信息持久化存储,每次每个索引采集20个page
innodb_stats_persistent=0、innodb_stats_transient_sample_pages:统计信息不持久化存储,重启失效,每次每个索引采集8个page
innodb_stats_auto_recalc:默认启用,持久化下修改数据量>10%时,自动更新统计信息;非持久化下修改数据量>1/16时,自动更新统计信息
innodb_stats_on_metadata:默认禁用,访问metadate时是否更新统计信息
innodb_stats_method:控制统计信息对索引中NULL值的算法(nulls_equal、nulls_unequal、nulls_ignored)
动态/非持久化更新统计信息:重启、修改量超过1/16、analyze table、访问元数据、--auto-rehash、表第一次访问
单表设定统计模式:create/alter table...stats_persistent=1,stats_auto_recalc=1,stats_sample_pages=200;
ANALYZE TABLE
更新索引统计信息
代价估算:n_sample * (n_cols_in_uniq_i + n_cols_in_non_uniq_i + n_cols_in_pk * (1 + n_non_uniq_i)) * n_part
案例:MySQL为什么有时候会选错索引
索引使用注意事项
什么情况下索引会"失效"
条件字段函数操作
隐式类型转换
隐式字符编码转换
字段定义允许为NULL对索引有什么影响
索引查找、统计、值比较,会更加复杂
在B+树里,所有null值放在最左边,增加搜索代价
索引的每一行要增加一个字节存储
索引最佳实践
mysqld进程消耗CPU长时间过高,99.9%是因为没用好索引
show processlist用户线程状态经常看到Sending data,也基本上是因为索引不当导致
避坑
所有主要列都创建单独索引
长varchar列创建索引
基数低的列单独创建索引,或者放在联合索引的最左边
相关参数优化
sort-buffer-size/join-buffer-size/read-rnd-buffer-size,4~8MB
optimizer_switch="index_condition_pushdown=on,mrr=on,mrr_cost_based=off,batched_key_access=on,use_index_extensions=on"
tmp-table-size=max-heap-table-size,100MB
案例分享
index key、index filter、table filter
调优手段/工具
desc/explain
type
all:全表扫描,最糟糕的情况
index:全索引扫描,大部分情况下,一样糟糕
key_len
只计算index key的索引列长度,不包括用于group by/order by的索引列长度
等于索引列字节长度;字符串类型需考虑字符集(utf8mb4=4,utf8=3,gbk=2,latin1=1);日期时间型需考虑精度值;\n若允许NULL,再+1;变长类型(VARCHAR),再+2
extra
Using filesort:没有办法利用现有索引进行排序,需要额外排序
Using temporary:需要用临时表存储结果集,通常是因为group by的列上没有索引。也有可能是因为同时有group by\n和order by,但group by和order by的列不一样
filesort仅用于单表排序,若多表join时有排序,且排序的列不是驱动表的索引列,则走temporary,再基于temptable进行filesort
profiling
flush status
optimizer_trace
案例:交易平台慢查询20190516
MySQL8.0索引新特性
倒序索引(Descending Indexes)
不可见索引(Invisible Indexes)
函数索引
索引跳跃扫描(Index skip scan)