事务报错没有显示回滚导致DDL阻塞引发的问题

在业务开发过程中,显示的开启事务并且在事务处理过程中对不同的情况进行显示的COMMIT或ROLLBACK,这是一个完整数据库事务处理的闭环过程。

在这里插入图片描述

这种在应用开发逻辑层面去handle的事务执行的结果,既确保了事务操作的数据完整性,又遵循了业务处理逻辑。所以显示的提交或回滚事务也是开发规范中的要求,但是也有一些存量的业务系统或开发人员并不能严格按照这一规范执行,进而在实际生产过程中引发故障。这里介绍一个因为开启事务后未显示的回滚导致DDL阻塞进而引发的问题。

应用系统使用的是MySQL生态的数据库,业务使用的是分区表,业务在处理时候因为当日的分区没有创建导致插入报错,应用逻辑上每日又有对表新增分区的操作,结果是事务没有显示回滚导致新增表分区的DDL阻塞,进而又引发后续的问题。

1、MySQL数据库故障模拟
1.1 创建分区表并插入数据

登录mysql数据库并创建分区表

CREATE TABLE tt1 (  id int NOT NULL, sdate date NOT NULL,  c1 varchar(4) NOT NULL,  PRIMARY KEY (id, sdate)  
)  
PARTITION BY RANGE columns(sdate) (  PARTITION p20240524 VALUES LESS THAN ('2024-05-25'),  PARTITION p20240525 VALUES LESS THAN ('2024-05-26')
);
1.2 显示的开启事务并插入数据
mysql> begin;
mysql> select * from tango.tt1;
+----+------------+-----+
| id | sdate      | c1  |
+----+------------+-----+
|  1 | 2024-05-25 | aaa |
+----+------------+-----+
1 row in set (0.00 sec)insert into tt1 values(1,'2024-05-25','aaa');
mysql> insert into tt1 values(3,'2024-05-27','ccc');
ERROR 1526 (HY000): Table has no partition for value from column_list

数据库执行报错提示插入的记录分区不存在。

1.3 查看数据库表中锁和事务的状态
mysql> select * from performance_schema.metadata_locks where object_name='tt1';
+-------------+---------------+-------------+-------------+-----------------------+--------------+---------------+-------------+-------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE    | LOCK_DURATION | LOCK_STATUS | SOURCE            | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+---------------+-------------+-------------+-----------------------+--------------+---------------+-------------+-------------------+-----------------+----------------+
| TABLE       | tango         | tt1         | NULL        |       140712994313232 | SHARED_READ  | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |              85 |             24 |
| TABLE       | tango         | tt1         | NULL        |       140712994947616 | SHARED_WRITE | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |              85 |             25 |
+-------------+---------------+-------------+-------------+-----------------------+--------------+---------------+-------------+-------------------+-----------------+----------------+
2 rows in set (0.00 sec)

可以看到表持有SHARED_READ和SHARED_WRITE锁,并不因为事务执行失败而释放,这也是mysql系数据库内核机制,事务报错后数据库层面并没有执行rollback操作,而是由应用自己决定是rollback还是commit。

1.4 其它业务执行新增分区的DDL操作
mysql> ALTER table tt1 ADD PARTITION ( PARTITION p20240526 VALUES LESS THAN ('2024-05-27') );

此时这个DDL操作会hang住,查看表的元数据锁情况

mysql> select * from performance_schema.metadata_locks where object_name='tt1';
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE         | LOCK_DURATION | LOCK_STATUS | SOURCE            | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
| TABLE       | tango         | tt1         | NULL        |       140712801139968 | SHARED_READ       | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             123 |             21 |
| TABLE       | tango         | tt1         | NULL        |       140712793308528 | SHARED_WRITE      | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             123 |             22 |
| TABLE       | tango         | tt1         | NULL        |       140712926580592 | SHARED_UPGRADABLE | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             121 |             20 |
| TABLE       | tango         | tt1         | NULL        |       140712928177104 | EXCLUSIVE         | TRANSACTION   | PENDING     | mdl.cc:3753       |             121 |             20 |
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
5 rows in set (0.00 sec)

可以看到一个pending状态的锁状态,查看对应的SQL语句,知道是新增分区的DDL操作。

mysql> select THREAD_ID,EVENT_ID,EVENT_NAME,TIMER_START,TIMER_END,TIMER_WAIT,LOCK_TIME,SQL_TEXT,STATEMENT_ID from events_statements_current where thread_id=121;
+-----------+----------+---------------------------+------------------+------------------+----------------+-----------+---------------------------------------------------------------------------------------+--------------+
| THREAD_ID | EVENT_ID | EVENT_NAME                | TIMER_START      | TIMER_END        | TIMER_WAIT     | LOCK_TIME | SQL_TEXT                                                                              | STATEMENT_ID |
+-----------+----------+---------------------------+------------------+------------------+----------------+-----------+---------------------------------------------------------------------------------------+--------------+
|       121 |       20 | statement/sql/alter_table | 2670208499587000 | 2687425357664000 | 17216858077000 | 246000000 | ALTER table tt1 ADD PARTITION ( PARTITION p20240526 VALUES LESS THAN ('2024-05-27') ) |        32613 |
+-----------+----------+---------------------------+------------------+------------------+----------------+-----------+---------------------------------------------------------------------------------------+--------------+
1 row in set (0.00 sec)

这里的DDL操作,在mysql数据库中通过参数lock_wait_timeout控制DDL等待超时时间,超过该时间DDL会报错。默认该参数配置为31536000s,实际生产业务系统会设置30~60s,一些核心业务系统会设置为5s。但是在DDL阻塞期间,也会影响新的业务的执行。

1.5 影响新的业务操作
mysql> select * from tango.tt1;

该操作也会hang住,查看对应的锁情况,也是处于pending状态。也就是阻塞的DDL操作会影响接下去的业务对该表的访问,直到DDL超时失败后,后续的业务才会正常。

mysql> select * from performance_schema.metadata_locks where object_name='tt1';
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE         | LOCK_DURATION | LOCK_STATUS | SOURCE            | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
| TABLE       | tango         | tt1         | NULL        |       140712801139968 | SHARED_READ       | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             123 |             21 |
| TABLE       | tango         | tt1         | NULL        |       140712793308528 | SHARED_WRITE      | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             123 |             22 |
| TABLE       | tango         | tt1         | NULL        |       140712926580592 | SHARED_UPGRADABLE | TRANSACTION   | GRANTED     | sql_parse.cc:5768 |             121 |             20 |
| TABLE       | tango         | tt1         | NULL        |       140712928177104 | EXCLUSIVE         | TRANSACTION   | PENDING     | mdl.cc:3753       |             121 |             20 |
| TABLE       | tango         | tt1         | NULL        |       140713468045808 | SHARED_READ       | TRANSACTION   | PENDING     | sql_parse.cc:5768 |             120 |              6 |
+-------------+---------------+-------------+-------------+-----------------------+-------------------+---------------+-------------+-------------------+-----------------+----------------+
5 rows in set (0.00 sec)
1.6 在这个场景下存在的问题
  • 事务处理报错时,业务层没有handle这个报错,并显示的去做commit或rollback;
  • 表分区的预创建和监控:对于分区表是要有预先创建分区的机制,每天或每月定时窗口创建一批分区,同时分区不足时能够及时告警出来;
  • 数据库层元数据锁等待超时:有些不重要的业务系统将lock_wait_timeout设置为600s设置更大,在该故障场景下是存在问题的,相当于DDL阻塞的这期间新的业务也会受到影响。所以将该参数设置到合理区间,比如5~60s是有必要的。

对于MySQL生态的数据库,事务内执行失败后数据库没有锁资源没有释放本身机制上没有问题,像国产数据库中TiDB、GoldenDB都有类似的现象。对于其它数据库,比如Oracle、PostgreSQL等,针对这个场景是什么样的表现,接下去以openGauss数据库为例进行验证。

2、openGauss数据库下故障场景模拟
2.1 登录openGauss单机版数据库,并创建分区表
gsql -d postgres -p 5432
[opgauss@tango-01 data]$ gsql -d postgres -p 5432
gsql ((openGauss-lite 5.0.2 build 48a25b11) compiled at 2024-05-14 10:41:04 commit 0 last mr  release)
openGauss=# create database tango;tango=# CREATE TABLE tt1 (  
tango(#     id int NOT NULL, 
tango(#     sdate date NOT NULL,  
tango(#     c1 varchar(4) NOT NULL
tango(# )  
tango-# PARTITION BY RANGE(sdate) (  
tango(#     PARTITION p20240524 VALUES LESS THAN ('2024-05-25'),  
tango(#     PARTITION p20240525 VALUES LESS THAN ('2024-05-26') 
tango(# );
CREATE TABLEtango=# \dt
Schema | Name | Type  |  Owner  |             Storage              
--------+------+-------+---------+----------------------------------public | tt1  | table | opgauss | {orientation=row,compression=no}
2.2 开启事务并插入数据
tango=# begin;
BEGIN
tango=# select * from tt1;id |        sdate        | c1  
----+---------------------+-----1 | 2024-05-25 00:00:00 | aaa
(1 row)tango=# insert into tt1 values(3,'2024-05-28','ccc'); 
ERROR:  inserted partition key does not map to any table partition

提示报错分区不存在

2.3 另外开启一个任务执行新增分区操作
tango=# ALTER table tt1 ADD PARTITION p20240526 VALUES LESS THAN ('2024-05-27');
ALTER TABLE

可以看到分区是新增成功的。

2.4 查看这种场景下表的锁和事务状态信息
tango=# SELECT l.locktype,l.database,l.relation::regclass,l.page,l.tuple,l.virtualxid,l.transactionid,l.classid,l.objid,l.objsubid,l.pid,l.mode,l.granted FROM pg_locks l JOIN pg_class c ON l.relation = c.oid WHERE c.relname = 'tt1';locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid |       pid       |      mode       | granted 
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+-----------------+-----------------+---------relation |    16384 | tt1      |      |       |            |               |         |       |          | 140405684233984 | AccessShareLock | t
(1 row)tango=# SELECT datname,pid,sessionid,usename,application_name,backend_start,xact_start,query_start,state,query FROM pg_stat_activity where datname='tango';datname |       pid       | sessionid | usename | application_name |         backend_start         |          xact_start           |          query_start          |      state        |                                                                        query                                                                        
---------+-----------------+-----------+---------+------------------+-------------------------------+-------------------------------+-------------------------------+------
---------------+-----------------------------------------------------------------------------------------------------------------------------------------------------tango   | 140405684233984 |         8 | opgauss | gsql             | 2024-05-26 15:45:47.008274+08 | 2024-05-26 15:47:40.481015+08 | 2024-05-26 15:47:45.822262+08 | idle 
in transaction | select * from tt1;

当执行失败后,事务处于idle in transaction (aborted)状态,表锁持有的锁也不存在了。

tango=# SELECT l.locktype,l.database,l.relation::regclass,l.page,l.tuple,l.virtualxid,l.transactionid,l.classid,l.objid,l.objsubid,l.pid,l.mode,l.granted FROM pg_locks l JOIN pg_class c ON l.relation = c.oid WHERE c.relname = 'tt1';locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | pid | mode | granted 
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+-----+------+---------
(0 rows)tango=# SELECT datname,pid,sessionid,usename,application_name,backend_start,xact_start,query_start,state,query FROM pg_stat_activity where datname='tango';datname |       pid       | sessionid | usename | application_name |         backend_start         |          xact_start           |          query_start          |      state             |                                                                        query                                                                    ---------+-----------------+-----------+---------+------------------+-------------------------------+-------------------------------+-------------------------------+------
-------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------
----tango   | 140405684233984 |         8 | opgauss | gsql             | 2024-05-26 15:45:47.008274+08 |                               | 2024-05-26 15:49:09.048895+08 | idle 
in transaction (aborted) | insert into tt1 values(3,'2024-05-28','ccc');

可以看到openGauss数据库和MySQL数据库在这种故障场景下的不同表现,对于openGauss数据库而言,当事务内处理失败后,事务已经被数据库rollback了,事务中所持有的表锁也相应的释放了,其它如Oracle、PostgreSQL数据库是有相同的表现。

其它数据库因为时间关系暂时不验证了,总结针对这个场景需要优化的点有:①业务开发时候对事务报错主动处理,并显示的执行commit或rollback操作;②数据库层设置合理的DDL超时时间;③对分区表进行预创建和有效的监控手段;④数据库的DDL操作和业务处理主流程松耦合,尽量在投产窗口执行。


参考资料:

  1. https://docs-opengauss.osinfra.cn/zh/docs/5.0.0-lite

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

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

相关文章

探索未来设计新境界,PSAI插件 艺术创作神器来袭!

想象一下,如果有一个工具,能够让你的设计工作变得既简单又高效,那会是怎样的体验?现在,梦想成真了! 这是一款革命性的PSAI设计插件,专为创意人士打造。它将彻底改变你的设计流程,让你…

【OpenCV】像素信息统计

介绍了计算像素均值、方差的API,以及统计像素信息的方法。相关API: minMaxLoc()mean()meanStdDev() 代码: #include "iostream" #include "opencv2/opencv.hpp"using namespace std; using namespace cv;int main(int…

【全开源】JAVA同城搬家系统源码小程序APP源码

JAVA同城搬家系统源码 特色功能: 强大的数据处理能力:JAVA提供了丰富的数据结构和算法,以及强大的并发处理能力,使得系统能够快速地处理大量的货物信息、司机信息、订单信息等,满足大规模物流的需求。智能路径规划&a…

香橙派 AIPro开发板上手测评

前言 最近拿到了一个新玩具:香橙派 AIPro。一个只比银行卡大一点点的开发板能带给我们多少惊喜呢?接下来就跟我一起来体验下这块开发板的魅力。 一、硬件配置 CPU:配备了4核64位ARM处理器,其中默认预留1个给AI处理器使用 NPU&am…

SpringBoot和Apache Doris实现实时广告推荐系统

本专题旨在向读者深度解读Apache Doris技术,探讨其与SpringBoot框架结合在各类实际应用场景中的角色与作用。本专题包括十篇文章,每篇文章都概述了一个特定应用领域,如大数据分析、实时报告系统、电商数据分析等,并通过对需求的解析、解决方案的设计、实际应用示例的展示以…

【Python实战】你还在冲会员看电影电视剧吗?Python带你实现各大资源免费看!

前言 halo,包子们下午好 今天给大家实现一个视频播放器,可以看任何电影,电视剧,不要再为以后看电视看电影而烦恼,今天是福利文章,相信我绝对有用! 开发工具 Python版本:3.7.8 相…

驱动未来:IT行业的现状与发展趋势

前言 随着技术的不断进步,IT行业已成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链,这些技术正在重塑我们的生活和工作方式。本文将探讨IT行业的现状和未来发展趋势,并邀请行业领袖、技术专家和…

Follow Your Pose: Pose-Guided Text-to-Video Generation using Pose-Free Videos

清华深&港科&深先进&Tencent AAAI24https://github.com/mayuelala/FollowYourPose 问题引入 本文的任务是根据文本来生成高质量的角色视频,并且可以通过pose来控制任务的姿势;当前缺少video-pose caption数据集,所以提出一个两…

拿捏数据结构- 链式二叉树

链式二叉树的概念: 链式二叉树解决的是非完全二叉树解决不了的问题 什么意思呢,简单的说就是,链式二叉树 可以是下面三种二叉树 但是非链式二叉树只能是前两种 链式二叉树的存储 节点结构:首先定义一个结构体或类来表示二叉树的节…

单元测试的实现方式

单元测试的实现方式包括:人工静态检查、动态执行跟踪 人工静态检查 人工静态检查是一种单元测试实现方式,它主要依赖开发人员的人工代码审查和静态分析工具来识别潜在的代码问题。 代码审查:开发人员通过仔细检查代码来发现潜在的问题。他…

不怕YOLOv10高歌猛进,我有YOLOv8稳扎稳打

YOLOv10 出来有几天时间了,这次我没有选择第一时间出文章解析,如此频繁的发布数字版本的 YOLO 着实让人头疼,虽然数字的更新并非旧版技术的过时, 但是这肯定会让很多在校同学增加很多焦虑情绪。这里还是请大家辩证看待。 v10 这次…

区间选点问题-贪心-C++

问题: 给定 𝑁 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 𝑁,表示区间数…

CSS文本粒子动画特效之爱心粒子文字特效-Canvas

1. 效果图 2.完整代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><style>body,html {margin: 0;paddin…

order by工作过程和优化

工作过程 order by 是由优化器决定的&#xff0c;如果优化器认为filesort速度快&#xff0c;那么走filesort排序&#xff0c;如果优化器认为索引速度快&#xff0c;那么走索引排序。

有一个3x4的矩阵,求矩阵中所有元素中的最大值。要求用函数处理

解此题的算法已在之前的文章中介绍&#xff0c;详见&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/139181787 编写程序&#xff1a; 运行结果&#xff1a;

卷径计算(PID输出补偿法 SCL源代码)

卷径计算有很多方法,这里我们提供另一个思路,这里我们采用的是通过速度控制间接控制张力通过线速度和系统卷径我们可以计算出我们的速度前馈量(主速度)。具体收放卷前馈量计算可以参考下面文章链接: 收放卷前馈量计算FC(梯形图+SCL代码)-CSDN博客文章浏览阅读584次。这篇博…

【数据分析面试】55. 寻找双词组 (Python)

题目&#xff1a; 寻找双词组 &#xff08;Python&#xff09; 编写一个名为 find_bigrams 的函数&#xff0c;该函数接收一个句子或段落的字符串&#xff0c;并按顺序返回其所有双词组的列表。 注意&#xff1a; 双词组是指连续的两个单词。 示例&#xff1a; 输入&#x…

JavaScript(ES6)入门

ES6 1、介绍 ECMAScript 6&#xff08;简称ES6&#xff09;是于2015年6月正式发布的JavaScript 语言的标准&#xff0c;正式名为ECMAScript 2015&#xff08;ES2015&#xff09;。它的目标是使得JavaScript语言可以用来编写复杂的大型应用程序&#xff0c;成为企业级开发语言。…

Dolphinscheduler不重启加载Oracle驱动

转载自刘茫茫看山 问题背景 某天我们的租户反馈数据库连接缺少必要的驱动&#xff0c;我们通过日志查看确实是缺少部分数据库的驱动&#xff0c;因为DolphinScheduler默认只带了Oracle和MySQL的驱动&#xff0c;并且需要将pom文件中的test模式去掉才可以在打包的时候引入。我…

Unity Dotween 定位点的制作

目录 前言 一、动画预览 二、动画拆分 三、素材准备 四、曲线 OutCirc详解 五、速度分类详解 六、代码 七、组件和设置 八、作者的话 前言 我答应我的粉丝接下来更新Dotween系列&#xff0c;但是我一直没想好&#xff0c;从哪里开始讲。 Dotween的安装我就跳过了&…