索引是存储引擎用于提高数据库表的访问速度的一种数据结构。它可以比作一本字典的目录,可以帮你快速找到对应的记录。
索引一般存储在磁盘的文件中,它是占用物理空间的。
优点:
缺点:
数据是存储在磁盘上的,查询数据时,如果没有索引,会加载所有的数据到内存,依次进行检索,读取磁盘次数较多。有了索引,就不需要加载所有数据,因为B+树的高度一般在2-4层,最多只需要读取2-4次磁盘,查询速度大大提升。
where
条件中用不到的字段不适合建立索引索引的数据结构主要有B+树和哈希表,对应的索引分别为B+树索引和哈希索引。InnoDB引擎的索引类型有B+树索引和哈希索引,默认的索引类型为B+树索引。
B+树索引
B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B树的平衡性,并且通过顺序访问指针来提高区间查询的性能。
在 B+ 树中,节点中的 key
从左到右递增排列,如果某个指针的左右相邻 key
分别是 keyi 和 keyi+1,则该指针指向节点的所有 key
大于等于 keyi 且小于等于 keyi+1。
进行查找操作时,首先在根节点进行二分查找,找到key
所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出key
所对应的数据项。
MySQL 数据库使用最多的索引类型是BTREE
索引,底层基于B+树数据结构来实现。
mysql> show index from blogG;
*************************** 1. row ***************************
Table: blog
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: blog_id
Collation: A
Cardinality: 4
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
哈希索引
哈希索引是基于哈希表实现的,对于每一行数据,存储引擎会对索引列进行哈希计算得到哈希码,并且哈希算法要尽量保证不同的列值计算出的哈希码值是不同的,将哈希码的值作为哈希表的key值,将指向数据行的指针作为哈希表的value值。这样查找一个数据的时间复杂度就是O(1),一般多用于精确查找。
从数据结构角度来看,MySQL索引可以分为以下几类:
从常见的基于InnoDB B+树索引角度来看,可以分为:(后文中的聚集索引也就是聚簇索引)
从索引性质的角度来看,可以分为:
CHAR
、VARCHAR
和TEXT
类型字段上使用全文索引。这与hash表的原理有关。
使用hash表的目的是为了尽可能的散列,因此在使用hash表的时候要选择hash算法,避免hash碰撞和hash冲突。
这就导致了hash表存储的数据是无序的,当需要进行范围查询的时候,只能挨个进行遍历对比,效率极低
所以:
如果将一个乱序的数据放入二叉树中,效率会高,但是如果数据是有顺序的,比如1、2、3、4、5,则二叉树将会编程一个链表的样式,失去了二叉树的优势
总结:有可能退化成链表,读取数据需要频繁IO
红黑树也是一种二叉平衡树,红黑树可以有效解决掉顺序数据一次放入二叉树而导致的形成链表的结果,但是红黑树一个节点只能存储一个数据,就导致如果是大量的数据,红黑树的高度就不可控,如果一个红黑树是20的高度,要查询的数据在叶子结点,则表示需要需磁盘20次IO,效率还是不高
总结:相比于二叉树能避免退化成链表,但IO次数还是会很高
B树比红黑树的优势是,B树是一个节点上存储多个数据,比如磁盘的一页数据,这样的横向扩展,相同的数据量就可以比红黑树减少更多的高度,从而减少了磁盘的IO次数
B树:data为数据的磁盘地址,还可能是整条列的数据
B+树:data为数据的磁盘地址,还可能是整条列的数据
总结:B+树相比于B树,非叶子节点不再存储data数据,非叶子可以存储的元素则会变多,一个非叶子节点就可以存储更多的索引数据,更进一步降低树的高度,提高了查询的效率。相比于hash表,B+树利用叶子节点之间的指针可以进行范围查询
如果 SQL 语句中用到了组合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个组合索引去进行匹配。当遇到范围查询(>
、、
between
、like
)就会停止匹配,后面的字段不会用到索引。
对(a,b,c)
建立索引,查询条件使用 a/ab/abc 会走索引,使用 bc 不会走索引。
对(a,b,c,d)
建立索引,查询条件为a = 1 and b = 2 and c > 3 and d = 4
,那么a、b和c三个字段能用到索引,而d无法使用索引。因为遇到了范围查询。
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)。直接执行b = 2
这种查询条件无法使用索引。
当a的值确定的时候,b是有序的。例如a = 1
时,b值为1,2是有序的状态。当a = 2
时候,b的值为1,4也是有序状态。 当执行a = 1 and b = 2
时a和b字段能用到索引。而执行a > 1 and b = 2
时,a字段能用到索引,b字段用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b值不是有序的,因此b字段无法使用索引。
InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的即为整张表的记录数据。聚集索引叶子节点的存储是逻辑上连续的,使用双向链表连接,叶子节点按照主键的顺序排序,因此对于主键的排序查找和范围查找速度比较快。
聚集索引的叶子节点就是整张表的行记录。InnoDB 主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
对于InnoDB
来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL
的唯一索引。如果没有主键也没有合适的唯一索引,那么InnoDB
内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。
非聚集索引:索引叶子节点存储的是数据行的主键和对应的索引列,需通过主键才能访问完整的数据行。一个表可以有多个非聚集索引(也称之为非主键索引、辅助索引、二级索引),适用于快速查找特定列的数据。
"回表"是指在使用二级索引(非聚簇索引)作为条件进行查询时,由于一级索引中只存储了索引字段的值和对应的主键值,无法得到真正想要的数据。
如果要查询数据行中的其它数据,需要根据主键去聚簇索引查找实际的数据行,这个过程被称为回表
select
的数据列只用从索引中就能够取得,不需要回表进行二次查询,也就是说查询列要被所使用的索引覆盖。对于innodb
表的二级索引,如果索引能覆盖到查询的列,那么就可以避免对主键索引的二次查询。
不是所有类型的索引都可以成为覆盖索引。覆盖索引要存储索引列的值,而哈希索引、全文索引不存储索引列的值,所以MySQL使用b+树索引做覆盖索引。
对于使用了覆盖索引的查询,在查询前面使用explain
,输出的extra列会显示为using index
。
比如user_like
用户点赞表,组合索引为(user_id, blog_id)
,user_id
和blog_id
都不为null
。
explain select blog_id from user_like where user_id = 13;
explain
结果的Extra
列为Using index
,查询的列被索引覆盖,并且where筛选条件符合最左前缀原则,通过索引查找就能直接找到符合条件的数据,不需要回表查询数据。
explain select user_id from user_like where blog_id = 1;
explain
结果的Extra
列为Using where; Using index
, 查询的列被索引覆盖,where筛选条件不符合最左前缀原则,无法通过索引查找找到符合条件的数据,但可以通过索引扫描找到符合条件的数据,也不需要回表查询数据。
MYSOL索引(的最左前缀匹配原则指的是在使用联合索引时,查询条件必须从索引的最左侧开始匹配。如果一个联合索引包含多个列,查询条件必须包含第一个列的条件,然后是第二个列,以此类推。
底层原理:因为联合索引在B+树中的排列方式道循“从左到右”的顺序,例如联合索引(a,b,c) 会按照(a,b,c)的顺字在B+ 树中进行排序。MYSQL在查找时会优先使用 a 作为匹配依据,然后依次使用 b 和 c。因此,组合索引能够从左到右依次高效匹配,跳过最左侧字段会导致无法利用该索引。
以下查询条件符合最左匹配原则
where a=1;
where a=1, b=2;
where a=1, b=2, c=3;
以下条件不符合最左匹配原则
where b=2;
where c=3;
where b=2, c=3;
选择合适的列:
优化多列索引:
控制索引的数量和类型 :
考虑数据分布和选择性 :
避免冗余和重复索引
其他注意点:
MySQL
在维护索引的时候是会将字段值一起维护的,那这样必然会导致索引占用更多的空间,另外在排序的时候需要花费更多的时间去对比。索引不一定有效。
例如查询条件中不包含索引列、低基数列索引效果不佳,或查询条件复杂且不匹配索引的顺序。
对于一些小表,MySQL可能选择全表扫描而非使用索引,因为全表扫描的开销可能更小。
最终是否用上索引是根据 MySQL 成本计算决定的,评估 CPU 和 IO 成本最终选择用辅助索引还是全表扫猫,有时候确实是全表扫描成本低所以没用上索引,但有时候由于一些统计数据的不准确,导致成本计算误判,而没用上索引。
排查索引效果的方法:使用 EXPLAIN 命令,通过在查询前加上 EXPLAIN,可以查看 MSQL选择的执行计划,了解是否使用了索引、使用了哪个索引、估算的行数等信息。
主要观察 EXPLAIN 结果以下几点
区分度不高的字段建索引的问题:
那何时低选择性索引可能有用呢 ?
以上属于特定情况,区分度不高的字段建立索引还是要依据具体应用场景和查询场景进行权衡和测试。
导致索引失效的情况:
%abc
,无法使用索引;非%开头的like查询如abc%
,相当于范围查询,会使用索引or
连接,也会导致索引失效有时需要在很长的字符列上创建索引,这会造成索引特别大且慢。使用前缀索引可以避免这个问题。
前缀索引是指对文本或者字符串的前几个字符建立索引,这样索引的长度更短,查询速度更快。
创建前缀索引的关键在于选择足够长的前缀以保证较高的索引选择性。索引选择性越高查询效率就越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的数据行。
索引下推(Index Condition Pushdown,ICP)是一种减少回表查询,提高查询效率的技术。它允许 MVSQL在使用索引查找数据时,将部分査询条件下推到存储引层过滤,从而减少需要从表中读取的数据行,减少了IO(本该由 Server 层做操作,交由存储引擎层因此叫做“下推”)。
注意:索引下推是应用在联合索引上的。
在没有索引下推的情况下,当查询使用复合索引时,MySQL可能需要访问主表来评估不能完全通过索引条件确定的行。例如,如果只有索引的部分条件在索引中能被使用,而其他条件需要读取实际行再进行筛选,传统方式可能会导致更多的全行读取。
在联合索引遍历过程中,对联合索引中包含的字段先做判断,在存储引擎层进行数据过滤,而不是在服务层过滤,直接过滤掉不满足条件的记录,利用索引现有的数据减少回表次数,就叫索引下推
假设当前 people 表有索引 INDEX(zipcode,lastname,firstname),当执行以下查询时:
SELECT * FROM people
WHERE zipcode='95054'
AND lastname LIKE '%etrunia%'
AND address LIKE '%Main Street%';
如果没有索引下推,当前的联合索引只能用上 zipcode='95854’这个条件,引擎层用不上lastname 这个条件过滤,只能得到所有符合 zipcode='95054'记录,传递给 server 层过滤。
有了索引下推之后,引擎层在得到符合 zipcode='95854'的数据后,可以直接通过 lastname 条件过滤数据,不符合条件的不会返回给 server 层。
索引并不是越多越好。因为索引不论从时间还是空间上都是有一定成本的
从时间上:每次对表中的数据进行增删改的时候,索引也必须被更新,,这会增加写入操作的开销,例如出除了一个 name为seven 的记录,不仅主键索引上需要修改,如果name字段有索引,那么 name 索引都需要修改,所以索引越多需要修改的地方也就越多,时间开销就大了,并目 B+ 树可能会有页分裂、合并等操作,时间开销就会更大。还有一点需要注意,mysql 有个查询优化器,它需要分析当前的查询,选择最优的计划、这过程就需要考虑出选择哪个索引的查询或本低,如果索引过多,那么会导致优化器耗费更多的时间在选择上,甚至可能因为索引选的不准确而选择了次优的索引。
从空间上:每建立一个二级索引,都需要新建一个 B+ 树,默认每个数据页都是16KB,如果教据量很大,索引又很多,占用的空间可不小
在 InnoDB 中,操作索引时是否锁表取决于具体的操作类型。有些情况可能会导致表锁,而有些情况则不会。
本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top
参与评论
手机查看
返回顶部