mysql系列之索引
mysql系列之索引
索引相关数据结构
Mysql 使用B+树。
索引的作用是为了快速检索/查找。
与查找相关的数据结构:
- 散列表
- 二叉查找树以及衍生树结构
散列表
select * from user where id=7; |
针对上述查找需求可以满足,hashmap 有数据碰撞问题(链地址/ 红黑树)
select * from user where id>3; |
但针对范围查找,不适用
二叉查找树以及衍生树结构
BSF时间复杂度\(O(lgn)\) ,检索效率较高,另外针对范围查询,可以先找到起始点然后查找右子树知道终止点。
二叉树问题: 极端情况-斜树,退化为O(N).
改进: AVL (相对于红黑树,树保持平衡旋转)和 红黑树(非严格平衡)
问题 (可认为树的高度就等于每次查询数据时磁盘 IO 操作的次数。)- 磁盘IO,每个树节点只存储一个数据,树高较高需要多次IO。
磁盘IO一个特点: 一次读取1B和一次读取1KB数据消耗的资源差别不大,因此需要尽量在一个树节点多存储数据。
磁盘读取数据靠的是机械运动,每次读取数据花费的时间包含:寻道时间、旋转延迟时间、传输时间3部分。
B树,B+树
区别
第一,B 树一个节点里存的是数据,而 B+树存储的是索引(地址),所以 B 树里一个节点存不了很多个数据,但是 B+树一个节点能存很多索引,B+树叶子节点存所有的数据。
第二,B+树的叶子节点是数据阶段用了一个链表串联起来,便于范围查找。
聚簇索引和非聚簇索引
MYSQL 索引是在存储引擎层实现,
非聚簇索引: 数据和索引分开 (MyISAM)

聚簇索引: 数据和索引一块存储(Innodb)
主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。
例如: userinfo根据主键ID做做key建立索引,执行select * from userinfo where id=15
, 查询主索引B+树。只有主键索引会存储具体数据,辅助索引存储主键的引用。 - 节省存储空间,带来的问题要到主键索引B+树查找真正的数据。 user_name 添加索引:
因此,我们在应用中应该尽量使用主键查询。
一般B+树根都会存储再内存(很大概率第二层也在内存)。 比如N为1200,当树高为4可以存储10亿行数据,因此IO次数大幅度减少。
索引维护
写入和删除需要更新索引树,可能需要页分裂。除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,整体空间利用率降低大约 50%。
覆盖索引
辅助索引回表问题
|

select * from T where k between 3 and 5
执行过程:在 k 索引树上找到 k=3 的记录,取得 ID = 300;再到 ID 索引树查到 ID=300 对应的 R3;在 k 索引树取下一个值 k=5,取得 ID=500;再回到 ID 索引树查到 ID=500 对应的 R4;在 k 索引树取下一个值 k=6,不满足条件,循环结束。
select ID from T where k between 3 and 5, 因为ID已经在K索引树,可直接提供结果。
盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
联合索引
create table t ( |

select * from t where a =? and b =?
可以使用该联合索引
针对单个a查询,也可以
针对单个b查询,无法使用,因为叶子节点b并不是排好序的
例: buy_log table : userId, buyDate , 索引(userid) + 联合索引(userid,buydate)
select * from buy_log where userid=2

两个索引可以使用,最终优化器选择userid, 因为该索引叶子简单包含单个键值(空间)
select * from buy_log where userid=1 order by buy_date desc limit 3
因为联合索引在同一个userid情况下buydate已经排好序了,无需额外排序。
如果强制使用userid索引,extra显示using filesort, 即需要额外排序。

|
如果有根据身份证号查询市民信息的需求,我们只要在身份证号字段上建立索引就够了
身份证号、姓名)的联合索引:
如果有一个高频请求,要根据市民的身份证号查询他的姓名
最左前缀原则
B+ 树这种索引结构,可以利用索引的“最左前缀”,来定位记录。
如果查询所有名字是“张三”的人时,可以快速定位到 ID4,然后向后遍历得到所有需要的结果。
如果要查的是所有名字第一个字是“张”的人,你的 SQL 语句的条件是"where name like ‘张 %’"。这时,你也能够用上这个索引,查找到第一个符合条件的记录是 ID3,然后向后遍历,直到不满足条件为止。
最左前缀可以是联合索引的最左 N 个字段,也可以是字符串索引的最左 M 个字符。
在建立联合索引的时候,如何安排索引内的字段顺序?
评估标准是,索引的复用能力: 如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑采用的。
如果既有联合查询,又有基于 a、b 各自的查询呢?查询条件里面只有 b 的语句,是无法使用 (a,b) 这个联合索引的,这时候你不得不维护另外一个索引,也就是说你需要同时维护 (a,b)、(b) 这两个索引。
比如上面这个市民表的情况,name 字段是比 age 字段大的 ,那我就建议你创建一个(name,age) 的联合索引和一个 (age) 的单字段索引.
索引下推
如果现在有一个需求:检索出表中“名字第一个字是张,而且年龄是 10 岁的所有男孩”。
mysql> select * from tuser where name like '张%' and age=10 and ismale=1;
前缀索引规则, 语句在搜索索引树的时候,只能用 “张”,找到第一个满足条件的记录 ID3。
MySQL 5.6 之前,只能从 ID3 开始一个个回表。到主键索引上找出数据行,再对比字段值。MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
Cadinality
B+ 索引针对访问很少一部分数据时 ,并且离散度高才有意义。
eg, 性别, 取值只有M、F, 那么SQL得到的结果是表的50%,B+索引没有必要。
如何确定索引是否有高选择性? - Cadinality
Cardinality/row_in_table 应尽可能趋近1,如果非常小,可以考虑删除索引。
Cadinality 统计
如果每次索引更新都统计cadinality ,数据库负担重。另外大表统计慢。
采样 - 发生在insert 和 update, 策略:
- 表中1/6 数据发生过变化
- stat_modified_counter (mysql innodb引擎内部计数器,记录数据变化次数)超过一定阈值
根据叶子节点数,随机取得8个叶子节点,统计每个页不同记录数,计算得出。
索引相关问题
0. 什么场景适合创建索引
- 较频繁的作为查询条件的字段应该创建索引;
- 唯一性太差的字段不适合单独创建索引,即使该字段频繁作为查询条件;
- 更新非常频繁的字段不适合创建索引。
补充2: 离散度高适合索引,否则重复值太多,可能mysql优化器反而会执行全表扫描。
1. 主键的选择问题?
一般要求建表语句都要有自增主键。 结合索引维护相关知识,自增主键的插入数据模式,正符合了我们前面提到的递增插入的场景。每次插入一条新记录,都是追加操作,都不涉及到挪动其他记录,也不会触发叶子节点的分裂或页合并。
而有业务逻辑的字段做主键,则往往不容易保证有序插入,这样写数据成本相对较高。
另外业务相关的字段(例如使用身份证),可能会占用树节点更多存储空间。主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小。
2. 辅助索引回表问题
覆盖索引 章节
3. 左匹配
联合索引 章节
4. 联合索引
联合索引 章节
5. mysql中的N叉B+树,N的调整问题。
可以通过调整数据页的大小间接调整。
6. B+索引使用
OLTP Vs OLAP
在线事务系统一般只是查询数据库中一小部分数据,如根据主键查询用户信息,根据订单号查询订单详细信息,B+索引适用。而统计分析类应用,一般会需要读取大量数据。
6. 索引是否生效如何查看? 什么时候创建了索引,却没有使用。
索引选择- 查询优化器。
Explain(执行计划) : possiblekeys and key
有时候优化器不使用索引,多发生于范围查找和join
例如: select* from orderdetails where orderid>1000 and ordered < 200000
即使针对orderid 有辅助索引,优化器最终选择了PRIMARY聚集索引,也就是表扫描,因为 用户选取数据是整行,无法使用覆盖索引,如果使用辅助索引需要回表,离散IO操作,也就是如果访问数据量占比大,会选取聚集索引查找。
ForceIndex - 强制使用某个索引
index hint - use index ,只是告诉优化器可以选择哪个索引,并非强制。
Explain 参考 Post not found: mysql系列之执行计划 mysql系列之执行计划
7. 索引越多越好么?
NO
- 数据量小的表不需要建立索引,建立会增加额外的索引开销
- 不经常引用的列不要建立索引,因为不常用,即使建立了索引也没有多大意义
- 经常频繁更新的列不要建立索引,因为肯定会影响插入或更新的效率
- 数据重复且分布平均的字段,因此他建立索引就没有太大的效果(例如性别字段,只有男女,不适合建立索引)
- 数据变更需要维护索引,意味着索引越多维护成本越高。
- 更多的索引也需要更多的存储空间
https://zhuanlan.zhihu.com/p/113917726
https://zhuanlan.zhihu.com/p/73204847
https://zhuanlan.zhihu.com/p/164666142
https://blog.csdn.net/nonage_bread/article/details/108432157