GreatSQL 的刷新锁

news/2024/11/14 21:40:10/文章来源:https://www.cnblogs.com/greatsql/p/18329441

GreatSQL 的刷新锁

file

前言

因为运维小伙伴执行dump备份命令,导致数据库卡住,很多会话都在waiting for table flush,基于这一故障,我对GreatSQL的刷新锁进行了研究。感兴趣的小伙伴请随我一探究竟吧。

刷新锁的症状

刷新锁问题的主要症状是数据库会进入嘎然而止的状态,所有需要使用部分或全部表的新查询都停下来等待刷新锁。要寻找的信号如下:

1.新查询的查询状态为Waiting for table flush。这可能出现在所有新查询中,也可能只出现在访问特定表的新查询中。

2.数据库连接数增多,最终可能由于连接数用尽,新连接失败。

3.至少有一个查询的运行时间晚于最早的刷新锁请求。

4.进程列表中可能有flush table语句,也可能flush table语句已经超时(超过lock_wait_timeout设置)或被取消(会话被Ctr +C 终止或被kill)。

刷新锁构建

本实验使用的GreatSQL版本: 8.0.32-25 GreatSQL (GPL)。

创建四个连接,第一个连接执行一个慢查询,第二个连接用于执行flush tables语句,第三个连接执行第一个连接中慢查询语句相关表的快速查询。第四个连接执行其他表的查询和插入:

Connection 1> select count(*) ,sleep(100) from t1;
Connection 2> flush tables;   (flush tables with read lock;)
Connection 3> select count(*) from t1;
Connection 4> select count(*) from t2;
​             insert into t2 values(5,'a');

刷新锁争用问题诊断及解决

1.flush tables 实验

Connection 2 执行flush tables 时 ,Connection 3 受阻塞,Connection 4成功执行。

使用sys.session视图来输出各会话运行情况,也可以使用show processlist 来展示。默认输出是按执行时间降序排列,这让 查询刷新锁争用之类的问题变得容易。

[root@GreatSQL][test]>select thd_id,conn_id,state,current_statement,statement_latency from sys.session where command='Query';
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
| thd_id | conn_id | state                   | current_statement                                                 | statement_latency |
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
|    116 |      61 | User sleep              | select count(*),sleep(100) from t1                                | 10.81 s           |
|    117 |      62 | Waiting for table flush | flush tables                                                      | 8.15 s            |
|    109 |      57 | Waiting for table flush | select count(*) from t1                                           | 3.91 s            |
|    118 |      63 | NULL                    | select thd_id,conn_id,state,cu ... .session where command='Query' | 71.49 ms          |
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
4 rows in set (0.07 sec)

从上面会话的查询结果可以看出,flush tables会话的状态是Waiting for table flush,在它之前有一个运行时间较长的查询,这是阻塞flush tables完成的查询。第三个查询的状态也是Waiting for table flush,说明flush tables语句又阻塞了其他查询。

而Connection4 未受影响,说明flush tables不影响其他表的读写操作。

当等待刷新锁成为问题时,这意味着有一条或多条查询阻塞了flush tables 语句获得刷新锁。由于flush tables语句需要一个排他锁,因此又会阻塞后续会话对相关表的共享锁或排他锁。

手动Ctr+C中断flush tables 会话后,再次查询各会话的运行情况。

[root@GreatSQL][test]>select thd_id,conn_id,state,current_statement,statement_latency from sys.session where command='Query';
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
| thd_id | conn_id | state                   | current_statement                                                 | statement_latency |
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
|    116 |      61 | User sleep              | select count(*),sleep(100) from t1                                | 20.10 s           |
|    109 |      57 | Waiting for table flush | select count(*) from t1                                           | 13.19 s           |
|    118 |      63 | NULL                    | select thd_id,conn_id,state,cu ... .session where command='Query' | 68.14 ms          |
+--------+---------+-------------------------+-------------------------------------------------------------------+-------------------+
3 rows in set (0.07 sec)

从查询结果可以看出,被flush table阻塞的查询依然还被阻塞着,这时候解决问题的办法就是结束第一个阻塞flush tables会话的慢查询。

2.flush table with read lock实验

Connection2 执行flush table with read lock 时,Connection 3 受阻塞,Connect 4的select成功,insert 被阻塞。

使用sys.session视图来输出各会话运行情况

[root@GreatSQL][test]>select thd_id,conn_id,state,current_statement,statement_latency from sys.session where command='Query';
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
| thd_id | conn_id | state                        | current_statement                                                 | statement_latency |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
|    116 |      61 | User sleep                   | select count(*),sleep(100) from t1                                | 52.74 s           |
|    117 |      62 | Waiting for table flush      | flush table with read lock                                        | 26.36 s           |
|    109 |      57 | Waiting for table flush      | select count(*) from t1                                           | 22.00 s           |
|    124 |      69 | Waiting for global read lock | insert into t2 values(8,'b')                                      | 6.01 s            |
|    118 |      63 | NULL                         | select thd_id,conn_id,state,cu ... .session where command='Query' | 82.90 ms          |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
5 rows in set (0.09 sec)

从上面结果看出, flush table with read lock 的会话状态为Waiting for table flush,Connection3 状态同样为Waiting for table flush,而Connect4的状态为Waiting for global read lock。

手动Ctr+C中断flush tables 会话后,再次查询各会话的运行情况。

[root@GreatSQL][test]>select thd_id,conn_id,state,current_statement,statement_latency from sys.session where command='Query';
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
| thd_id | conn_id | state                        | current_statement                                                 | statement_latency |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
|    116 |      61 | User sleep                   | select count(*),sleep(100) from t1                                | 1.37 min          |
|    109 |      57 | Waiting for table flush      | select count(*) from t1                                           | 51.58 s           |
|    124 |      69 | Waiting for global read lock | insert into t2 values(8,'b')                                      | 35.57 s           |
|    118 |      63 | NULL                         | select thd_id,conn_id,state,cu ... .session where command='Query' | 65.26 ms          |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
4 rows in set (0.06 sec)

发现Connection 3,Connection4 仍然受到阻塞。

Connection 1 查询结束后查询各会话运行情况

[root@GreatSQL][test]>select thd_id,conn_id,state,current_statement,statement_latency from sys.session where command='Query';
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
| thd_id | conn_id | state                        | current_statement                                                 | statement_latency |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
|    124 |      69 | Waiting for global read lock | insert into t2 values(8,'b')                                      | 57.44 s           |
|    118 |      63 | executing                    | select thd_id,conn_id,state,cu ... .session where command='Query' | 2.06 ms           |
+--------+---------+------------------------------+-------------------------------------------------------------------+-------------------+
2 rows in set (0.06 sec)

Connection 3 成功执行,Connection 4的insert 仍然受到阻塞。

显示执行unlock tables 命令后,Connection4的insert才执行完成。

实验结论:

由上面两个实验得出,诊断刷新锁争用的问题时,只要有会话处于 Waiting for table flush状态,说明曾发生过刷新表的操作,无论当前能否看到flush tables的相关会话,而通常处于Waiting for table flush状态的会话之前发生的慢查询都有可能是造成 后续阻塞的原因。

flush tables with read lock语句要获取的全局读锁,在等待获取锁时,症状与flush tables语句差不多,不同的是:

1.flush tables with read lock等待获取锁及得到锁之后,都会阻止所有表的写入,而flush tables只是在执行过程中持有锁,它不会阻止长查询之外的其他表写操作

2.flush tables with read lock需要通过unlock tables 显示释放锁,而flush tables不需要。

为什么flush table或者flush tables with read lock 会话都结束了,后续的查询还是会被阻塞呢?

这是低版本表定义缓存(TDC)的原因,这两条命令都会close all open tables,将表版本推高(refresh_version +1), 但因为长查询线程的存在,导致旧表无法被close,在访问旧表时都会认为是旧版本,等待 TABLE cache flush,而refresh_version 的推高是不可逆的结果,也就是说即使发出flush table或flush tables with read lock 的会话中断了 ,但是实际产生的 TABLE flush 的效果还是存在的。

另外这个症状与隔离级别关系不大,笔者测试了READ COMMITTED, REPEATABLE READ两种隔离级别,症状都相同。

通常除了手动发出这两个命令,使用mysqldump工具进行备份时也会发出这两个命令。

mysqldump备份加哪种选项会触发命令flush tables

打开general log, 进行多次dump测试实验,发现有以下几种情况会触发flush tables命令

1.--flush-logs,--single-transaction 一起使用时,触发flush tables,flush tables with read lock

2.--source-data 不和--single-transaction搭配使用时,触发FLUSH /*!40101 LOCAL */ TABLES, FLUSH TABLES WITH READ LOCK

3.--flush-logs,--single-transaction,--source-data 这三个选项同时使用时,会触发FLUSH /*!40101 LOCAL */ TABLES, FLUSH TABLES WITH READ LOCK

DBA小伙伴要熟悉备份工具各个选项或者选项组合使用时带来效果,尽量避免在业务高峰进行备份操作。

结语

Flush table 的功能是关闭所有已经打开的表,强制关闭所有正在使用的表,然而,正在使用的表对象是不能关闭的,所以Flush Tables操作会被正在运行的SQL请求阻塞,而在Flush table 之后的SQL请求又会被Flush table会话阻塞,即使Flush table会话被取消了,这些发生在Flush table之后的SQL请求也还是会被阻塞。所以当会话出现大量waiting for table flush时,无论当前是否还存在flush table 命令,查询耗时比这些waiting会话更久的慢查询,将其kill掉才能解决问题。


Enjoy GreatSQL 😃

关于 GreatSQL

GreatSQL是适用于金融级应用的国内自主开源数据库,具备高性能、高可靠、高易用性、高安全等多个核心特性,可以作为MySQL或Percona Server的可选替换,用于线上生产环境,且完全免费并兼容MySQL或Percona Server。

相关链接: GreatSQL社区 Gitee GitHub Bilibili

GreatSQL社区:

社区博客有奖征稿详情:https://greatsql.cn/thread-100-1-1.html

image-20230105161905827

技术交流群:

微信:扫码添加GreatSQL社区助手微信好友,发送验证信息加群

image-20221030163217640

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

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

相关文章

【AI模型】PPT生成

一、天工AIhttps://www.tiangong.cn/ 先对话进行提纲生成,然后可以编辑提纲,再进行PPT生成 生成完毕后,可以直接点击导出下载 二、星火讯飞 讯飞智文 生成的PPT相比天工的要略微简单,没有配图信息 同样可以导出下载

一个基于 SpringBoot + Vue 复刻高仿B站的视频网站!

Teriteri —— 一个采用前后端分离的模式,参考 Bilibili PC 端,基于 SpringBoot + Vue3 实现的弹幕视频网站。大家好,我是 Java陈序员。 今天,给大家介绍一个开源的视频网站,复刻高仿B站!关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算…

VirtualBox 7.0.20 (macOS, Linux, Windows) - 开源跨平台虚拟化软件

VirtualBox 7.0.20 (macOS, Linux, Windows) - 开源跨平台虚拟化软件VirtualBox 7.0.20 (macOS, Linux, Windows) - 开源跨平台虚拟化软件 Oracle VM VirtualBox 7 请访问原文链接:https://sysin.org/blog/virtualbox-7/,查看最新版。原创作品,转载请保留出处。VirtualBox 简…

C# 推荐一种开机自启动的方式

直接写到用户开机自启动目录里,系统开机会带着一起启动。cmd-shell:startup可验证是否成功生成快捷方式到启动目录。概述(Overview)网上多数搜索结果以注册表设置为优先,这个方法需要管理员权限,实际工作中可能并不适用。这个方法是直接写到用户开机自启动目录里,系统开机会…

70%的人都答错了的面试题,vue3的ref是如何实现响应式的?

最近在我的vue源码交流群有位面试官分享了一道他的面试题:vue3的ref是如何实现响应式的?下面有不少小伙伴回答的是Proxy,其实这些小伙伴只回答对了一半。前言 最近在我的vue源码交流群有位面试官分享了一道他的面试题:vue3的ref是如何实现响应式的?下面有不少小伙伴回答的…

《最新出炉》系列入门篇-Python+Playwright自动化测试-56- 多文件上传 - 下篇

1.简介 前边的两篇文章中,宏哥分别对input控件上传文件和非input控件上传文件进行了从理论到实践地讲解和介绍,但是后来又有人提出疑问,前边讲解和介绍的都是上传一个文件,如果上传多个文件,Playwright是如何实现的呢?宏哥看了一下官方的API也有上传多个文件的API,那么今…

7.28-8.11 中山集训

第一次外地集训欸主要放模拟赛,还有一些简短的记录。 7.29頑張って

GPU的体系结构几个演进

GPU的体系结构几个演进 从体系结构的视角看, GPU的诞生一开始是为了解决访存的问题, 1994年的时候整个图形渲染流水线基本上已经固定成为开放的OpenGL标准。伴随着GeForce 6的发布,Vertex Shader和Pixel Shader都支持了完整分支、循环、预测等功能实现,最终一个完全支持高级渲…

LInux 组合命令小结

cut -d: -f1 /etc/passwd #显示特定行ifconfig lo | grep "inet" | grep -v "inet6" |awk {print $2} #显示回环地址lo地址

一文汇总全球热门新闻API

新闻API通过提供快速、准确和全面的新闻内容,已经成为现代社会不可或缺的一部分,对人们的生活、工作环境和科技发展产生了深远的影响。新闻API使人们能够快速获取来自世界各地的实时新闻和信息,提高了信息的可访问性。通过新闻API,用户可以根据自己的兴趣和偏好接收定制化的…

读零信任网络:在不可信网络中构建安全系统03威胁模型

读零信任网络:在不可信网络中构建安全系统03威胁模型1. 信任管理 1.1. 信任源自于人,并通过计算机可以执行的信任机制流入其他系统 1.1.1. 只有使用系统的人确信它确实按照其意愿忠实地运行,该系统才能被认为是可信的 1.2. 信任管理是零信任…