返回顶部
首页 > 资讯 > 数据库 >MySQL性能优化:MySQL中的隐式转换造成的索引失效
  • 606
分享到

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

MySQL性能优化:MySQL中的隐式转换造成的索引失效 2020-08-13 13:08:29 606人浏览 猪猪侠
摘要

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

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

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

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


数据准备

首先使用存储过程生成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万条数据,我用了33分钟才跑完(实际时间跟你电脑硬件配置有关)。这里贴几条生成的数据,大致长这样。

img

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.005秒,在千万级的数据量下这样的结果可以判定这三条SQL性能基本没差别了。但是第三条SQL,多次测试耗时基本在4.5~4.8秒之间。

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

img

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

仔细观察你会发现,34两条SQL查询的字段num2varchar类型的,查询条件等号右边加引号的第4条SQL是用到索引的,那么是查询的数据类型和字段数据类型不一致造成的吗?如果是这样那12两条SQL查询的字段num1int类型,但是第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后进行比较,如果另外一个参数是浮点数,则会把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;进行查询:

img

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

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

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

img

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

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

img

分析和总结

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

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

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


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

首发地址:https://www.guitu18.com/post/2019/11/24/61.html

您可能感兴趣的文档:

--结束END--

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

本文链接: https://lsjlt.com/news/3512.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • MySQL性能优化:MySQL中的隐式转换造成的索引失效
    数据库优化是一个任重而道远的任务,想要做优化必须深入理解数据库的各种特性。在开发过程中我们经常会遇到一些原因很简单但造成的后果却很严重的疑难杂症,这类问题往往还不容易定位,排查费时费力最后发现是一个很小的疏忽造成的,又或者是因为不...
    99+
    2020-08-13
    MySQL性能优化:MySQL中的隐式转换造成的索引失效
  • MySQL索引失效之隐式转换的问题
    目录常见索引失效:一、常见索引失效场景1、条件字段函数操作2、条件字段运算操作3、隐式类型转换4、隐式字符编码转换二、类型转换1、字符串转整型2、时间类型转换常见索引失效: 1. 条...
    99+
    2024-04-02
  • MySQL隐式类型转换导致索引失效
    今天发现一个问题,where条件的列上明明有索引,但是执行计划还是走全表扫描mysql>  explain select task_id&n...
    99+
    2024-04-02
  • mysql隐式转换索引失效怎么解决
    明确数据类型:确保在创建表时,将字段的数据类型定义为与查询条件中的数据类型一致。 使用合适的函数:在查询中使用函数时,可能会...
    99+
    2024-04-23
    mysql
  • MySQL隐式类型转换导致索引失效的解决
    目录问题 复现 隐式转换 总结 参考 问题 在工作中发现,有一个接口只执行一条SQL查询语句,并且SQL明明使用了主键列,但是速度很慢。 在MySQL中EXPLAINN后发现,执行...
    99+
    2024-04-02
  • MySQL索引失效后隐式转换的问题这么解决
    MySQL索引失效后隐式转换的问题这么解决,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。常见索引失效: 条件索引字段"不干净":函数操作、运算操作 隐式...
    99+
    2023-06-26
  • 浅谈MySql整型索引和字符串索引失效或隐式转换问题
    目录问题概述问题重现问题引申结论问题概述 今天在上班时,DBA突然找出来一段sql,表示该sql存在隐式转换,不走索引。经过我们的查看后,发现是类型varchar的字段, 我们使用条...
    99+
    2024-04-02
  • 如何解决MySql整型索引和字符串索引失效或隐式转换问题
    这篇文章主要为大家展示了“如何解决MySql整型索引和字符串索引失效或隐式转换问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决MySql整型索引和字符串索引失效或隐式转换问题”这篇文章...
    99+
    2023-06-25
  • 怎么进行MySQL性能优化中的索引优化
    本篇文章为大家展示了怎么进行MySQL性能优化中的索引优化,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。大家都知道索引对于数据访问的性能有非常关键的作用,都知道索引...
    99+
    2024-04-02
  • MySQL优化之避免索引失效的方法
    在上一篇文章中,通过分析执行计划的字段说明,大体说了一下索引优化过程中的一些注意点,那么如何才能避免索引失效呢?本篇文章将来讨论这个问题。 避免索引失效的常见方法 1.对于复合索引的使用,应按照索引建立的顺序使用,尽量不要跨列(最...
    99+
    2018-05-25
    MySQL优化之避免索引失效的方法
  • Mysql 5.6 "隐式转换"导致的索引失效和数据不准确的问题
    背景 在一次进行SQl查询时,我试着对where条件中vachar类型的字段去掉单引号查询,这个时候发现这条本应该很快的语句竟然很慢。这个varchar字段有一个复合索引。其中的总条数有58989,甚...
    99+
    2022-05-17
    Mysql 5.6隐式转换导致的索引失效 Mysql 5.6隐式转换
  • Mysql 5.6 "隐式转换"导致的索引失效和数据不准确的解决方法
    这篇文章主要介绍了Mysql 5.6 "隐式转换"导致的索引失效和数据不准确的解决方法,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了...
    99+
    2024-04-02
  • MySQL性能优化之如何高效正确的使用索引
    实践是检验真理的唯一途径,本篇只是站在索引使用的全局来定位的,你只需要通读全篇并结合具体的例子,或回忆以往使用过的地方,对整体有个全面认识,并理解索引是如何工作的,就可以了。在后续使用索引,或者优化索引时,可以从这些...
    99+
    2022-05-14
    MySQL 索引 MySQL 优化索引 MySQL 高效使用索引
  • MySQL索引优化的性能分析和总结
    本篇内容主要讲解“MySQL索引优化的性能分析和总结”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL索引优化的性能分析和总结”吧!案例分析我们先简单了解...
    99+
    2024-04-02
  • 谈谈MySQL中的隐式转换
    工作过程中会遇到比较多关于隐式转换的案例,隐式转换除了会导致慢查询,还会导致数据不准。本文通过几个生产中遇到的案例来。 基础知识 关于比较运算的原则,MySQL官方文档的描述: https://dev.mysql.c...
    99+
    2022-05-25
    MySQL 转换 MySQL 隐式转换
  • MySQL查询性能优化七种方式索引潜水
    目录前言: 有读者可能会一脸懵? 啥是索引潜水? 你给起的名字的吗?有没有索引蛙泳? 这个名字还真不是我起的,今天要讲的知识点就叫索引潜水(Index dive) 。 先要...
    99+
    2022-11-13
    MySQL查询性能优化 MySQL索引潜水
  • MySQL中Order By索引的优化
    本篇内容介绍了“MySQL中Order By索引的优化”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 在...
    99+
    2024-04-02
  • MySQL中的索引如何优化
    这篇文章主要介绍了MySQL中的索引如何优化的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇MySQL中的索引如何优化文章都会有所收获,下面我们一起来看看吧。使用索引优化索引是数...
    99+
    2023-03-01
    mysql
  • Mysql索引优化的方式有哪些
    MySQL索引优化的方式有以下几种:1. 选择正确的索引类型:MySQL支持多种索引类型,包括B-tree索引、哈希索引、全文索引等...
    99+
    2023-10-23
    Mysql
  • MySQL索引优化之不适合构建索引及索引失效的几种情况详解
    目录结论不建议建立索引的场景索引失效的场景小结结论 具体案例下文有详尽描述 不适合建立索引的场景: 数据量比较小的表不建议建立索引有大量重复数据的字段上不建议建立索引(类似:性别字段)需要进行频繁更新的表不建议建立索引w...
    99+
    2022-07-29
    MySQL 索引优化 MySQL 不适合构建索引的场景
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作