单行数据查询之谜:为什么慢?细解查询长时间不返回和查询慢

2023年 7月 24日 31.1k 0

本文为《MySQL归纳学习》专栏的第四篇文章,同时也是关于《MySQL查询》知识点的第四篇文章。

往期回顾:

MySQL玩转指南:探秘Server层组件及权限校验实践

MySQL 中的 count 你用

你真的了解MySQL的排序吗?全字段排序与rowid排序大揭秘

单行数据查询速度缓慢,你有没有想过其中的原因是什么?本文深入分析了单行数据查询为何会变慢的两大类原因:查询长时间不返回和查询慢。我们将揭开这个谜团,并探索其中的细致原因。

首先来看一下这张思维导图,对本文内容有个直观的认识。

img

接下来进入正文。

一般情况下,如果我跟你说查询性能优化,你首先会想到一些复杂的语句,想到查询需要返回大量的数据。但有些情况下,“查一行”,也会执行得特别慢。

CREATE TABLE `t_color` ( `id` int(11) NOT NULL, `c` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;

delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i select * from t_color where id=1 lock in share mode; 

由于访问 id=1 这个记录时要加读锁,如果这时候已经有一个事务在这行记录上持有一个写锁,我们的 select 语句就会被堵住。

复现步骤

begin;
update t_color set c=c+1 where id=1;

select * from t_color where id=1 lock in share mode;

img

执行 show processlist 命令。

img

如果你用的是 MySQL 5.7 版本,可以通过 sys.innodb_lock_waits 表查到。

select * from sys.innodb_lock_waits where locked_table='`mysql_db`.`t_color`'G

img

上图中有这样两个参数:sql_kill_blocking_query、sql_kill_blocking_connection,前者表示停止 19 号线程当前正在执行的语句,而这个方法其实是没有用的。因为占有行锁的是 update 语句,这个语句已经是之前执行完成了的,现在执行 KILL QUERY,无法让这个事务去掉 id=1 上的行锁。

尝试执行 KILL QUERY 19,没有效果,如下图所示:

img

因为 blocking_pid 为 19,所以执行 kill 19,直接断开这个连接,这里隐含的一个逻辑就是,连接被断开的时候,会自动回滚这个连接里面正在执行的线程,也就释放了 id=1 上的行锁。

查询慢

无索引 limit 1

select * from t_color where c=50000 limit 1;

由于字段 c 上没有索引,这个语句只能走 id 主键顺序扫描,因此需要扫描 5 万行。

此处我们需要通过设置慢查询,可以看到扫描了多少数据行。

开启慢查询的两个步骤,全局变量设置:

  • 将 slow_query_log 全局变量设置为“ON”状态
  • mysql> set global slow_query_log='ON'; 
    
  • 查询超过0秒就记录
  • set global long_query_time=0;
    
  • 查看配置效果:

    mysql> show variables like 'slow_query%';
    mysql> show variables like 'long_query_time';
    

    img

    执行上述查询语句后,查看日志(注意,需要使用sudo命令才可以有权限看日志):

    img

    扫描行数多,所以执行慢。

    主键索引 一致性读,undolog空间激增

    如果根据主键来查询单行数据呢?会不会出现很慢的情况。

    mysql> select * from t_color where id=1
    

    虽然扫描行数是 1,但执行时间却长达 1.2秒。

    img

    如果我把这个 slow log 的截图再往下拉一点,你可以看到下一个语句,select * from t where id=1 lock in share mode,执行时扫描行数也是 1 行,执行时间是 0.08 毫秒。

    img

    复现过程

    start transaction with consistent snapshot;

    delimiter ;;
    create procedure iudata()
    begin
    declare i int;
    set i=1;
    while(i

    相关文章

    Oracle如何使用授予和撤销权限的语法和示例
    Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
    下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
    社区版oceanbase安装
    Oracle 导出CSV工具-sqluldr2
    ETL数据集成丨快速将MySQL数据迁移至Doris数据库

    发布评论