『MySQL 实战 45 讲』18 - 为什么这些SQL语句逻辑相同,性能却差异巨大

2024-05-03 00:04

本文主要是介绍『MySQL 实战 45 讲』18 - 为什么这些SQL语句逻辑相同,性能却差异巨大,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

为什么这些SQL语句逻辑相同,性能却差异巨大

条件字段函数操作

  1. 创建一个 sql 表。该表为交易记录表,包含交易流水号(tradeid)、交易员 id(operator)、交易时间(t_modified)等字段
CREATE TABLE `tradelog` (`id` int(11) NOT NULL,`tradeid` varchar(32) DEFAULT NULL,`operator` int(11) DEFAULT NULL,`t_modified` datetime DEFAULT NULL,PRIMARY KEY (`id`),KEY `tradeid` (`tradeid`),KEY `t_modified` (`t_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 假设要求的是统计发生在所有年份中 7 月份的交易记录总数,可能会写出下面样式的 sql
select count(*) from tradelog where month(t_modified)=7;
  1. 由于函数计算,不会走索引,t_modified 的索引如图

在这里插入图片描述
4. 对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能
5. 优化器可以选择遍历主键索引,也可以选择遍历索引 t_modified,优化器对比索引大小后发现,索引 t_modified 更小,遍历这个索引比遍历主键索引来得更快。因此最终还是会选择索引 t_modified。但是这里是全索引扫描
在这里插入图片描述
6. 即使对于对于不改变有序性的函数,优化器也不会考虑使用索引(除非计算是在函数入参),例如

select * from tradelog where id + 1 = 10000
# 函数入参计算,这样才能用索引
# where id = 10000 -1

隐式类型转换

  1. 下面语句需要进行隐式转换,因为 tradeid 的字段类型是 varchar(32),而输入的参数却是整型,需要全表扫描
select * from tradelog where tradeid=110717;
  1. 对于优化器来说,这个语句相当于
select * from tradelog where  CAST(tradid AS signed int) = 110717;
  1. 这就说明了,这条语句触发了上面的规则:对索引字段做函数操作,优化器会放弃走树搜索功能

隐式字符编码转换

  1. 创建另外一个表,用于记录交易的操作细节,同时插入一些数据
CREATE TABLE `trade_detail` (`id` int(11) NOT NULL,`tradeid` varchar(32) DEFAULT NULL,`trade_step` int(11) DEFAULT NULL, /*操作步骤*/`step_info` varchar(32) DEFAULT NULL, /*步骤信息*/PRIMARY KEY (`id`),KEY `tradeid` (`tradeid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;insert into tradelog values(1, 'aaaaaaaa', 1000, now());
insert into tradelog values(2, 'aaaaaaab', 1000, now());
insert into tradelog values(3, 'aaaaaaac', 1000, now());insert into trade_detail values(1, 'aaaaaaaa', 1, 'add');
insert into trade_detail values(2, 'aaaaaaaa', 2, 'update');
insert into trade_detail values(3, 'aaaaaaaa', 3, 'commit');
insert into trade_detail values(4, 'aaaaaaab', 1, 'add');
insert into trade_detail values(5, 'aaaaaaab', 2, 'update');
insert into trade_detail values(6, 'aaaaaaab', 3, 'update again');
insert into trade_detail values(7, 'aaaaaaab', 4, 'commit');
insert into trade_detail values(8, 'aaaaaaac', 1, 'add');
insert into trade_detail values(9, 'aaaaaaac', 2, 'update');
insert into trade_detail values(10, 'aaaaaaac', 3, 'update again');
insert into trade_detail values(11, 'aaaaaaac', 4, 'commit');
  1. 查询 id=2 的交易的所有操作步骤信息
  • 第一行显示优化器会先在交易记录表 tradelog 上查到 id=2 的行,这个步骤用上了主键索引,rows=1 表示只扫描一行
  • 第二行 key=NULL,表示没有用上交易详情表 trade_detail 上的 tradeid 索引,进行了全表扫描
select d.* from tradelog l, trade_detail d where d.tradeid=l.tradeid and l.id=2; /*语句Q1*/

在这里插入图片描述
3. Q1 语句的执行过程
在这里插入图片描述

  • (1)是根据 id 在 tradelog 表里找到 L2 这一行
  • (2)是从 L2 中取出 tradeid 字段的值
  • (3)是根据 tradeid 值到 trade_detail 表中查找条件匹配的行。explain 的结果里面第二行的 key=NULL 表示的就是,这个过程是通过遍历主键索引的方式,一个一个地判断 tradeid 的值是否匹配
  1. 问题就出在第 3 步,单独把这一步变成 SQL 语句的话,如下
select * from trade_detail where tradeid=$L2.tradeid.value; 
  1. 其中 $L2.tradeid.value 的字符集是 utf8mb4,字符集 utf8mb4 是 utf8 的超集,MySQL 内部的操作是,先把 utf8 字符串转成 utf8mb4 字符集,再做比较
  2. 也就是说,实际上这个语句等同于下面这个写法
select * from trade_detail  where CONVERT(traideid USING utf8mb4)=$L2.tradeid.value; 
  1. 对索引字段做函数操作,优化器会放弃走树搜索功能。字符集不同只是条件之一,连接过程中要求在被驱动表的索引字段上加函数操作,是直接导致对被驱动表做全表扫描的原因
  2. 换个需求,如果是“查找 trade_detail 表里 id=4 的操作,对应的操作者是谁”
select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4;

在这里插入图片描述
9. 这次的第 3 步语句相当于

select operator from tradelog  where traideid =$R4.tradeid.value; 
  1. 按照转换规则,实际是在输入参数上使用函数,所以能使用索引
select operator from tradelog  where traideid =CONVERT($R4.tradeid.value USING utf8mb4); 
  1. 所以,如果要使查 detail 表可以使用上索引,要不就修改字段的编码,要不就强制转换 tradeid 为 uft8
# 修改字段为 utf8mb4 
alter table trade_detail modify tradeid varchar(32) CHARACTER SET utf8mb4 default null;
# 强制转换 tradeid 为 uft8
select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2; 
  1. 总结:对索引字段做函数操作,可能会破坏索引值的有序性,因此优化器就决定放弃走树搜索功能

这篇关于『MySQL 实战 45 讲』18 - 为什么这些SQL语句逻辑相同,性能却差异巨大的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/955425

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

spoj705( 求不相同的子串个数)

题意:求串s的不同子串的个数 解题思路:任何子串都是某个后缀的前缀,对n个后缀排序,求某个后缀的前缀的个数,减去height[i](第i个后缀与第i-1 个后缀有相同的height[i]个前缀)。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstrin

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount