1.索引数据结构红黑树, Hash, B+树详解
2.索引是怎样支撑千万级表的快速查找
3.如何基于索引B+树精准建立高性能索引
4.联合索引底层数据结构又是怎样的
5.MySQL索引优化最佳实践
什么是索引?
索引是帮助MySQL高效获取数据的排好序的数据结构
索引数据结构有哪些
select * from t where t.col2 = 89;
当没有索引时,需要在磁盘中
6次IO
当创建了索引时,如右侧二叉树,只需要2次IO
右侧的二叉树的每个节点中存储的时每个记录的地址
什么是
记录
?
简单的说,就是插入的每一行数据元素
这么看来,二叉树读取元素的时间复杂度是O(log n),为什么索引内部实现是B+树,他有什么缺点
比如说,当创建的主键是col1,二叉树就会成为这样的
如果是有序的话,那么他就和没有索的时候向磁盘读取的次数是一样的
所以,没有采用这种数据结构
那
红黑树
呢,他是可以自选的,不会使其单边增长,他的使用情况时怎样的
红黑树也叫不完全平衡二叉树,如果放入的数据多了,那么树的高度会很高,查询的成本就会随树的高度增加而增加
如果进行一次千万级别的查询 那么就要向磁盘读取23次左右,差不多就10秒左右了
那么Hash表呢
只需要进行一次hash(key),就可以找到在磁盘上的内存地址只需要一次读取磁盘操作,就可以得到想要的记录,这是非常快的
既然Hash表查询操作那么快,为什么MySQL在99%的情况下使用的是B+树
这个很好理解,如果我们查找的是一个范围,Hash表就无能为力了,因为哈希表的特点就是可以快速的精确查询,但是不支持范围查询
如果是B-Tree呢?
B-Tree:
那为啥没用B树,而用了B+树?
如果查询的是一个范围,也是相当麻烦的,需要拿出后面的每个节点,没有解决查询范围的查询
那么来看看B+树 :
顺序访问指针,提高区间访问的性能
可以看出开,所有的数据在叶子节点上有一份完整的数据,叶子节点上有一份非叶子节点的冗余
.
我们看一下上面的数据结构,最开始的Hash不支持范围查询,二叉树树高很高,只有B树跟B+有的一比。
B树一个节点可以存储多个元素,相对于完全平衡二叉树整体的树高降低了,磁盘IO效率提高了。
而B+树是B树的升级版,只是把非叶子节点冗余一下,这么做的好处是为了提高范围查找的效率
。
提高了的原因也无非是会有指针指向下一个节点的叶子节点。
小结:到这里可以总结出来,Mysql选用B+树这种数据结构作为索引,可以提高查询索引时的磁盘IO效率,并且可以提高范围查询的效率,并且B+树里的元素也是有序的。
那么,一个B+树的节点中到底存多少个元素最合适你有了解过么?
大概三行B+树就可以存储2000多万个索引元素,一个节点16k,非叶子节点大概可存储1170个索引,叶子节点大概16个索引
一般2~4层,如果数据更多的话,那就分库分表
数据库的存储引擎了解吗
存储引擎:
哦? 说说MyISAM
MyISAM索引文件和数据文件是分离的(非聚集)
创建一个表时,会创建三个文件:
表结构
所有的数据行
B+树
select * from t where col1 = 49;
那么就通过三次IO就取到地址0x90,在MYD中查找记录
说说InnoDB
InnoDB索引实现(聚集):
创建一个表时,会创建两个文件:
对比两张B+树的叶子节点,你就能明白聚集与非聚集的含义
那你说说什么是聚集索引?
主键索引和数据是聚集在叶子节点上,搜索效率是高于非聚集索引的
为什么InnoDB表必须有主键,并且推荐使用整型的自增型的自增主键?
会影响整个表结构,表会分裂
为什么
非主键索引结构叶子节点存储的是主键值
?
一致性和节省存储空间
联合索引的底层存储结构是什么样子
满足最左前缀匹配原则(a,b,c)先匹配a,如果a相等,匹配b,以此类推。
其实联合索引的查找就跟查字典是一样的,先根据第一个字母查,然后再根据第二个字母查,或者只根据第一个字母查,但是不能跳过第一个字母从第二个字母开始查。这就是所谓的最左前缀原理。
详细说说最左匹配原则
比如联合索引索引的例子(a,b,c).下面的索引都可以用到:
select * from t where a=1;//(a)
select * from t where a=1 and b=2;//(a,b)
select * from t where a=1 and b=2 and c=3; // (a,b,c)
再比如说:
select * from t where a=1 and c=3;// 只会用到索引a
select * from t where b=1 and c=2;// 没有用到索引,会查询到索引,辅助索引查主键索引
如果是这样
select * from table where b=2 and a=1;
select * from table where b=2 and a=1 and c=3;
mysql查询优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。但我们还是最好按照索引顺序来查询,这样查询优化器就不用重新编译了。
听说过前缀索引吗?
like 'XXX%'
,但是像like '%xx%
不会用到前缀索引ORDER BY
或 GROUP BY
中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)。简单说说你知道的索引优化策略
最左前缀匹配原则
一定要建主键索引
对 where,on,group by,order by
中出现的列使用索引
尽量选择区分度高的列作为索引
,区分度的公式是count(distinct col)/count(*)
,表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
对较小的数据列使用索引,这样会使索引文件更小,同时内存中也可以装载更多的索引键
索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’
就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值
,但这个进行检索时,需要把所有元素都应用函数才能比较,显然成本太大
。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)
为较长的字符串使用前缀索引
尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
不要过多创建索引, 权衡索引个数与DML之间关系,DML也就是插入、删除数据操作。这里需要权衡一个问题,建立索引的目的是为了提高查询效率的,但建立的索引过多,会影响插入、删除数据的速度
,因为我们修改的表数据,索引也需要进行调整重建
对于like查询,"%"不要放在前面
SELECT * FROM t WHERE uname LIKE'后盾%' -- 走索引
SELECT * FROM t WHERE uname LIKE "%后盾%" -- 不走索引·
-- 查询where条件数据类型不匹配也无法使用索引
CREATE TABLE a (a char(10));
EXPLAIN SELECT * FROM a WHERE a="1" – 走索引
EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引