MySQL 函数触发隐式转换应对策略

文章目录

    • 前言
    • 1. 环境介绍
    • 2. MySQL 5.7
      • 2.1 执行计划
      • 2.2 虚拟列优化
      • 2.3 原理解析
    • 3. MySQL 8.0
      • 3.1 执行计划
      • 3.2 函数索引优化
      • 3.3 原理解析

前言

MySQL 中,当 SQL 索引字段使用了函数的话,会出现隐式转换的问题,导致索引失效,从而导致 SQL 执行效率变慢。本篇文章介绍 MySQL 不同版本此类问题的应对策略。

1. 环境介绍

以下是本篇文章,测试需要准备的表结构和环境信息。

数据库版本:

  • MySQL 5.7.37
  • MySQL 8.0.32

测试表结构:

CREATE TABLE `task_queue` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`instance_id` varchar(50) NOT NULL COMMENT '实例ID',`instance_name` varchar(50) NOT NULL COMMENT '实例名',`create_time` datetime NOT NULL COMMENT '任务创建时间',`end_time` datetime NOT NULL COMMENT '任务结束时间',`instance_type` varchar(10) NOT NULL COMMENT '数据库类型:MySQL、SQLServer',`status` varchar(3) NOT NULL DEFAULT '未完成' COMMENT '任务状态:完成 & 未完成 & 异常',PRIMARY KEY (`id`) USING BTREE,KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

示例 SQL:

select instance_id, instance_name, status from task_queue where DATE_FORMAT(create_time, '%Y-%m-%d') = '2023-09-10'

2. MySQL 5.7

MySQL 5.7 版本新增了一个虚拟列的新特性,可以通过该特性让示例 SQL 可以用到索引。

2.1 执行计划

先看看 SQL 在 5.7 版本的执行计划:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "13.20"},"table": {"table_name": "task_queue","access_type": "ALL","rows_examined_per_scan": 61,"rows_produced_per_join": 61,"filtered": "100.00","cost_info": {"read_cost": "1.00","eval_cost": "12.20","prefix_cost": "13.20","data_read_per_join": "28K"},"used_columns": ["instance_id","instance_name","create_time","status"],"attached_condition": "(date_format(`op_service_db_bak`.`task_queue`.`create_time`,'%Y-%m-%d') = '2023-11-11')"}}
}

因为 create_time 字段使用了函数,导致 SQL 隐式转换,从而导致索引失效。

2.2 虚拟列优化

接下来创建一个虚拟列,该列是基于 create_time 计算创建而来。

alter table task_queue add column v_create_time datetime GENERATED ALWAYS AS (DATE_FORMAT(create_time, '%Y-%m-%d'));

然后为该列添加一个索引:

alter table task_queue add index idx_v_create_time(v_create_time);

此时的表结构如下:

CREATE TABLE `task_queue` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`instance_id` varchar(50) NOT NULL COMMENT '实例ID',`instance_name` varchar(50) NOT NULL COMMENT '实例名',`create_time` datetime NOT NULL COMMENT '任务创建时间',`end_time` datetime NOT NULL COMMENT '任务结束时间',`instance_type` varchar(10) NOT NULL COMMENT '数据库类型:MySQL、SQLServer',`status` varchar(3) NOT NULL DEFAULT '未完成' COMMENT '任务状态:完成 & 未完成 & 异常',`v_create_time` datetime GENERATED ALWAYS AS (date_format(`create_time`,'%Y-%m-%d')) VIRTUAL,PRIMARY KEY (`id`) USING BTREE,KEY `idx_create_time` (`create_time`),KEY `idx_v_create_time` (`v_create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

再次执行 SQL 查看执行计划:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "5.20"},"table": {"table_name": "task_queue","access_type": "ref","possible_keys": ["idx_v_create_time"],"key": "idx_v_create_time","used_key_parts": ["v_create_time"],"key_length": "6","ref": ["const"],"rows_examined_per_scan": 11,"rows_produced_per_join": 11,"filtered": "100.00","cost_info": {"read_cost": "3.00","eval_cost": "2.20","prefix_cost": "5.20","data_read_per_join": "5K"},"used_columns": ["instance_id","instance_name","create_time","status","v_create_time"]}}
}

通过执行计划可以看到,通过创建虚拟列,让隐式转换的 SQL 使用上索引,且未对原 SQL 进行任何修改,只是调整了表结构。

2.3 原理解析

Generated Column 是 MySQL 5.7 引入的新特性,就是数据库中的一个字段,是由其它字段计算而得。我们以官方文档中的例子给予说明。

例如,知道直角三角形的两条直角边,要求斜边的长度。很明显,斜边的长度可以通过两条直角边计算而得,那么,这时候就可以在数据库中只存放直角边,斜边使用 Generated Column 如下所示:

CREATE TABLE triangle (sidea DOUBLE,sideb DOUBLE,sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb))
);
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);
mysql> SELECT * FROM triangle;
+-------+-------+--------------------+
| sidea | sideb | sidec              |
+-------+-------+--------------------+
|     1 |     1 | 1.4142135623730951 |
|     3 |     4 |                  5 |
|     6 |     8 |                 10 |
+-------+-------+--------------------+

Generated Column 的创建语法如下:

col_name data_type [GENERATED ALWAYS] AS (expr)[VIRTUAL | STORED] [NOT NULL | NULL][UNIQUE [KEY]] [[PRIMARY] KEY][COMMENT 'string']

需要注意的是 Generated Column 有两种模式,分别是 VIRTUAL 和 STORED,默认模式为 VIRTUAL。

  • VIRTUAL 是将字段的计算逻辑存储在数据字典中,既元数据。
  • STORED 是会将计算而得的数据,存储到磁盘中,会得到持久化存储。

Generated Column 字段支持创建二级索引,这也是能解决函数计算造成的索引失效的原因。

推荐阅读:13.1.18.7 CREATE TABLE and Generated Columns

3. MySQL 8.0

MySQL 8.0 提供了一个函数索引新特性,相比 5.7 的虚拟列,可以更方便的解决隐式转换问题。

3.1 执行计划

以下是示例 SQL 在 8.0 版本中的执行计划:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "6.35"},"table": {"table_name": "task_queue","access_type": "ALL","rows_examined_per_scan": 61,"rows_produced_per_join": 61,"filtered": "100.00","cost_info": {"read_cost": "0.25","eval_cost": "6.10","prefix_cost": "6.35","data_read_per_join": "28K"},"used_columns": ["instance_id","instance_name","create_time","status"],"attached_condition": "(date_format(`test`.`task_queue`.`create_time`,'%Y-%m-%d') = '2023-11-11')"}}
}

与 MySQL 5.7 版本相同,由于隐式转换,无法使用 create_time 字段的索引。

3.2 函数索引优化

MySQL 8.0 函数索引创建方式非常简单,如下:

alter table task_queue add index idx_func_create_time((DATE_FORMAT(create_time, '%Y-%m-%d')));

通过如下 SQL 可以查看函数索引:

mysql>select table_name,index_name,seq_in_index,column_name,is_visible,expression from information_schema.statistics where table_name='task_queue';
+------------+----------------------+--------------+-------------+------------+-------------------------------------------------+
| TABLE_NAME | INDEX_NAME           | SEQ_IN_INDEX | COLUMN_NAME | IS_VISIBLE | EXPRESSION                                      |
+------------+----------------------+--------------+-------------+------------+-------------------------------------------------+
| task_queue | idx_create_time      |            1 | create_time | YES        | NULL                                            |
| task_queue | idx_func_create_time |            1 | NULL        | YES        | date_format(`create_time`,_utf8mb4\'%Y-%m-%d\') |
| task_queue | PRIMARY              |            1 | id          | YES        | NULL                                            |
+------------+----------------------+--------------+-------------+------------+-------------------------------------------------+

再次执行示例 SQL 查看执行计划:

{"query_block": {"select_id": 1,"cost_info": {"query_cost": "0.35"},"table": {"table_name": "task_queue","access_type": "ref","possible_keys": ["idx_func_create_time"],"key": "idx_func_create_time","used_key_parts": ["date_format(`create_time`,_utf8mb4'%Y-%m-%d')"],"key_length": "43","ref": ["const"],"rows_examined_per_scan": 1,"rows_produced_per_join": 1,"filtered": "100.00","cost_info": {"read_cost": "0.25","eval_cost": "0.10","prefix_cost": "0.35","data_read_per_join": "520"},"used_columns": ["instance_id","instance_name","create_time","status","date_format(`create_time`,_utf8mb4'%Y-%m-%d')"]}}
} 

通过执行计划可以看到,通过创建函数,让隐式转换的 SQL 使用上索引,且未对原 SQL 进行任何修改,只是调整了表结构。相比于 MySQL 5.7 版本的虚拟列更新便捷。

3.3 原理解析

MySQL 8.0 的函数索引,比较好理解,就是根据用户指定的计算规则,维护一个二级索引,来应对更复杂的查询。

以下是函数索引的使用限制。

  1. 主键不支持函数索引,因为主键以实际列进行存储,而函数索引是作为虚拟列存在的
  2. 在有主键的情况下,唯一索引支持函数索引;但在无主键的情况下,被提升为主键的唯一索引不支持
  3. 外键不支持函数索引
  4. 空间索引和全文索引不支持函数索引
  5. 函数索引不能直接使用列前缀,可以通过SUBSTRING()和CAST()来替代
  6. 在删除列之前,要先删除相关的函数索引

推荐阅读:Functional Key Parts

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# CAD界面-自定义窗体(三)

运行环境 vs2022 c# cad2016 调试成功 一、引用 二、开发代码进行详细的说明 初始化与获取AutoCAD核心对象: Database db HostApplicationServices.WorkingDatabase;:这行代码获取当前工作中的AutoCAD数据库对象。在AutoCAD中,所有图形数…

《短链接--阿丹》--技术选型与架构分析

整个短链接专栏会持续更新。有兴趣的可以关注一下我的这个专栏。 《短链接--搭建解析》--立项+需求分析文档-CSDN博客 阿丹: 其实整套项目中的重点,根据上面的简单需求分析来看,整体的项目难题有两点。 1、快速的批量生成短链,并找到对应的存储。 并且要保持唯一性质。…

深入探索MySQL的虚拟列:发展、原理与应用

当我们谈论数据库优化时,经常会遇到各种技术和策略。其中,MySQL的虚拟列(也被称为生成列或存储列)是一个引人注目的特性。它不仅可以帮助开发者提高查询效率,还能为数据表提供额外的计算功能,而无需真正改变…

【Linux驱动】块设备驱动(二)—— 块设备读写(使用请求队列)

块设备的操作函数并没有类似于字符驱动中的read 和write函数,要实现读写操作,只能在请求处理函数中实现。这就分为两种,是否要使用请求队列,请求队列的主要作用是管理和调度IO请求。在以下情况中,一般需要用到请求队队…

跑路页面HTML源码

简单的HTMLJSCSS,记事本修改内容,喜欢的朋友可以下载 https://download.csdn.net/download/huayula/88811984

vivado RTL综合中的多线程

RTL综合中的多线程 在多处理器系统上,RTL合成默认情况下利用多个CPU核心(最多四个)来加快编译时间。同时运行的线程的最大数量会有所不同,具体取决于处理器的数量可在系统、操作系统和流程阶段使用(请参阅Vivado Desi…

Thinkpad E550 安装 Ubuntu

参考:使用U盘安装Ubuntu20.04 Thinkpad E550 使用 enterf1进入 bios, 使用 f12 进入 boot 选项。不知道这个和系统有啥关系,感觉之前不是这样。。。 下载镜像 下载镜像 22.04 可以下载相应的 torrent 文件,然后用 百度网盘或者…

HTTP1.1、HTTP2、HTTP3

HTTP1.1 HTTP/1.1 相比 HTTP/1.0 性能上的改进: 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去&…

Linux基础-磁盘分区

1.Linux磁盘分区方案 1.根分区/ 2./boot 3./var/log 4.swap虚拟交换分区,当物理内存不够用,需要用swap 5.自定义分区: ①创建一个目录/etc/mysql装mysql数据库软件 ②再创建一个目录/backup存放mysql数据库备份文件 2.磁盘分区划分 第一个…

(22)删除指定的数

文章目录 每日一言题目解题思路代码结语 每日一言 你热爱生命吗?那就别浪费时间,因为时间是组成生命的材料。——富兰克林 题目 先输入10个整数存放在数组中,再输入一个整数n,删除数组中所有等于n的数字,打印数组中剩…

在VM虚拟机上搭建MariaDB数据库服务器

例题:搭建MariaDB数据库服务器,并实现主主复制。 1.在二台服务器中分别MariaDB安装。 2.在二台服务器中分别配置my.cnf文件,开启log_bin。 3.在二台服务器中分别创建专用于数据库同步的用户replication_user,并授权SLAVE。&#x…

Matplotlib绘制炫酷柱状图的艺术与技巧【第60篇—python:Matplotlib绘制柱状图】

文章目录 Matplotlib绘制炫酷柱状图的艺术与技巧1. 簇状柱状图2. 堆积柱状图3. 横向柱状图4. 百分比柱状图5. 3D柱状图6. 堆积横向柱状图7. 多系列百分比柱状图8. 3D堆积柱状图9. 带有误差线的柱状图10. 分组百分比柱状图11. 水平堆积柱状图12. 多面板柱状图13. 自定义颜色和样…

c#string方法对比

字符串的截取匹配操作在开发中非常常见,比如下面这个示例:我要匹配查找出来字符串数组中以“abc”开头的字符串并打印,我下面分别用了两种方式实现,代码如下: using System; namespace ConsoleApp23{ class Progra…

aidl复杂流程封装

1 aidl相关困扰点 1 制作步骤复杂,先定义然后编译,然后复制,两边都要一一对应 2 增加回调,自定义对象流程更加麻烦,还要处理对象数据流是 in 还是out。 3 一方异常怎么办,虽然服务端可以用 RemoteCallbackL…

UI自动化中元素无法定位问题解决方法

元素无法定位问题解决方法 1、display屏蔽元素2、iframe内元素无法定位3、 根据部分元素属性定位4、页面跳转到新的标签页,或者弹出的警告框等6、使用WebDriver调用JavaScript代码代替无法实现的功能 1、display屏蔽元素 分析一下html的display属性,可以…

计算机网络(复习资料)

1.互联网的两个重要基本特点 连通性和共享性 2.计算机网络由若干节点和连接这些节点的链路组成 3.有多个网络通过路由器相互连接起来,构成一个更大的计算机网络称为互联网 4.网络把许多计算机连接在一起,互联网把许多网络通过一些路由器连接在一起,与网络相连的计算机称为…

web应用课——(第五讲:React)

目录 一、配置环境 二、ES6语法补充 三、Components 四、组合Components 五、路由 六、Redux 一、配置环境 感谢z神(zst_2001)的分享: git下载与安装Node.js下载与安装React下载与安装 二、ES6语法补充 使用bind()函数绑定this取值&…

haiku实现TemplatePairStack类

TemplatePairStack是实现蛋白质结构模版pair_act特征表示的类: 通过layer_stack.layer_stack(c.num_block)(block) 堆叠c.num_block(配置文件中为2)block 函数,每个block对输入pair_act 和 pair_mask执行计算流程:TriangleAttention —> dropout ->TriangleAttentio…

Retrofit源码分析及理解

参考文档: 12W字;2022最新Android11位大厂面试专题(一) - 掘金 Retrofit 版本号:2.9.0 Retrofit简单来说,就是对OkHttp上层进行了封装,已达到用户方便使用管理网络请求的目的。 Retrofit内部有…

力扣热门100题刷题笔记 - 10. 正则表达式匹配

力扣热门100题 - 10. 正则表达式匹配 题目链接:10. 正则表达式匹配 题目描述: 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符 * 匹配零个或多个前面的那一个元素 所谓匹配&#xff…