索引是存储引擎用于快速找到记录数据行的一种分散存储的数据结构。
索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对性能的影响愈发重要。
在数据量较小且负载较低时,不恰当的索引对性能的影响可能还不明显,但是当数据量逐渐增大时,性能则会急剧下降。
所以 正确的创建合适的索引是提升数据库查询性能的基础。
索引有很多类型,可以为不同的场景提供更好的性能。
MySQL中,索引是在存储引擎层面实现的,所以,并没有统一的索引标准,一般来说,不同存储引擎的工作方式是不一样的,也不是所有的存储引擎都支持所有类型的索引
哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。
对于每一个数据行,存储引擎都会对所有的索引列根据一定的计算规则计算出一个哈希码,然后哈希索引将所有的哈希码存储在索引中,同时在哈希表中会保存一个指向对应数据行的指针。
MySQL中,Memory引擎是显式支持哈希索引的,他也是该引擎默认的索引类型,值得注意的一点是:Memory引擎是支持非唯一哈希索引的,也就是说如果多个列的哈希值相同,索引会以链表的方式存放多个记录指针到同一个哈希表中。
根据本人的理解,这种直接通过哈希索引的存储引擎,因为索引自身只需要存储对应的哈希值,所以索引的结构十分紧凑,这会让哈希索引查找的速度非常快
B-Tree索引使用B-Tree树数据结构存储数据,大多数MySQL引擎都支持这种索引(Archive引擎是个例外)
B+Tree索引图
详细的B-Tree和B+Tree可以参考 【MySQL一】开发人心里都该有的那颗 B 树
B树被作为实现索引的数据结构被创造出来,是因为它能够完美的利用“局部性原理”。
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
B树为何适合做索引?
(1)由于是m分叉的,高度能够大大降低;
(2)每个节点可以存储j个记录,如果将节点大小设置为页大小,例如4K,能够充分的利用预读的特性,极大减少磁盘IO;
注意:高度降低的原因在于:
所以:《高性能Mysql第三版》这本书也说了,一般的B+树都不会超过三层,也就意味着绝大数数据通过三次IO就可以找到
B+树,是在B树的基础上,做了一些改进:
以上改进让B+树比B树有更优的特性:
使用Myisam引擎的表在数据库中会存在三个文件
以user表为例:
一个是表定义文件 user.frm
一个是索引存储文件 user.MYI
还有一个是数据存储文件 user.MYD
因为Myisam引擎的索引和数据是分开存储的,叫做非聚集索引(UnClustered Index
)。并且在B+tree树的叶子节点存储的是数据行的地址,在检索数据时,以此从根节点开始检索,直到找到对应的关键字,然后到数据区获取数据行地址,最后根据这个数据行地址返回检索的数据行
Innodb和Myisam最大的不同是他是以主键为索引来组织数据的存储,叫做聚簇索引(Clustered Index
),也叫作聚集索引。
可能你会说,如果我的表没有主键怎么办?你也尽可放心:
使用Innodb引擎的表在数据库中会存在三个文件
以teacher表为例:
一个是表定义文件 teacher.frm
一个是数据和索引存储文件 teacher.IBD
此处引入一个聚簇索引(也叫聚集索引):数据库表行中数据的物理顺序与键值得逻辑顺序(也就是索引)相同,聚集索引并不是一种单独的索引类型,而是一种数据存储技术。
当有聚簇索引时,它的所有数据行实际上存放在索引的叶子页中,此处应该注意的是,因为无法同时把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
关于最后一点,是因为,二级索引叶子节点保存的并不是指向行的物理位置的指针,而是保存的是主键值,这意味着通过二级索引查找行的时候,存储引擎首先需要找到二级索引所对应的主键值,然后通过主键值再去聚簇索引找到对应的行。
MyISAM和InnoDB都使用B+树来实现索引:
如果一个索引包含所有需要查询的字段的值,我们就称之为“覆盖索引” 换言之,如果查询列可以通过索引节点中的关键字直接返回,则该索引称之为覆盖索引
单列索引可以理解成是一种特殊的联合索引
很多人对多列索引的理解都不够,一个常见错误哪就是 为每个列创建独立的索引,或者按照错误的顺序创建多列索引
记得之前看过一个博客说 建议把 where条件里边的列都加上索引,实际上这个建议是非常错误的。
在多个列上建立独立的单列索引大部分情况下并不能提高MySQL的查询性能
联合索引有几个选择原则: