首页 > 数据库 >MYSQL最左匹配原则及其底层逻辑

MYSQL最左匹配原则及其底层逻辑

时间:2024-06-19 16:27:43浏览次数:12  
标签:EXPLAIN 左匹配 t1 索引 MYSQL TABLE 查询 WHERE 底层

目录

前言

一、最左匹配原则示例

1.导入测试数据-索引(a,b,c)

2.全值匹配查询

3.连续匹配查询

​编辑

4.不连续匹配查询

三、底层原理详解

1.MYSQL优化器

2.最左匹配原理

前言

最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。

底层逻辑是基于b+树索引的数据结构。b+树是一种多叉树,每个节点可以包含多个键值对,用于加快查找速度。在MYSQL中,每个索引都是基于b+树实现的。

当查询条件中的所有列都包含在索引中时,MYSQL会根据最左匹配原则选择索引进行查询。最左匹配原则指的是从索引的最左边开始,逐个匹配查询条件中的列。如果某一列不满足查询条件,则停止匹配。这样可以快速定位到满足查询条件的记录。

使用最左匹配原则可以提高查询效率,因为MYSQL可以利用索引定位到满足查询条件的记录,而不需要扫描整个表。但是需要注意,如果查询条件中的列没有按照索引定义的顺序排序,MYSQL将无法使用索引,从而导致查询效率降低。

具体的查询过程是这样的:MySQL首先会查看查询条件中是否有使用到索引的列,如果有,则会选择使用包含这些列的索引进行查询。如果查询条件中只涉及到了复合索引的部分列,MySQL会从索引的最左边开始匹配,直到遇到一个不匹配的列,然后停止匹配。MySQL会利用这个索引的前缀,找到满足条件的记录。

最左匹配原则能够提高查询性能,因为MySQL只需要扫描索引的前缀,而不需要扫描整个索引。但同时,这也意味着复合索引的顺序非常重要,需要根据实际查询的情况来确定索引的顺序。


一、最左匹配原则示例

1.导入测试数据-索引(a,b,c)

CREATE TABLE `test_table` (
  `a` varchar(100) DEFAULT NULL,
  `b` varchar(100) DEFAULT NULL,
  `c` varchar(100) DEFAULT NULL,
  `d` varchar(100) DEFAULT NULL,
  KEY `test_table_a_IDX` (`a`,`b`,`c`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into test_table (a,b,c,d) values ('A1','B1','C1','D1');
insert into test_table (a,b,c,d) values ('A2','B2','C2','D2');
insert into test_table (a,b,c,d) values ('A3','B3','C3','D3');
insert into test_table (a,b,c,d) values ('A4','B4','C4','D4');
insert into test_table (a,b,c,d) values ('A5','B5','C5','D5');

2.全值匹配查询

EXPLAIN SELECT * FROM TEST_TABLE  WHERE A='A1' AND B = 'B1' AND C ='C1';

EXPLAIN SELECT * FROM TEST_TABLE  WHERE B='B1' AND A = 'A1' AND C ='C1';

EXPLAIN SELECT * FROM TEST_TABLE  WHERE C='C6' AND B = 'B4' AND A ='A1';

注:possible_keys:显示可能应用在这张表中的索引。key:实际使用的索引。

全值匹配查询中都用到了索引,子句几个搜索条件顺序调换不影响查询结果,因为Mysql中有查询优化器,会自动优化查询顺序。

3.连续匹配查询

  • 从a开始都从最左边开始连续匹配,用到了索引 
--(a),(a,b),(a,b,c)

EXPLAIN SELECT * FROM TEST_TABLE  WHERE A='A1';

EXPLAIN SELECT * FROM TEST_TABLE  WHERE A='A1' AND B = 'B1'; 

EXPLAIN SELECT * FROM TEST_TABLE  WHERE A='A1' AND B = 'B1' AND C ='C1';

  •  这些没有从最左边开始,最后查询没有用到索引,用的是全表扫描。
EXPLAIN SELECT * FROM TEST_TABLE  WHERE B='B1'; #b

EXPLAIN SELECT * FROM TEST_TABLE  WHERE C='C1';  #c

EXPLAIN SELECT * FROM TEST_TABLE  WHERE B='B1' AND C = 'C1'; #bc 

4.不连续匹配查询

  • 如果不连续时,只用到了a列的索引,b列和c列都没有用到 
EXPLAIN SELECT * FROM TEST_TABLE  WHERE B='B1' and D ='C1'; #bd

EXPLAIN SELECT * FROM TEST_TABLE  WHERE A='A1' and D ='D1'; #ad

二、底层原理详解

1.MYSQL优化器

以EXPLAIN SELECT * FROM TEST_TABLE WHERE B='B1' AND A = 'A1' AND C ='C1';为例查看优化器日志。

-- 开启
set optimizer_trace="enabled=on"; 
-- 执行sql
EXPLAIN SELECT * FROM TEST_TABLE  WHERE B='B1' AND A = 'A1' AND C ='C1';
-- 查看日志信息
select * from information_schema.OPTIMIZER_TRACE;
-- 关闭
set optimizer_trace="enabled=off";

{
   "steps": [
     {
       "join_preparation": {
         "select#": 1,
         "steps": [
           {
             "expanded_query": "/* select#1 */ select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c`,`t1`.`d` AS `d`,`t1`.`e` AS `e` from `t1` where ((`t1`.`a` = `t1`.`b`) and (`t1`.`b` = 1)) limit 0,1000"
           }
         ]
       }
     },
     {
       "join_optimization": {
         "select#": 1,
         "steps": [
           {
             "condition_processing": {
               "condition": "WHERE",
               "original_condition": "((`t1`.`a` = `t1`.`b`) and (`t1`.`b` = 1))",
               "steps": [
                 {
                   "transformation": "equality_propagation",
                   "resulting_condition": "(multiple equal(1, `t1`.`a`, `t1`.`b`))"
                 },
                 {
                   "transformation": "constant_propagation",
                   "resulting_condition": "(multiple equal(1, `t1`.`a`, `t1`.`b`))"
                 },
                 {
                   "transformation": "trivial_condition_removal",
                   "resulting_condition": "multiple equal(1, `t1`.`a`, `t1`.`b`)"
                 }
               ]
             }
           },
           {
             "substitute_generated_columns": {
             }
           },
           {
             "table_dependencies": [
               {
                 "table": "`t1`",
                 "row_may_be_null": false,
                 "map_bit": 0,
                 "depends_on_map_bits": [
                 ]
               }
             ]
           },
           {
             "ref_optimizer_key_uses": [
               {
                 "table": "`t1`",
                 "field": "a",
                 "equals": "1",
                 "null_rejecting": false
               },
               {
                 "table": "`t1`",
                 "field": "b",
                 "equals": "1",
                 "null_rejecting": false
               }
             ]
           },
           {
             "rows_estimation": [
               {
                 "table": "`t1`",
                 "rows": 1,
                 "cost": 1,
                 "table_type": "const",
                 "empty": false
               }
             ]
           },
           {
             "condition_on_constant_tables": "0",
             "condition_value": false
           }
         ],
         "empty_result": {
           "cause": "Impossible WHERE noticed after reading const tables"
         }
       }
     },
     {
       "join_execution": {
         "select#": 1,
         "steps": [
         ]
       }
     }
   ]
 }
  1. “join_preparation”:与准备阶段;
  2. “join_optimization”::join优化阶段;
  3. “join_execution”:执行阶段。
  4. “condition_processing”:条件优化,对查询条件进行一些优化!
  5. “rows_estimation”:行估算。

2.最左匹配原理

索引的底层是一颗 B+ 树,联合索引当然还是一颗 B+ 树,只不过联合索引的键值数量不是一个,而是多个。构建一颗 B+ 树只能根据一个值来构建,因此数据库依据联合索引最左的字段来构建 B+ 树。例子:假如创建一个(a,b,c)的联合索引,那么它的索引树是这样的:

该图就是一个形如(a,b,c)联合索引的 b+ 树,其中的非叶子节点存储的是第一个关键字的索引 a,而叶子节点存储的是三个关键字的数据。这里可以看出 a 是有序的,而 b,c 都是无序的。但是当在 a 相同的时候,b 是有序的,b 相同的时候,c 又是有序的。通过对联合索引的结构的了解,那么就可以很好的了解为什么最左匹配原则中如果遇到范围查询就会停止了。以 select * from t where a=5 and b>0 and c =1; #这样a,b可以用到(a,b,c),c不可以 为例子,当查询到 b 的值以后(这是一个范围值),c 是无序的。所以就不能根据联合索引来确定到底该取哪一行。

工作原理:以上图为例,冗余索引提前加载到了内存,在内存里面定位,折半查找,查询数据只与磁盘做一次IO交互就可以,节省时间。

具体的关于BTree数据结构相关知识我是阅读了以下文章学习的:

MySQL的B-Tree索引底层结构以及具体实现原理详解-CSDN博客

数据结构——B+树-CSDN博客

标签:EXPLAIN,左匹配,t1,索引,MYSQL,TABLE,查询,WHERE,底层
From: https://blog.csdn.net/jixiaoyaojing/article/details/139801087

相关文章

  • MySql入门操作集 6.0
    索引与完整性: 就是键之类的,通过键进行索引;看上图存在四种类型:nollnullprimarykeyuniqueforeignkey  对于键的设定可以在创建表的时候就添加相应的键:记得主键只设定一个作为索引吧好像;一般不设定实际意义,只是给他做一个标记作用用于索引其他的书写格式: 使......
  • mysql-connector-java 驱动jar包下載
    地址MySQL::MySQLDownloads,点击网址,进入mysql官网首页,我们点击downloads(下载)然后将页面往下拉,点击如图所画的选择,进入点击Connector/J,表示的java语言连接的数据库驱动器,如果是Python则选择下面Python为后缀的Connector,同理其他语言则选择对应的即可。因为我用的是Java,......
  • MYSQL 数字(Aggregate)函数
    目录1、AVG()2、MAX()3、MIN()4、SUM()5、COUNT()6、LIMIT()1、AVG()解释:返回数值列(字段)的平均值。语法格式:SELECTAVG(column_name)FROMtable_name中文注释:select AVG(数值列/字段)from表名;用法:SELECTAVG(column_name)FROMtable_name2、MAX()解......
  • MySQL数据库初体验
    目录1.数据库的基本概念(1)关系型数据库(SQL)(2)非关系型数据库(NoSQL)(3)主流的数据库介绍2.MySQL安装方法1.数据库的基本概念数据库分两大类:关系型数据库SQL、非关系型数据库NoSQL。(1)关系型数据库(SQL)典型代表:MySQL、MariaDB、PostgreSQL、SQL、Server、Oracle、Db2。信创标准(国产......
  • SpringData初步学习-连接MySQL数据库
    1.添加mysql驱动和spring-data-jpa依赖<dependencies><!--SpringDataJPA--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><......
  • SQLite vs MySQL vs PostgreSQL对比总结
    开发业务系统时,是绕不开RDBMS(关系型数据库)的。虽然现在诞生了各种NoSQL的数据库,RDBMS在业务系统中的严谨和优势依然无法取代。近几年大大小小的项目中,常用的三种RDBMS(SQLite,MySQL,Postgres)都有多次接触过,一些使用心得记录如下,供大家参考。1.SQLiteSQLite是一个独立的、基于文件......
  • MySQL的隔离级别
    读未提交(READUNCOMMITTED)这是最低的隔离级别,允许事务读取其他未提交事务的修改。这可能导致所谓的“脏读”问题,即读取到其他事务尚未提交的数据。虽然这种隔离级别可以提高并发性能,但数据的一致性和完整性可能会受到影响。读已提交(READCOMMITTED)在这个隔离级别下,事务只能......
  • 【学习笔记】MySQL(Ⅲ)
    MySQL(Ⅲ)11、进阶篇——视图      11.1、概述      11.2、基本语法      11.3、检查选项CASCADED      11.4、检查选项LOCAL      11.5、视图的更新原则12、进阶篇——存储过程      12.1、概述      1......
  • mysql中先进先出的例子
    在MySQL中,实现"先进先出"(FIFO,FirstInFirstOut)的数据结构通常不直接通过数据库的表结构来完成,因为数据库表本身并不保持元素的插入顺序。然而,你可以通过几种方法来模拟FIFO的行为。以下是一个简单的例子,说明如何在MySQL中模拟FIFO队列:使用带有时间戳的表你可以创建一个......
  • Java调用mysql后台函数的例子
    在Java中调用MySQL后台的存储函数(注意,这里我们区分存储过程和存储函数:存储过程没有返回值,但可以通过输出参数返回数据;而存储函数有一个返回值)时,你需要使用CallableStatement来执行这个函数并获取返回值。以下是一个简单的例子,说明如何在Java中调用MySQL的存储函数:MySQL存储函......