索引是存储引擎用于快速找到记录数据行的一种分散存储的数据结构。sql
索引对于良好的性能很是关键,尤为是当表中的数据量愈来愈大时,索引对性能的影响愈发重要。数据库
在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,可是当数据量逐渐增大时,性能则会急剧降低。编程
因此 正确的建立合适的索引是提高数据库查询性能的基础。bash
索引有不少类型,能够为不一样的场景提供更好的性能。微信
MySQL中,索引是在存储引擎层面实现的,因此,并无统一的索引标准,通常来讲,不一样存储引擎的工做方式是不同的,也不是全部的存储引擎都支持全部类型的索引数据结构
哈希索引基于哈希表实现,只有精确匹配索引全部列的查询才有效。性能
对于每个数据行,存储引擎都会对全部的索引列根据必定的计算规则计算出一个哈希码,而后哈希索引将全部的哈希码存储在索引中,同时在哈希表中会保存一个指向对应数据行的指针。大数据
MySQL中,Memory引擎是显式支持哈希索引的,他也是该引擎默认的索引类型,值得注意的一点是:Memory引擎是支持非惟一哈希索引的,也就是说若是多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希表中。优化
根据本人的理解,这种直接经过哈希索引的存储引擎,由于索引自身只须要存储对应的哈希值,因此索引的结构十分紧凑,这会让哈希索引查找的速度很是快ui
B-Tree索引使用B-Tree树数据结构存储数据,大多数MySQL引擎都支持这种索引(Archive引擎是个例外)
详细的B-Tree和B+Tree能够参考这篇文章
B树被做为实现索引的数据结构被创造出来,是由于它可以完美的利用“局部性原理”。
因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也一般会立刻被使用。程序运行期间所须要的数据一般比较集中。
因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。
预读的长度通常为**页(page)**的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。
B树为什么适合作索引?
(1)因为是m分叉的,高度可以大大下降;
(2)每一个节点能够存储j个记录,若是将节点大小设置为页大小,例如4K,可以充分的利用预读的特性,极大减小磁盘IO;
注意:高度下降的缘由在于:
在利用了局部性原理前提下,咱们把一个节点的大小设为一页,一页4K,假设一个KEY有8byte,一个节点能够存储500个KEY,即j=500
m叉树,大概m/2<= j <=m,便可以差很少是1000叉树
一层树:1个节点,1*500个KEY,大小4K
二层树:1000个节点,1000500=50W个KEY,大小10004K=4M
三层树:10001000个节点,10001000500=5亿个KEY,大小10001000*4K=4G
因此:《高性能Mysql第三版》这本书也说了,通常的B+树都不会超过三层,也就意味着绝大数数据经过三次IO就能够找到
(1)再也不是二叉搜索,而是m叉搜索;
(2)叶子节点,非叶子节点,都存储数据;
(3)中序遍历,能够得到全部节点;
B+树,是在B树的基础上,作了一些改进:
非叶子节点再也不存储数据,数据只存储在同一层的叶子节点上;
叶子之间,增长了链表,获取全部节点,再也不须要中序遍历;
以上改进让B+树比B树有更优的特性:
范围查找,定位min与max以后,中间叶子节点,就是结果集,不用中序回溯(范围查询在SQL中用得不少,这是B+树比B树最大的优点);
叶子节点存储实际记录行,记录行相对比较紧密的存储,适合大数据量磁盘存储;非叶子节点存储记录的PK,用于查询加速,适合内存存储;
非叶子节点,不存储实际记录,而只存储记录的KEY的话,那么在相同内存的状况下,B+树可以存储更多索引;
使用Myisam引擎的表在数据库中会存在三个文件
以user表为例:
一个是表定义文件 user.frm
一个是索引存储文件 user.MYI
还有一个是数据存储文件 user.MYD
由于Myisam引擎的索引和数据是分开存储的,叫作非汇集索引(UnClustered Index
)。而且在B+tree树的叶子节点存储的是数据行的地址,在检索数据时,以此从根节点开始检索,直到找到对应的关键字,而后到数据区获取数据行地址,最后根据这个数据行地址返回检索的数据行
Innodb和Myisam最大的不一样是他是以主键为索引来组织数据的存储,叫作聚簇索引(Clustered Index
),也叫做汇集索引。
可能你会说,若是个人表没有主键怎么办?你也尽可放心:
若是你没有主键,innodb会选择一个惟一的非空索引代替;
若是没有这样的惟一索引可用,Innodb会本身建立一个隐式的row-id索引用于组织存储数据,
使用Innodb引擎的表在数据库中会存在三个文件
以teacher表为例:
一个是表定义文件 teacher.frm
一个是数据和索引存储文件 teacher.IBD
此处引入一个聚簇索引(也叫汇集索引):数据库表行中数据的物理顺序与键值得逻辑顺序(也就是索引)相同,汇集索引并非一种单独的索引类型,而是一种数据存储技术。
当有聚簇索引时,它的全部数据行实际上存放在索引的叶子页中,此处应该注意的是,由于没法同时把数据行存放在两个不一样的地方,因此一个表只能有一个聚簇索引。
关于最后一点,是由于,二级索引叶子节点保存的并非指向行的物理位置的指针,而是保存的是主键值,这意味着经过二级索引查找行的时候,存储引擎首先须要找到二级索引所对应的主键值,而后经过主键值再去聚簇索引找到对应的行。
MyISAM和InnoDB都使用B+树来实现索引:
若是一个索引包含全部须要查询的字段的值,咱们就称之为“覆盖索引”
换言之,若是查询列能够经过索引节点中的关键字直接返回,则该索引称之为覆盖索引
单列索引能够理解成是一种特殊的联合索引
不少人对多列索引的理解都不够,一个常见错误哪就是 为每一个列建立独立的索引,或者按照错误的顺序建立多列索引
记得以前看过一个博客说 建议把 where条件里边的列都加上索引,实际上这个建议是很是错误的。
在多个列上创建独立的单列索引大部分状况下并不能提升MySQL的查询性能
联合索引有几个选择原则:
哪么如何选择合适的索引列顺序?
正确的顺序依赖于使用该索引的查询,而且同时须要考虑如何更好的知足排序和分组的须要
在B-Tree索引中,索引列的顺序意味着索引首先按照最左列进行排序,其次是第二列,也就是最左匹配原则,因此多列索引的列顺序相当重要。
有一个经验法则:
将选择性最高的列放到索引最前列
在不须要考虑排序和分组时,这个法则是很好的。由于此时索引的做用只是用于优化where条件的查找。这种状况下这样设计的索引可以最快的过滤出须要的行。可是查询性能还和查询条件的具体值以及值得分布有关
判断哪一个列的选择性更高
select * from paymen where staff_id = 2 and customer_id = 584;
select sum(staff_id = 2),sum(customer_id = 584) from payment \G
select count(distinct staff_id)/count(*) as staff_id_selectivity,
count(distinct customer_id)/count(*) as customer_id_selectivity,
count(*)
from payment \G
值越大 选择性更高
复制代码
最后在网上看到一个顺口溜,以下:
全值匹配我最爱,最左前缀要遵照;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围以后全失效;
LIKE百分写最右,覆盖索引不写星;
不等空值还有or,索引失效要少用。
可谓是很精辟
欢迎关注微信公众号 程序猿杂货铺 ID zhoudl_l
微信公众号
![]()