mysql优化-----多列索引的左前缀规则

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介:
复制代码
索引优化策略

1:索引类型

1.1B-tree索引
关注的是:Btree索引的左前缀匹配规则,索引在排序和分组上发挥的作用。

注:名叫btree索引,大的方面看都用的二叉树、平衡树。但具体的实现上,各引擎稍有不同。比如,严格的说,NDB引擎,使用的是T-tree。Myisam,innodb中,默认用B-tree索引。

凡是tree系列的,可理解为”排好序的、快速查找、的结构”。是排好序的,所以查询某个范围就很快。


btree索引的常见误区:在where条件常用的列上都加上索引,
例: where cat_id=3 and price>100 ; //查询第3个栏目,100元以上的商品
误: cat_id上,和, price上都加上索引.
错:只能用上cat_id或Price索引,因为是独立的索引,同时只能用上1个,因为每个索引都是针对整个表建的,而where and查询是在排好序的大范围内再查找小的。(mysql5.6以后做了稍微改进,把多列索引进行merge但是效果不好),因此要使用多列索引。

在多列上建立索引后,查询哪个列,索引都将发挥作用:
误: 多列索引上,索引发挥作用,需要满足左前缀要求(btree索引的左前缀规则)

B-tree多列索引:
a,b列各加上索引:index(a)和index(b)
Where a=3 and b=5 ,首先根据a查找到a=3的一小段,然后在从这小段中查找b=5的,此时b的索引已经发挥不了作用了。因为b的索引就不是在a的那一小段里面建的。b是针对整个表建的索引。


以 index(a,b,c) ,Where a=3 and b=5 and c=4 为例,联合索引是先根据a划分大类(a是有序的),再根据b在a里面划分小的(a里面的b是有序的),在根据c在b里面划分更小的(c在b里面是有序的)。就是这样查找的(这是tree系列的索引和查找方式)。

Where b=5 and c=4,索引就用不到了,因为首先是根据a分的大类。现在找b=5的,每一个a的区间都可能有b=5的所以首先排好序的a就进不去。

Where a=4 and c=4,a索引用到,c用不到。每段b里面都可能有c=4的。

多列索引:左前缀规则,中间断线就不行了。否则只能使用到部分。
复制代码

复制代码
index(a,b,c):
语句                                            索引是否发挥作用
Where a=3                                        是,只使用了a列
Where a=3 and b=5                                 是,使用了a,b列
Where a=3 and b=5 and c=4                        是,使用了abc
Where b=3  or  where c=4Where a=3 and c=4                                a列能发挥索引,c索引也就不能使用了
Where a=3 and b>10 and c=7                        A能利用,b能利用, b是一个范围,在这个范围里面的b[10,20]b[20,30]里面都有可能c=7,所以C不能利用
同上,where a=3 and b like ‘xxxx%and c=7        A能用,B能用,C不能用


多列索引经典题目:
http://www.zixue.it/thread-9218-1-4.html

假设某个表有一个联合索引(c1,c2,c3,c4)一下——只能使用该联合索引的c1,c2,c3部分,tree系列里面:c1是有序的,c2在c1里面是有序的,c3在c2里面是有序的,c4在c3里面是有序的。

A where c1=x and c2=x and c4>x and c3=x 
B where c1=x and c2=x and c4=x order by c3
C where c1=x and c4= x group by c3,c2
D where c1=x and c5=x order by c2,c3
E where c1=x and c2=x and c5=? order by c2,c3



create table t4 (
c1 tinyint(1) not null default 0,
c2 tinyint(1) not null default 0,
c3 tinyint(1) not null default 0,
c4 tinyint(1) not null default 0,
c5 tinyint(1) not null default 0,
index c1234(c1,c2,c3,c4)
);
insert into t4 values (1,3,5,6,7),(2,3,9,8,3),(4,3,2,7,5);


对于A:where c1=x and c2=x and c4>x and c3=x, 
等价c1=x and c2=x and c3=x and c4>x
因此 c1,c2,c3,c4都能用上. 如下:

mysql> explain select * from t4 where c1=1 and c2=2 and c4>3 and c3=3 \G
           id: 1
  select_type: SIMPLE
        table: t4
         type: range   //使用索引的方式,使用的是范围索引(c4)
possible_keys: c1234
          key: c1234
      key_len: 4       //4列#可以看出c1,c2,c3,c4索引都用上
          ref: NULL
         rows: 1
        Extra: Using where 


对于B: select * from t4 where c1=1 and c2=2 and c4=3 order by c3
c1 ,c2索引用上了,在c2用到索引的基础上,c3是排好序的,因此不用额外排序,c3,c4没发挥作用.

mysql> explain select * from t4 where c1=1 and c2=2 and c4=3 order by c3 \G
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234
          key: c1234   //使用了多列索引,但是不一定所有的列都用到了,
      key_len: 2       //2列发挥了作用c1c2,
          ref: const,const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)


mysql> explain select * from t4 where c1=1 and c2=2 and c4=3 order by c5 \G
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234
          key: c1234            //多列索引使用到了
      key_len: 2                //只是使用到了多列索引中的2列c1c2
          ref: const,const
         rows: 1
        Extra: Using where; Using filesort  //Usingfilesort是二次排序,在磁盘或者内存里面,c5是没有顺序的所以取出来之后要排序。

        
        
D语句:where c1=x and c5=x order by c2,c3,C1确定的基础上,c2是有序的,C2之下C3是有序的,因此c2,c3发挥的排序的作用.
因此,没用到filesort。只能使用一个索引,c2,c3的索引能够用来排序。

mysql> explain select * from t4 where c1=1 and c5=2 order by c2,c3 \G  
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234    //多列索引使用到了
          key: c1234
      key_len: 1        //只是使用到了多列索引中的1列c1,
          ref: const
         rows: 1
        Extra: Using where     //没有Using filesort的文件排序,因为c2c3是排好序的
        
        
        
mysql> explain select * from t4 where c1=1 and c5=2 order by c3 \G
           id: 1
  select_type: SIMPLE
        table: t4
   partitions: NULL
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 1
          ref: const
         rows: 1
     filtered: 20.00
        Extra: Using index condition; Using where; Using filesort  //要排序,因为c3跳过了c2,所以要文件排序(比如国家下面的省,先要国家排序后在省排序。现在跳过国家去排省就要重新排序了),没法利用索引了。




E: where c1=x and c2=x and c5=? order by c2,c3
这一句等价与 elect * from t4 where c1=1 and c2=3 and c5=2 order by c3; 
因为c2的值既是固定的,参与排序时并不考虑

mysql> explain select * from t4 where c1=1 and c2=3 and c5=2 order by c2,c3 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 2                //用到了2列索引,
          ref: const,const
         rows: 1
        Extra: Using where    //没有用到文件排序,说明c2c3索引都用到了,
        
        
        
mysql> explain select * from t4 where c1=1 and c5=2 order by c2,c3 \G
           id: 1
  select_type: SIMPLE
        table: t4
   partitions: NULL
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 1            //用到了1列索引,
          ref: const
         rows: 1
     filtered: 20.00
        Extra: Using index condition; Using where    //不用排序,因为先根据cw2排序然后根据c3排序,而c2c3是已经排好序的
        
        

mysql>  explain select * from t4 where c1=1 and c5=2 order by c3,c2 \G
           id: 1
  select_type: SIMPLE
        table: t4
   partitions: NULL
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 1        //用到了1列索引,
          ref: const
         rows: 1
     filtered: 20.00
        Extra: Using index condition; Using where; Using filesort  //要排序,因为先根据c3排序再根据c2排序,(比如国家下面的省,先要国家排序后在省排序。现在跳过国家去排省就要重新排序了)
        
        
        
mysql> explain select * from t4 where c1=1 and c2=3 and c5=2 order by c3,c2 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t4
   partitions: NULL
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 2
          ref: const,const
         rows: 1
     filtered: 20.00
        Extra: Using index condition; Using where   //没有排序,虽然c3在c2的前面,但是c2是定值。




mysql> select cat_id,avg(shop_price) from goods group by cat_id;
+--------+-----------------+
| cat_id | avg(shop_price) |
+--------+-----------------+
|      2 |      823.330000 |
|      3 |     1746.066667 |
|      4 |     2297.000000 |
|      5 |     3700.000000 |
|      8 |       75.333333 |
|     11 |       31.000000 |
|     13 |       33.500000 |
|     14 |       54.000000 |
|     15 |       70.000000 |
+--------+-----------------+

mysql> explain select cat_id,avg(shop_price) from goods group by cat_id \G;
           id: 1
  select_type: SIMPLE
        table: goods
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 31
     filtered: 100.00
        Extra: Using temporary; Using filesort   //分组操作的时候要先排序,这里Using temporary根据cat_id进行排序,使用的是临时表排序,如果将cat_id加上索引,那么cat_id已经排好序了,就不需要使用临时表来排序。(有可能加了索引还是使用了临时表,那是因为mysql做了自动优化.)
        
mysql> alter table goods add index catid_index(cat_id);



对于C where c1=x and c4= x group by c3,c2
只用到c1索引,因为group by c3,c2的顺序无法利用c2,c3索引
mysql> explain select * from t4 where c1=1 and c4=2 group by c3,c2 \G
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 1             #只用到c1,因为先用c3后用c2分组,导致c2,c3索引没发挥作用
          ref: const
         rows: 1
        Extra: Using where; Using temporary; Using filesort//并且还要排序

        
mysql> explain select * from t4 where c1=1 and c4=2 group by c2,c3 \G
           id: 1
  select_type: SIMPLE
        table: t4
         type: ref
possible_keys: c1234
          key: c1234
      key_len: 1
          ref: const
         rows: 1
        Extra: Using where
1 row in set (0.00 sec)
复制代码
复制代码
1.2 hash索引(数据散的放的)
在memory表里,默认是hash索引,hash的理论查询时间复杂度为O(1),O(1)是说任意给一行,理论上一次就能够找到。

疑问: 既然hash的查找如此高效,为什么不都用hash索引?
答:1:hash函数计算后的结果是随机的,如果是在磁盘上放置数据,随机查询是非常慢的。虽然算这行数据在哪里算的很快,但是去取这行数据的时候就很慢。
比如主键为id为例, 那么随着id的增长, id对应的行,在磁盘上随机放置.

2: 不法对范围查询进行优化,随机的范围查找慢。

3: 无法利用前缀索引. 比如在btree中,field列的值"hellopworld并加索引查询xx=helloword,自然可以利用索引, xx=hello,也可以利用索引(左前缀索引),而利用hash索引,因为hash('helloword')和hash('hello')就是截然不同的结果,所以没法利用前缀优化。

4: 排序也无法利用hash索引来优化.

5: 必须回行.就是说 通过索引拿到数据位置,必须回到表中取数据
复制代码

 


本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/8268183.html,如需转载请自行联系原作者

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
49
分享
相关文章
Mysql的索引
MYSQL索引主要有 : 单列索引 , 组合索引和空间索引 , 用的比较多的就是单列索引和组合索引 , 空间索引我这边没有用到过 单列索引 : 在MYSQL数据库表的某一列上面创建的索引叫单列索引 , 单列索引又分为 ● 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值和空值,纯粹为了查询数据更快一点。 ● 唯一索引:索引列中的值必须是唯一的,但是允许为空值 ● 主键索引:是一种特殊的唯一索引,不允许有空值 ● 全文索引: 只有在MyISAM引擎、InnoDB(5.6以后)上才能使⽤用,而且只能在CHAR,VARCHAR,TEXT类型字段上使⽤用全⽂文索引。
MySQL细节优化:关闭大小写敏感功能的方法。
通过这种方法,你就可以成功关闭 MySQL 的大小写敏感功能,让你的数据库操作更加便捷。
72 19
MySQL底层概述—8.JOIN排序索引优化
本文主要介绍了MySQL中几种关键的优化技术和概念,包括Join算法原理、IN和EXISTS函数的使用场景、索引排序与额外排序(Using filesort)的区别及优化方法、以及单表和多表查询的索引优化策略。
139 22
MySQL底层概述—8.JOIN排序索引优化
MySQL索引有哪些类型?
● 普通索引:最基本的索引,没有任何限制。 ● 唯一索引:索引列的值必须唯一,但可以有空值。可以创建组合索引,则列值的组合必须唯一。 ● 主键索引:是特殊的唯一索引,不可以有空值,且表中只存在一个该值。 ● 组合索引:多列值组成一个索引,用于组合搜索,效率高于索引合并。 ● 全文索引:对文本的内容进行分词,进行搜索。
MySQL底层概述—7.优化原则及慢查询
本文主要介绍了:Explain概述、Explain详解、索引优化数据准备、索引优化原则详解、慢查询设置与测试、慢查询SQL优化思路
158 15
MySQL底层概述—7.优化原则及慢查询
MySQL底层概述—6.索引原理
本文详细回顾了:索引原理、二叉查找树、平衡二叉树(AVL树)、红黑树、B-Tree、B+Tree、Hash索引、聚簇索引与非聚簇索引。
124 11
MySQL底层概述—6.索引原理
MySQL底层概述—5.InnoDB参数优化
本文介绍了MySQL数据库中与内存、日志和IO线程相关的参数优化,旨在提升数据库性能。主要内容包括: 1. 内存相关参数优化:缓冲池内存大小配置、配置多个Buffer Pool实例、Chunk大小配置、InnoDB缓存性能评估、Page管理相关参数、Change Buffer相关参数优化。 2. 日志相关参数优化:日志缓冲区配置、日志文件参数优化。 3. IO线程相关参数优化: 查询缓存参数、脏页刷盘参数、LRU链表参数、脏页刷盘相关参数。
120 12
MySQL底层概述—5.InnoDB参数优化
基于SQL Server / MySQL进行百万条数据过滤优化方案
对百万级别数据进行高效过滤查询,需要综合使用索引、查询优化、表分区、统计信息和视图等技术手段。通过合理的数据库设计和查询优化,可以显著提升查询性能,确保系统的高效稳定运行。
73 9
MySQL和SQLSugar百万条数据查询分页优化
在面对百万条数据的查询时,优化MySQL和SQLSugar的分页性能是非常重要的。通过合理使用索引、调整查询语句、使用缓存以及采用高效的分页策略,可以显著提高查询效率。本文介绍的技巧和方法,可以为开发人员在数据处理和查询优化中提供有效的指导,提升系统的性能和用户体验。掌握这些技巧后,您可以在处理海量数据时更加游刃有余。
189 9
图解MySQL【日志】——磁盘 I/O 次数过高时优化的办法
当 MySQL 磁盘 I/O 次数过高时,可通过调整参数优化。控制刷盘时机以降低频率:组提交参数 `binlog_group_commit_sync_delay` 和 `binlog_group_commit_sync_no_delay_count` 调整等待时间和事务数量;`sync_binlog=N` 设置 write 和 fsync 频率,`innodb_flush_log_at_trx_commit=2` 使提交时只写入 Redo Log 文件,由 OS 择机持久化,但两者在 OS 崩溃时有丢失数据风险。
69 3
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等