首页 > 数据库 >MySQL使用sysdate()导致无法使用索引以及NLJ驱动表结果集增大

MySQL使用sysdate()导致无法使用索引以及NLJ驱动表结果集增大

时间:2024-11-29 15:56:54浏览次数:10  
标签:sysdate zkm rows dtime t2 MySQL NLJ id

 

MySQL使用sysdate()导致无法使用索引以及NLJ驱动表结果集增大

 

MySQL版本:8.0.36

最近遇到一条MySQL的慢语句优化,发现是sysdate()导致的问题。

现在大致模拟一下环境。

 

创建表以及对应索引,如下:

create table zkm(id int,dtime datetime,c3 int);
create index idx_z_dtime on zkm(dtime);
create index idx_z_id on zkm(id);

 

生成随机数据,如下:

--生成随机数据的存储过程
DELIMITER //

CREATE PROCEDURE GenerateData(IN num INT)
BEGIN
    DECLARE i INT DEFAULT 0;
    DECLARE start_time datetime DEFAULT '2000-01-01 00:00:00';
    DECLARE end_time   datetime DEFAULT '2024-11-28 23:59:59';

    WHILE i <= num DO
        INSERT INTO zkm VALUES (i, FROM_UNIXTIME(FLOOR(RAND() * (UNIX_TIMESTAMP(end_time) - UNIX_TIMESTAMP(start_time) + 1)) + UNIX_TIMESTAMP(start_time)), round(100*rand(),0));
        SET i = i + 1;
    END WHILE;
END;
//

DELIMITER ;

--调用生成 10000 条数据
CALL GenerateData(10000);

--删除存储过程
--DROP PROCEDURE IF EXISTS GenerateData;

 

 

现在对比下边前两条SQL,一旦用上sysdate()会立刻使用全表扫描。

嘿!以前都是遇到对表字段做函数处理的时候索引失效的,现在只是个普通的数值就导致了无法使用索引。

(root@localhost 10:19:33) [zkm](1355501)> pager grep -vE "Code 1003"
PAGER set to 'grep -vE "Code 1003"'
(root@localhost 10:19:34) [zkm](1355501)> explain select * from zkm where dtime = '2006-01-05 16:29:01';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | zkm   | NULL       | ref  | idx_z_dtime   | idx_z_dtime | 6       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

(root@localhost 10:19:37) [zkm](1355501)> explain select * from zkm where dtime = date_sub(sysdate(), interval 1 day);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | zkm   | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10001 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

--强制使用索引也不行
--explain select /*+ INDEX(zkm idx_z_dtime) */ * from zkm where dtime = date_sub(sysdate(), interval 1 day);
(root@localhost 10:19:48) [zkm](1355501)> explain select * from zkm force index(idx_z_dtime) where dtime = date_sub(sysdate(), interval 1 day);
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
|  1 | SIMPLE      | zkm   | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10001 |    10.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------------+
1 row in set, 1 warning (0.18 sec)

--普通的值呦
(root@localhost 10:45:21) [zkm](1355701)> select date_sub(sysdate(), interval 1 day) dateValue;
+---------------------+
| dateValue           |
+---------------------+
| 2024-11-28 10:45:31 |
+---------------------+
1 row in set (0.00 sec)

 

而另外一个场景则是导致我实际生产SQL慢的主要原因,那就是NLJ无法有效过滤驱动表数据量导致被驱动表执行次数过多。

还是以上边表为例子,问题如下:

 1 (root@localhost 10:39:26) [zkm](1355701)> explain format=tree select count(*) from zkm t1,zkm t2 where t1.id = t2.id and t2.dtime = date_sub(sysdate(), interval 1 day) and t2.c3=4 \G
 2 *************************** 1. row ***************************
 3 EXPLAIN: -> Aggregate: count(0)  (cost=1030 rows=1)
 4     -> Nested loop inner join  (cost=1020 rows=100)
 5         -> Filter: ((t2.c3 = 4) and (t2.id is not null))  (cost=917 rows=100)
 6             -> Table scan on t2  (cost=917 rows=10001)
 7         -> Filter: (t2.dtime = (sysdate() - interval 1 day))  (cost=0.933 rows=1)
 8             -> Covering index lookup on t1 using idx_z_id (id=t2.id)  (cost=0.933 rows=1)
 9 
10 1 row in set (0.06 sec)

 

在以上的SQL语句中,t2表有两个过滤条件,分别是:

t2.c3=4

t2.dtime = date_sub(sysdate(), interval 1 day)

但是从执行计划看,这两个过滤条件竟然分到了第5行和第7行的Filter执行路径中。

嘶!!

理想情况是第5行的Filter使用过滤条件t2.c3=4 and t2.dtime = date_sub(sysdate(), interval 1 day)后,一行数据都没有,这样被驱动表一次都可以不需要执行。

这使我一度无法理解,放在Oracle里边也是相当炸裂的。

尝试使用with as,子查询改写SQL,都不行。但如果使用CTAS重新生成一张表替换掉t2进行测试的话就没问题。

也猜测了是否是date_sub(sysdate(), interval 1 day)整体类型的问题,使用cast转换了还是不行。

 

不知道尝试多久想放弃的时候,鬼使神差使用now()替换sysdate()之后,您猜怎么着,可以了。。

能走索引了,NLJ中也不会分开到两个Filter了。

仔细看了看sysdate()now()的区别,才执行为啥。(点击链接跳转官网说明)

(root@localhost 11:05:44) [zkm](1355701)> select now(),sysdate(),sleep(2),now(),sysdate();
+---------------------+---------------------+----------+---------------------+---------------------+
| now()               | sysdate()           | sleep(2) | now()               | sysdate()           |
+---------------------+---------------------+----------+---------------------+---------------------+
| 2024-11-29 11:05:59 | 2024-11-29 11:05:59 |        0 | 2024-11-29 11:05:59 | 2024-11-29 11:06:01 |
+---------------------+---------------------+----------+---------------------+---------------------+
1 row in set (2.02 sec)

--now()用上索引了
(root@localhost 14:18:36) [zkm](1356202)> explain select * from zkm where dtime = date_sub(now(), interval 1 day);
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | zkm   | NULL       | ref  | idx_z_dtime   | idx_z_dtime | 6       | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

--多表关联中,NLJ执行路径下,now()也能够减少驱动表结果集
(root@localhost 14:18:37) [zkm](1356202)> explain analyze select count(*) from zkm t1,zkm t2 where t1.id = t2.id and t2.dtime = date_sub(now(), interval 1 day) and t2.c3=4 \G
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(0)  (cost=0.373 rows=1) (actual time=0.0188..0.0189 rows=1 loops=1)
    -> Nested loop inner join  (cost=0.363 rows=0.1) (actual time=0.0173..0.0173 rows=0 loops=1)
        -> Filter: ((t2.c3 = 4) and (t2.id is not null))  (cost=0.26 rows=0.1) (actual time=0.0166..0.0166 rows=0 loops=1)
            -> Index lookup on t2 using idx_z_dtime (dtime=(now() - interval 1 day))  (cost=0.26 rows=1) (actual time=0.0162..0.0162 rows=0 loops=1)
        -> Covering index lookup on t1 using idx_z_id (id=t2.id)  (cost=1.93 rows=1) (never executed)

--多表关联中,now()不走索引的情况
--explain analyze select count(*) from zkm t1,zkm t2 ignore index (idx_z_dtime) where t1.id = t2.id and t2.dtime = date_sub(now(), interval 1 day) and t2.c3=4\G
(root@localhost 14:20:55) [zkm](1356202)> explain analyze select /*+ no_index(t2 idx_z_dtime) */ count(*) from zkm t1,zkm t2 where t1.id = t2.id and t2.dtime = date_sub(now(), interval 1 day) and t2.c3=4\G
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(0)  (cost=1008 rows=1) (actual time=12.1..12.1 rows=1 loops=1)
    -> Nested loop inner join  (cost=1008 rows=1) (actual time=12.1..12.1 rows=0 loops=1)
        -> Filter: ((t2.c3 = 4) and (t2.dtime = <cache>((now() - interval 1 day))) and (t2.id is not null))  (cost=1007 rows=1) (actual time=12.1..12.1 rows=0 loops=1)
            -> Table scan on t2  (cost=1007 rows=10001) (actual time=0.0295..11 rows=10001 loops=1)
        -> Covering index lookup on t1 using idx_z_id (id=t2.id)  (cost=1.03 rows=1) (never executed)

 

now()返回开始执行时候的时间,是一个常量。

sysdate()则返回那个时候sysdate()被调用时候的时间,因此在同一语句中也可以返回不同的值。

简而言之,在SQL语句开始执行后,sysdate()可以理解为一个不确定的值,是一个变量,因此无法使用索引(包括强制),这在官网也提到了。

同样也无法在上边的NLJ执行计划的第5行的Filter中和t2.c3=4一块成为过滤条件,转而在第7行Filter进行了过滤,虽然我觉得这取决于优化器。

也可以通过设置启动时候--sysdate-is-now参数控制让sysdate()等同于now(),但不建议这么做。

重启生效。

vi /etc/my.cnf
sysdate-is-now = on

 

另外,now()是受到 set timestamp 影响的,而sysdate()则不会。

(root@localhost 15:44:57) [mysql](1356557)> SET session TIMESTAMP=UNIX_TIMESTAMP('2024-01-01');
Query OK, 0 rows affected (0.00 sec)

(root@localhost 15:44:58) [mysql](1356557)> SELECT NOW(),SYSDATE();
+---------------------+---------------------+
| NOW()               | SYSDATE()           |
+---------------------+---------------------+
| 2024-01-01 00:00:00 | 2024-11-29 15:45:03 |
+---------------------+---------------------+
1 row in set (0.00 sec)

 

后续经过沟通确认,实际业务上使用now()更加准确,因此将sysdate()换成now()来优化SQL语句。

至此。

标签:sysdate,zkm,rows,dtime,t2,MySQL,NLJ,id
From: https://www.cnblogs.com/PiscesCanon/p/18576903

相关文章

  • mysql约束
    #创建一个表#需求:id为主键,并自增;name不能为空,且不可重复;age年龄在0-120之间;status状态默认为1,gender性别要求无createtableperson(idintprimarykeyauto_incrementcomment'主键',namevarchar(10)notnulluniquecomment'名字',ageintche......
  • mysql备忘录
    安装    yum安装                #centos安装##上传mysql57-community-release-el7-11.noarch.rpmrpm-Uvhmysql57-community-release-el7-11.noarch.rpm##安装mysqlyum-yinstallmysqlmysql-server--nogpgcheck##查看mysql......
  • Sql中SYSDATE函数的使用方法
    在SQL语言中,SYSDATE 是一个非常实用且常见的系统内置函数,尤其在Oracle和MySQL数据库中广泛使用。它主要用来获取服务器当前的日期和时间,这对于进行实时数据记录、审计跟踪、有效期计算等场景特别有用。你且听我慢慢道来。 1.SYSDATE函数的基本概念Oracle中的SYSDATE在Oracl......
  • Z2400034Java+MySQL+spring boot+mybatis学习资源共享平台系统代码 文档分享
    学习资源共享平台的设计与实现1.项目背景与概述2.系统角色与功能3.技术选型与架构系统特点与不足4.运行环境5.系统界面截图6.源码获取1.项目背景与概述本系统是一个基于SpringBoot和MyBatis的学习资源共享平台,旨在为用户提供一个便捷、高效、安全的资源共享与学......
  • Y20030009基于Java+springboot+MySQL+uniapp框架的待办事项提醒微信小程序的设计与实
    待办事项提醒小程序1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取1.摘要随着现代人的工作和生活压力越来越大,人们的精力和时间也越来越有限。在这样的情况下,很容易忘记一些很重要的行程,有时会导致严重的后果,如何处理好自己的待办事项,便成为了一个需......
  • Y20030012基于php+mysql的药店药品信息管理系统的设计与实现 源码 配置 文档
    库存管理系统1.摘要2.系统功能3.功能结构图4.界面展示5.源码获取1.摘要21世纪是信息的时代,信息技术发展突飞猛进。各种信息化管理系统如雨后春笋一样出现。Internet的迅猛发展使其成为全球信息传递与共享的巨大的资源库。越来越多的网络环境下的Web应用系统被建立起......
  • Z2400036 Java+Maven+MySQL+SSM的个人博客系统 代码 文档 PPT
    个人博客系统1.项目概述2.系统功能3.运行环境4.界面展示5.源码获取1.项目概述本博客系统基于SSM(Spring+SpringMVC+MyBatis)框架开发,旨在提供一个功能全面、操作便捷的博客管理平台。系统涵盖了文章发布、评论管理、用户管理以及后台管理等多个模块,满足个人或小......
  • PolarDB MySQL limit m,n Top K问题优化
    现有的limitm,n处理方式堆排序,topK算法归并排序时基于offset和limit做truncateSelf-sharpeninginputfilter算法假如有若干个sortedrun有序数组,则取第K大的元素(这个元素称为cutoffvalue)以及之前的值,其余的值都过滤掉,然后再用这个cutoffvalue值过滤其他sorte......
  • MySQL_索引失效_类型转换
    1.类型转换索引失效场景SELECT*FROMtbl_nameWHEREstr_col=1;列str_col上有建立索引,一个字符串类型的列给一个整数类型的值。问题:为什么会导致索引失效?原因:  MySQL官方文档解释:Thereasonforthisisthattherearemanydifferentstringsthatmayconverttot......
  • Mysql索引底层原理详细讲解
    学习目标Mysql索引底层原理索引数据结构R树B树B+树二叉搜索树(BST)平衡二叉树(AVL树)哈希表(hash)索引的分类空间索引网格索引四叉树索引R树家族索引K-D树索引金字塔索引H3索引主键索引唯一索引单值索引(单列索引)复合索引(组合索引)普通索引全文索引前缀索引覆盖索引......