MySQL性能优化:MySQL中的隐式转换造成的索引失效

目录

  • 前言
  • 数据准备
  • SQL测试
  • 分析和总结

前言

数据库优化是一个任重而道远的任务,想要做优化必须深入理解数据库的各种特性。在开发过程中我们经常会遇到一些原因很简单但造成的后果却很严重的疑难杂症,这类问题往往还不容易定位,排查费时费力最后发现是一个很小的疏忽造成的,又或者是因为不了解某个技术特性产生的。

于数据库层面,最常见的恐怕就是索引失效了,且一开始因为数据量小还不易被发现。但随着业务的拓展数据量的提升,性能问题慢慢的就体现出来了,处理不及时还很容易造成雪球效应,最终导致数据库卡死甚至瘫痪。造成索引失效的原因可能有很多种,相关技术博客已经有太多了,今天我要记录的是隐式转换造成的索引失效。

数据准备

首先使用存储过程生成1000万条测试数据,

测试表一共建立了7个字段(包括主键),num1num2保存的是和ID一样的顺序数字,其中num2是字符串类型。
type1type2保存的都是主键对5的取模,目的是模拟实际应用中常用类似type类型的数据,但是type2是没有建立索引的。

str1str2都是保存了一个20位长度的随机字符串,str1不能为NULLstr2允许为NULL,相应的生成测试数据的时候我也会在str2字段生产少量NULL值(每100条数据产生一个NULL值)。

-- 创建测试数据表
DROP TABLE IF EXISTS test1; 
CREATE TABLE `test1` (`id` int(11) NOT NULL,`num1` int(11) NOT NULL DEFAULT '0',`num2` varchar(11) NOT NULL DEFAULT '',`type1` int(4) NOT NULL DEFAULT '0',`type2` int(4) NOT NULL DEFAULT '0',`str1` varchar(100) NOT NULL DEFAULT '',`str2` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),KEY `num1` (`num1`),KEY `num2` (`num2`),KEY `type1` (`type1`),KEY `str1` (`str1`),KEY `str2` (`str2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 创建存储过程
DROP PROCEDURE IF EXISTS pre_test1; 
DELIMITER //
CREATE PROCEDURE `pre_test1`()
BEGINDECLARE i INT DEFAULT 0;SET autocommit = 0;WHILE i < 10000000 DOSET i = i + 1;SET @str1 = SUBSTRING(MD5(RAND()),1,20);-- 每100条数据str2产生一个null值IF i % 100 = 0 THENSET @str2 = NULL;ELSESET @str2 = @str1;END IF;INSERT INTO test1 (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`)VALUES (CONCAT('', i), CONCAT('', i), CONCAT('', i), i%5, i%5, @str1, @str2);-- 事务优化,每一万条数据提交一次事务IF i % 10000 = 0 THENCOMMIT;END IF;END WHILE;
END;
// DELIMITER ;
-- 执行存储过程
CALL pre_test1();

数据量比较大,还涉及使用MD5生成随机字符串,所以速度有点慢,稍安勿躁,耐心等待即可。

1000万条数据,我用了60分钟才跑完(实际时间跟你电脑硬件配置有关)。这里贴几条生成的数据,大致长这样。

在这里插入图片描述

SQL测试

先来看这组SQL,一共四条,我们的测试数据表num1int类型,num2varchar类型,但是存储的数据都是跟主键id一样的顺序数字,两个字段都建立有索引。

1: SELECT * FROM `test1` WHERE num1 = 10000;
2: SELECT * FROM `test1` WHERE num1 = '10000';
3: SELECT * FROM `test1` WHERE num2 = 10000;
4: SELECT * FROM `test1` WHERE num2 = '10000';

这四条SQL都是有针对性写的,12查询的字段是int类型,34查询的字段是varchar类型。12或34查询的字段虽然都相同,但是一个条件是数字,一个条件是用引号引起来的字符串。这样做有什么区别呢?先不看下边的测试结果你能猜出这四条SQL的效率顺序吗?

经测试这四条SQL最后的执行结果却相差很大,其中124三条SQL基本都是瞬间出结果,大概在0.001 ~ 0.1秒,在千万级的数据量下这样的结果可以判定这三条SQL性能基本没差别了。但是第三条SQL,多次测试耗时基本在30秒左右。

在这里插入图片描述

为什么34两条SQL效率相差那么大,但是同样做对比的12两条SQL却没什么差别呢?查看一下执行计划,下边分别1234条SQL的执行计划数据:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,124三条SQL都能使用到索引,连接类型都为ref,扫描行数都为1,所以效率非常高。再看看第三条SQL,没有用上索引,所以为全表扫描,rows直接到达1000万了,所以性能差别才那么大。

仔细观察你会发现,34两条SQL查询的字段num2varchar类型的,查询条件等号右边加引号的第4条SQL是用到索引的,那么是查询的数据类型和字段数据类型不一致造成的吗?如果是这样那12两条SQL查询的字段num1是int类型,但是第2条SQL查询条件右边加了引号为什么还能用上索引呢。

查阅MySQL相关文档发现是隐式转换造成的,看一下官方的描述:

官方文档: 12.2 Type Conversion in Expression Evaluation

当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL会根据需要自动将字符串转换为数字,反之亦然。以下规则描述了比较操作的转换方式:

  1. 两个参数至少有一个是NULL时,比较的结果也是NULL,特殊的情况是使用<=>对两个NULL做比较时会返回1,这两种情况都不需要做类型转换
  2. 两个参数都是字符串,会按照字符串来比较,不做类型转换
  3. 两个参数都是整数,按照整数来比较,不做类型转换
  4. 十六进制的值和非数字做比较时,会被当做二进制串
  5. 有一个参数是TIMESTAMPDATETIME,并且另外一个参数是常量,常量会被转换为timestamp
  6. 有一个参数是decimal类型,如果另外一个参数是decimal或者整数,会将整数转换为decimal后进行比较,如果另外一个参数是浮点数,7则会把decimal转换为浮点数进行比较
  7. 所有其他情况下,两个参数都会被转换为浮点数再进行比较

根据官方文档的描述,我们的第23两条SQL都发生了隐式转换,第2条SQL的查询条件num1 = ‘10000’,左边是int类型右边是字符串,第3条SQL相反,那么根据官方转换规则第7条,左右两边都会转换为浮点数再进行比较。

先看第2条SQL:SELECT * FROM test1 WHERE num1 = '10000'; 左边为int类型10000,转换为浮点数还是10000,右边字符串类型'10000',转换为浮点数也是10000。两边的转换结果都是唯一确定的,所以不影响使用索引。

第3条SQL:SELECT * FROM test1 WHERE num2 = 10000; 左边是字符串类型'10000',转浮点数为10000是唯一的,右边int类型10000转换结果也是唯一的。但是,因为左边是检索条件,'10000'转到10000虽然是唯一,但是其他字符串也可以转换为10000,比如'10000a''010000''10000'等等都能转为浮点数10000,这样的情况下,是不能用到索引的。

关于这个隐式转换我们可以通过查询测试验证一下,先插入几条数据,其中num2='10000a''010000''10000'

INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000001', '10000', '10000a', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');
INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000002', '10000', '010000', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');
INSERT INTO `test1` (`id`, `num1`, `num2`, `type1`, `type2`, `str1`, `str2`) VALUES ('10000003', '10000', ' 10000', '0', '0', '2df3d9465ty2e4hd523', '2df3d9465ty2e4hd523');

在这里插入图片描述

然后使用第三条SQL语句SELECT * FROM test1 WHERE num2 = 10000;进行查询:

在这里插入图片描述

从结果可以看到,后面插入的三条数据也都匹配上了。那么这个字符串隐式转换的规则是什么呢?为什么num2='10000a''010000''10000'这三种情形都能匹配上呢?查阅相关资料发现规则如下:

  1. 不以数字开头的字符串都将转换为0。如'abc''a123bc''abc123'都会转化为0;
  2. 以数字开头的字符串转换时会进行截取,从第一个字符截取到第一个非数字内容为止。比如'123abc'会转换为123'012abc'会转换为012也就是12'5.3a66b78c'会转换为5.3,其他同理。

现对以上规则做如下测试验证:

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

如此也就印证了之前的查询结果了。

再次写一条SQL查询str1字段:SELECT * FROM test1 WHERE str1 = 1234;

在这里插入图片描述

分析和总结

通过上面的测试我们发现MySQL使用操作符的一些特性:

  1. 当操作符左右两边的数据类型不一致时,会发生隐式转换
  2. 当where查询操作符左边为数值类型时发生了隐式转换,那么对效率影响不大,但还是不推荐这么做。
  3. 当where查询操作符左边为字符类型时发生了隐式转换,那么会导致索引失效,造成全表扫描效率极低。
  4. 字符串转换为数值类型时,非数字开头的字符串会转化为0,以数字开头的字符串会截取从第一个字符到第一个非数字内容为止的值为转化结果。

所以,我们在写SQL时一定要养成良好的习惯,查询的字段是什么类型,等号右边的条件就写成对应的类型。特别当查询的字段是字符串时,等号右边的条件一定要用引号引起来标明这是一个字符串,否则会造成索引失效触发全表扫描。

码海无涯,不进则退,日积跬步,以至千里。本博客所写内容仅为个人在学习和研究MySQL过程中的一些心得体会及总结笔记,仅代表个人观点。本次测试使用的MySQL版本是 5.7.26,随着MySQL版本的更新某些特性可能会发生改变,本文不代表所述观点和结论于MySQL所有版本均准确无误,版本差异请自行甄别。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3032553.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

NGINX SPRING HTTPS证书

服务器&#xff1a;xxx.xxx.xxx.56 客户端器&#xff1a;xxx.xxx.xxx.94##生成服务器证书和密钥容器 keytool -genkey -alias tas-server -keypass 250250 -keyalg RSA -keysize 2048 -validity 3650 -keystore D:\https证书\tas-server.jks -storepass 250250 -dname "C…

【电子学会】2023年12月图形化一级 -- 遇见春天

遇见春天 1. 准备工作 &#xff08;1&#xff09;选择背景Flowers&#xff1b; &#xff08;2&#xff09;删除默认角色小猫&#xff0c;选择角色蝴蝶Butterfly 2、小狗Dot&#xff1b; &#xff08;3&#xff09;角色小狗只保留三个造型&#xff1a;dot-a、dot-b、dot-c&a…

AI应用之智能体介绍

AI应用之智能体介绍 一、LLM介绍二、智能客服应用1&#xff0c;阿里智能能话机器人2&#xff0c;华为对话机器人3&#xff0c;公司基于讯飞知识库和讯飞大模型的智能客服 三、大模型应用平台介绍1&#xff0c;fastgpt2&#xff0c;毕昇3&#xff0c; 字节海外版&#xff08;科学…

Django创建网站的地基

相关文档 1、为新网站创建一个文件夹&#xff08;这里是&#xff1a;locallibrary&#xff09; D:\django>mkdir locallibraryD:\django>cd locallibraryD:\django\locallibrary>dirVolume in drive D is 新加卷Volume Serial Number is B68C-03F7Directory of D:\dj…

01.认识HTML及常用标签

目录 URL&#xff08;统一资源定位系统&#xff09; HTML&#xff08;超文本标记语言&#xff09; 1&#xff09;html标签 2&#xff09;head标签 3&#xff09;title标签 4&#xff09;body标签 标签的分类 DTD文档声明 基础标签 1&#xff09;H系列标签 2&#xff09…

Spring AI默认gpt版本源码探究

Spring AI默认gpt版本源码探究 调试代码 通过调试&#xff0c;可以看到默认mdel为gpt-3.5-turbo 源码探究 进入OpenAiChatClient类查看具体的代码信息 可以看到如下代码&#xff0c;在有参构造方法中可以看到&#xff0c;model默认使用OpenAiApi.DEFAULT_CHAT_MODELpublic…

Unity使用sherpa-onnx实现离线语音合成

sherpa-onnx https://github.com/k2-fsa/sherpa-onnx 相关dll和lib库拷进Unity&#xff0c;官方示例代码稍作修改 using SherpaOnnx; using System; using System.IO; using System.Runtime.InteropServices; using UnityEngine;public class TTS : MonoBehaviour {public st…

【Stylus详解与引入】

文章目录 Stylus详解与引入一、Stylus简介二、Stylus的特性1. 变量2. 嵌套规则3. 混合&#xff08;Mixins&#xff09;4. 函数5. 条件语句和循环 三、Stylus的引入与配置1. 安装Stylus和stylus-loader2. 配置Webpack3. 在Vue项目中使用Stylus4. 编译Stylus代码四、Stylus的性能…

基于51单片机的二氧化碳检测及调节系统仿真

基于51单片机的二氧化碳检测及调节系统 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.二氧化碳传感器测得二氧化碳数据后经过单片机处理。 2.LCD1602实时显示&#xff0c;第一行显示测得的浓度值&#xff0c;第二行显示报警阈值。 3.测…

空间复杂度与链表刷题

"一切的一切都是你自己在感应." 本文索引 空间复杂度复杂度实例实例1实例2实例3 链表题目1. 返回倒数第K个节点2. 链表的回文结构3. 相交链表4. 随机链表的复制5. 环形链表 总结: 前言: 本文主要探究空间复杂度与链表题目讲解 更多文章点击主页: 酷酷学!!! 如果此文对…

探索白啤:清爽与纯净的完善呈现

啤酒的世界色彩斑斓&#xff0c;各种风格迥异的啤酒满足着人们不同的口味需求。而在众多啤酒中&#xff0c;白啤以其与众不同的清爽与纯净口感&#xff0c;成为了许多人的心头好。Fendi club白啤作为精酿啤酒的代表&#xff0c;更是将这种口感发挥到了超卓。 Fendi club白啤的酿…

撤销及变更:31个自然基金项目!

本周投稿推荐 SSCI • 2区社科类&#xff0c;3.0-4.0&#xff08;社科均可&#xff09; EI • 计算机工程类&#xff08;接收广&#xff0c;录用极快&#xff09; SCI&EI • 4区生物医学类&#xff0c;1.5-2.0&#xff08;录用率99%&#xff09; • 1区工程类&#…

命名规范总结Java

小驼峰命名 主要用于变量和方法的命名&#xff0c;当标识符是一个单词时首字母小写&#xff0c;当标识符为多个单词时第一个单词首字母小写&#xff0c;其他单词首字母大写 大驼峰命名 主要用于类(Class)名等。标识符各个单词首字母大写。 全部大写命名 常量名 全部小写命…

凡尔码安全巡检卡替代传统纸质记录卡

建筑行业、物业管理、医院等行业的安全巡检的记录方式通常以&#xff1a;1、纸质记录&#xff1a;巡检人员使用纸质巡检表格&#xff0c;手动填写巡检时间、巡检区域、巡检发现的问题以及处理情况。这种方式简单直接&#xff0c;但可能存在信息记录不完整、易丢失等问题。 2、电…

uniapp音乐播放整理

一、前置知识点 1.1 音频组件控制-uni.createInnerAudioContext() 创建并返回内部 audio 上下文 innerAudioContext 对象。 主要用于当前音乐播放&#xff1b; 1.1.1 innerAudioContext属性 属性类型说明只读平台差异说明srcString音频的数据链接&#xff0c;用于直接播放…

聚观早报 | 乐道L60实车曝光;《萤火突击》公测定档

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 5月11日消息 乐道L60实车曝光 《萤火突击》公测定档 华为官网更新管理层信息 OPPO Reno12 Pro细节曝光 三星电子…

瀚高数据库(HighGoDB)Windows安装使用

1.下载 2.安装 瀚高数据库下载与安装&#xff08;Windows版&#xff09;-CSDN博客 3.连接工具 4.建库、建表操作 瀚高数据库管理工具-CSDN博客 *报错Cant access non-default database&#xff0c;需要右键数据库-设为活动对象 5.导入外部数据&#xff08;迁移、对比&…

Stable Diffusion写真完整教程

前言 最近自己对AI非常痴迷&#xff0c;并且今后也会一直在这个领域深耕&#xff0c;所以就想着先入门&#xff0c;因此花时间研究了一番&#xff0c;还好&#xff0c;出了点小成果&#xff0c;接下来给大家汇报一下。 AI绘画 提到AI绘画&#xff0c;大家可能立马会想到made…

住宅IP代理和数据中心/机房IP代理之间的区别

一、什么是数据中心/机房IP代理&#xff1f; 数据中心/机房IP代理是使用数据中心拥有并进行分配和管理的IP的代理&#xff0c;俗称机房IP代理。 二、数据中心/机房IP代理的特点 与住宅代理通过使用ISP拥有和分配的IP地址的设备路由请求的情况不同&#xff0c;数据中心代理利…

品鉴中的挑战与探索:如何勇敢尝试不同类型的云仓酒庄雷盛红酒

品鉴云仓酒庄雷盛红酒不仅是一种感官的享受&#xff0c;更是一种挑战与探索的过程。不同类型的云仓酒庄雷盛红酒具有各自与众不同的风味和特点&#xff0c;通过勇敢尝试不同类型的红酒&#xff0c;我们可以拓展自己的品鉴视野&#xff0c;发现更多未知的美妙滋味。 首先&#x…