MYSQL(05)-索引原理
Mysql两种索引结构:B+Tree索引和Hash索引的区别和使用场景
-
Hash索引:使用hash散列的形式,已KV格式存数,查找单条数据的时候速度很快,但是范围查找和排序的时候效率慢,目前就只有MEMORY引擎显式地支持这种索引,底层会维护一个类似KV结构的文件
-
B+tree索引 :是mysql使用最频繁的一个索引数据结构,是Inodb和Myisam存储引擎模式的索引类型。相对Hash索引,B+树在查找单条记录的速度比不上Hash索引,但是因为更适合排序等操作。Myisam 和 Innodb默认都是使用B+Tree实现的
B+Tree作为mysql数据库索引原理
-
与普通的二叉树不同,B-Tree允许每个节点有更多的子节点,将每个节点的数据量增大,这样设计很复合磁盘的设计原则,让每个节点的数据大小正好放在磁盘的一个页上,这样从而减少磁盘的IO的次数,从而减少寻址的时间
-
而mysql使用的是B+Tree,除了拥有B-Tree的优先,相对于B-Tree,他更适合外部存储(一般指磁盘存储),由于内节点(非叶子节点)不存储data,所以一个节点可以存储更多的内节点,每个节点能索引的范围更大更精确。也就是说使用B+树单次磁盘IO的信息量相比较B树更大,IO效率更高
-
mysql是关系型数据库,经常会按照区间来访问某个索引列,B+树的叶子节点间按顺序建立了链指针,加强了区间访问性,所以B+树对索引列上的区间范围查询很友好。而B树每个节点的key和data在一起,无法进行区间查找。
MYISAM和INNODB索引B+TREE的区别
-
MYISAM引擎中--非聚集索引
-
使用B+TREE的时候,索引文件和数据文件是分离的,索引文件仅保存数据记录的地址,又叫做非聚集索引
-
主键索引和非主键索引都保存在.myi文件中,然后指向.myd中的内存
-
-
INNODB引擎中--聚集索引
-
使用B+TREE的时候,表数据文件本身就是按照B+TREE组织的索引接口,叶子节点data域保存了完成的数据记录,这个索引key是数据表的主键,因此INNODB表数据文件本身就是主索引,又叫做聚集索引
-
索引文件(就是本身的文件)保存在.ibd文件中的,副索引文件也是保存在.ibd中的但是指向的地址是主索引的地址,所以是执行了两次查询
-
索引覆盖
在上图中可以看到,如果使用name作为的索引,属于二级索引,查询条件为name的时候先查询到name索引中指定的主键,然后再回表查询,这样"回表"查询是需要消耗IO性能的,所以我们在设计查询的时候最好能够避免这种回表查询。
概念:当能通过检索索引就可以读取想要的数据,那就不需要再到数据表中读取行了。如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫做覆盖索引,简单解释来说就是查询的正好是name字段的时候查询条件也使用的name,这样的话就无需回表查询,增加查询速度
显示效果:当发起一个被索引覆盖的查询(也叫索引覆盖查询)时,在EXPLAIN的Extra列可以看到“Using index”的信息。
索引下推
MySQL 5.6引入了索引下推优化,默认开启,使用以下命令可以将其关闭。
SET optimizer_switch = 'index_condition_pushdown=off';
官方文档中给的例子和解释如下:
people表中(zipcode,lastname,firstname)构成一个索引
SELECT * FROM people WHERE zipcode='95054' AND lastname LIKE '%etrunia%' AND address LIKE '%Main Street%';
如果没有使用索引下推技术,则MySQL会通过zipcode='95054'从存储引擎中查询对应的数据,返回到MySQL服务端,然后MySQL服务端基于lastname LIKE '%etrunia%'和address LIKE '%Main Street%'来判断数据是否符合条件。 如果使用了索引下推技术,则MYSQL首先会返回符合zipcode='95054'的索引,然后根据lastname LIKE '%etrunia%'和address LIKE '%Main Street%'来判断索引是否符合条件。如果符合条件,则根据该索引来定位对应的数据,如果不符合,则直接reject掉。 有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。
-
innodb引擎的表,索引下推只能用于二级索引。就像之前提到的,innodb的主键索引树叶子结点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果。
-
索引下推一般可用于所求查询字段(select列)不是/不全是联合索引的字段,查询条件为多条件查询且查询条件子句(where/order by)字段全是联合索引。假设表t有联合索引(a,b),下面语句可以使用索引下推提高效率 select * from t where a > 2 and b > 10;
索引的最左匹配原则
非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
索引使用原则
-
=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式
-
尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
-
索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);
-
尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可
普通索引,唯一索引的选择
在普通索引和唯一索引的使用成本上,主要在于更新操作的时候,普通索引数据的更新,直接将数据更新到change buffer中,然后change buffer会定期的刷新到磁盘即可,而唯一索引需要去对比索引是否是唯一的,如果更新的数据在change buffer中,则对比的过程可以直接在内存中,这样时间成不不大,但是如果更新的数据不在change buffer中,那么更新就需要去IO磁盘进行查询对比,这样就会导致更新过程缓慢。所以如果业务上没有要求必须唯一的化,尽量使用普通索引,这样的成本会更低