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`()
BEGIN
    DECLARE i INT DEFAULT 0;
    SET autocommit = 0;
    WHILE i < 10000000 DO
        SET i = i + 1;
        SET @str1 = SUBSTRING(MD5(RAND()),1,20);
        -- 每100条数据str2产生一个null值
        IF i % 100 = 0 THEN
            SET @str2 = NULL;
        ELSE
            SET @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 THEN
            COMMIT;
        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所有版本均准确无误,版本差异请自行甄别。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年第九届数维杯大学生数学建模挑战赛B 题思路1.0版本

B题&#xff1a;生物质和煤共热解问题的研究 数维杯分享资料&#xff08;问题一代码论文思路&#xff09;链接&#xff08;18点更新&#xff09;&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1GSv9dkqcY6G-XUnd0sJe_A 提取码&#xff1a;sxjm 题目分析&#xff1…

基于LLM的自行车道CAD

LLM&#xff08;大型语言模型&#xff09;是强大的工具。对于许多人来说&#xff0c;用语言表达愿望通常比浏览复杂的 GUI 更简单。 1、系统简介和环境搭建 urb-x.ch&#xff0c;这是一家专门从事自行车道建设的公司。轨道采用模块化构建块进行独特设计&#xff0c;可以通过多…

有什么实用的还原试卷的app免费?6个软件教你快速进行还原试卷

有什么实用的还原试卷的app免费&#xff1f;6个软件教你快速进行还原试卷 在现代化的教学环境中&#xff0c;使用数字化工具进行试卷还原变得愈发重要。以下是六个实用的、免费的应用程序&#xff0c;它们为还原试卷提供了便捷的解决方案。 FunAI&#xff1a; 这款应用程序可…

QT作业5

1、聊天室 服务器端 //头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #include <QList> #include <QListWidget> #include <QMessageBox> #include <QDebug> #includ…

软件验收计划书-验收规程(Word原件)

编写软件验收计划是软件开发过程中的一个关键步骤&#xff0c;其重要性体现在以下几个方面&#xff1a; 明确验收标准&#xff1a;软件验收计划详细列出了验收的标准、测试方法、测试环境等&#xff0c;确保所有相关人员对验收的期望和要求有清晰的认识。这有助于避免在验收阶段…

Electron+Vue+pyinstaller服务打包

electron环境安装略 1. electron的入口文件配置test.js, 需要在package.json 配置文件中指定main: src/test.js const { app, BrowserWindow } require(electron)const createWindow () > {const win new BrowserWindow({width: 800,height: 600})// win.loadFile(inde…

机器学习面试篇

如何理解机器学习数据集的概念 数据集是机器学习的基础&#xff0c;它包括了用于训练和测试模型所需的数据。数据集通常以矩阵的形式存在&#xff0c;其中每一行代表一个样本&#xff08;或实例&#xff09;&#xff0c;每一列代表一个特征&#xff08;或属性&#xff09;。…

浅谈C++ overload(重载) override(覆盖) overwrite(重写)

目录 1. 名词辨析2 含义解析1 overload重载2 override覆盖3 overwrite重写 3 区别4 代码示例 1. 名词辨析 关于这3个名词的中文翻译&#xff1a; overload翻译为重载&#xff0c;基本是没有歧义的&#xff1b;override和overwrite的翻译&#xff0c;我在参考了cppreference中…

文物藏品库房管理:守护珍贵文化遗产的宝库

一、引言 文物是人类文化遗产的珍贵财富&#xff0c;为了保护和传承科学有效的文物藏品库房管理是必不可少的。本文将介绍文物藏品库房管理的重要性&#xff0c;以及一些常用的管理方法和技术&#xff0c;提高文物保护工作的效果和水平以确保文物的安全与完整性。 二、文物藏品…

语音网关有哪些?

语音网关是一种网络设备&#xff0c;它使得通过传统的电话网络&#xff08;如公共交换电话网络&#xff0c;PSTN&#xff09;和现代的数据网络&#xff08;如互联网或私有数据网络&#xff09;进行的语音通信成为可能。语音网关的主要作用是在模拟或数字电话信号与数据网络的数…

C++ | Leetcode C++题解之第67题二进制求和

题目&#xff1a; 题解&#xff1a; class Solution { public:string addBinary(string a, string b) {string ans;reverse(a.begin(), a.end());reverse(b.begin(), b.end());int n max(a.size(), b.size()), carry 0;for (size_t i 0; i < n; i) {carry i < a.siz…

出海企业哪种组网方案更省事?

对于出海企业而言&#xff0c;建立跨地区的数据传输和协同工作至关重要&#xff0c;以提升运营效率。因此&#xff0c;网络构建变得迫在眉睫。通过构建企业组网&#xff0c;企业能够加强与海外分支、客户和合作伙伴之间的联系&#xff0c;加速海外业务的发展。 然而&#xff0c…

机器学习:基于K-近邻(KNN)、高斯贝叶斯(GaussianNB)、SVC、随机森林(RF)、梯度提升树(GBDT)预测葡萄酒质量

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

mac内存不足怎么清理?有哪些免费的软件工具?

当你的mac电脑使用一段时间之后&#xff0c;你可能就会发现&#xff0c;原本非常流畅的运行开始出现卡顿的现象&#xff0c;此时正是mac内存不足的外在表现。可mac内存不足怎么清理呢&#xff0c;别急&#xff0c;清理内存的方式方法有很多&#xff0c;小编将结合实际情况给大家…

字节跳动测试开发岗 3+1 面经+经验分享(收到offer,入职月薪27K)

现在&#xff0c;招聘黄金时间已经过了&#xff0c;在网上看了很多大佬的面经&#xff0c;也加了很多交流群&#xff0c;受到了很多朋友的提点&#xff0c;今天终于轮到我来分享面经啦&#xff0c;之前面试了几家公司&#xff0c;最后在八月初拿到了字节跳动测试岗的 offer&…

sql-labs(11-20)

1.less-11 1.判断类型 根据测试在使用 " 不会报错&#xff0c; 会报错&#xff0c;所以他是字符型的并且被单引号闭合&#xff0c;而且只有用户 登陆成功才会显示数据。所以先尝试报错注入 2.爆数据库 and updatexml(2,concat(0x7e,(select database()),0x7e),2)-- 3.爆数…

谷歌上架攻略:个人号20人连续14天封闭测试的详细流程及相关注意事项

众所周知&#xff0c;近年来&#xff0c;Google play为了确保应用质量和用户体验&#xff0c;对开发者提出不少新要求。其中&#xff0c;对于个人开发者的一项要求是&#xff0c;自2023年11月13日起&#xff0c;新注册的个人开发者账号在上架正式版应用前&#xff0c;必须经过2…

面试题:数组指针的创建和访问,取地址符号的使用//定义一个指向含有10个整型的数组的指针,如何通过这个指针访问数组?

下面哪个代码是错误的&#xff1f;&#xff08;&#xff09; #include <stdio.h> int main() {int* p NULL;int arr[10] { 0 };return 0; }A. p arr; B. int(*ptr)[10] &arr; C. p &arr[0]; D. p &arr; 上题答案选D&#xff0c;通过如下代…

Spring框架学习-详细

文章目录 1. Spring简介1.1 面向接口编程1.2 Spring简介1.3 Spring体系结构 2 Spring IoC - 基于XML2.1 Sping框架部署&#xff08;IoC&#xff09;2.2 Spring IoC使用2.3 IoC和DI2.4 DI依赖注入Spring容器通过反射方法实现属性注入有三种方式1. set方法注入2. 构造器注入 2.5 …

系统设计 —— 随用户扩展

单服务器设置&#xff1a; 在单服务器设置中&#xff0c;所有内容都运行在一台服务器上。这包括网页应用程序、数据库、缓存等。 1*HQXZgCc5Vh8KooJHwKfzjw.png 图1.1 请求流程 1.最终用户通过域名&#xff08;myurl.com&#xff09;访问网站。请求发送到 DNS&#xff0c;将域名…