事务报错没有显示回滚导致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.rhkb.cn/news/338831.html

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

相关文章

Jenkins流水线pipeline--基于上一章的工作流程

1流水线部署 1.流水线文本名Jenkinsfile,将流水线放入gitlab远程仓库代码里面 2构建参数 2pipeline脚本 Jenkinsfile文件内容 pipeline {agent anyenvironment {key"value"}stages {stage("拉取git仓库代码") {steps {deleteDir()checkout scmGit(branc…

kafka-生产者发送消息消费者消费消息

文章目录 1、生产者发送消息&消费者消费消息1.1、获取 kafka-console-producer.sh 的帮助信息1.2、生产者发送消息到某个主题1.3、消费主题数据 1、生产者发送消息&消费者消费消息 1.1、获取 kafka-console-producer.sh 的帮助信息 [rootlocalhost ~]# kafka-console…

CISCN 2022 初赛 ez_usb

还是从第一个 URB向后看 发现 同时 存在 2.8.1 2.10.1 2.4.1 但是显然 2.4.1 是7个字节 不满足 usb流量要求 只考虑 2.8.1 和 2.10.1 tshark -r ez_usb.pcapng -T json -Y "usb.src \"2.8.1\"" -e usbhid.data > 281.json 正常取数据即可 import js…

Vue3 - Mac系统用文本编辑写html不显示效果的坑

平时在win系统下,可以直接对文本进行编辑,非常的舒服。 在mac系统中,也有类似的功能,就是文本编辑,没想到居然还有坑。 这是我mac系统中创建的html文件,想着没有几行代码,就没有开编辑器了&am…

crossover软件是干什么的 crossover软件安装使用教程 crossover软件如何使用

CrossOver 以其出色的跨平台兼容性,让用户在Mac设备上轻松运行各种Windows软件,无需复杂的设置或额外的配置,支持多种语言,满足不同国家和地区用户的需求。 CrossOver 软件是干嘛的 使用CrossOver 不必购买Windows 授权&#xf…

Java Spring Boot 从必应爬取图片

获取图片主要就是通过必应图片页面控制台的元素,确认图片和标题在哪个类中(浏览器 F12) 引入依赖 这里需要引入两个依赖 jsoup 和 hutool maven依赖网站地址:Maven Repository: Search/Browse/Explore (mvnrepository.com) 挑选…

基于java18多端展示+ idea hbuilder+ mysql家政预约上门服务系统,源码交付,支持二次开发

基于java18多端展示 idea hbuilder mysql家政预约上门服务系统,源码交付,支持二次开发 家政预约上门系统是一种通过互联网或移动应用平台,为用户提供在线预约、下单、支付和评价家政服务的系统。该系统整合了家政服务资源,使用户能…

c++学生管理系统

想要实现的功能 1,可以增加学生的信息,包括(姓名,学号,c成绩,高数成绩,英语成绩) 2,可以删除学生信息 3,修改学生信息 4,显示所有学生信息 5&#xff0c…

图形学初识--多边形剪裁算法

文章目录 前言正文为什么需要多边形剪裁算法?前置知识二维直线直线方程:距离本质:点和直线距离关系: 三维平面平面方程距离本质:点和直线距离关系: Suntherland hodgman算法基本介绍基本思想二维举例问题描…

uni-app解决表格uni-table样式问题

一、如何让表格文字只显示一行,超出部分用省略号表示 步骤 : 给table设置table-layout:fixed; 列宽由表格宽度和列宽度设定。(默认是由单元格内容设定)让表格元素继承父元素宽度固定table-layout: inherit;overflow: hidden;超过…

Docker安装启动Mysql

1、安装Docker(省略) 网上教程很多 2、下载Mysql5.7版本 docker pull mysql:5.7 3、查看镜像是够下载成功 docker images 4、启动镜像,生成容器 docker run --name mysql5.7 -p 13306:3306 -e MYSQL_ROOT_PASSWORD123456 -d mysql:5.7 5…

我成功创建了一个Electron应用程序

1.创建electron项目命令: yarn create quick-start/electron electron-memo 2选择:√ Select a framework: vue √ Add TypeScript? ... No √ Add Electron updater plugin? ... Yes √ Enable Electron download mirror proxy? ... Yes 3.命令&a…

services层和controller层

services层 我的理解,services层是编写逻辑代码语句最多的一个层,非常重要,在实际的项目中,负责调用Dao层中的mybatis,在我的项目中它调用的是这两个文件 举例代码如下 package com.example.sfdeliverysystem.servic…

基于单片机的 wifi 家电开关控制系统设计

摘要 : 本文是利用 51 单片机基础知识结合 wifi 通信技术完成的一套可通过手机无线遥控家电开关系统设计。整个系统以 STC89C51 单片机为核心,采用业界主流的 ESP8266wifi 模块作为通信模块,家电开关的自动控制部分采用 3 路继电器开关来实现。本系统的…

【python】多线程(3)queue队列之不同延时时长的参数调用问题

链接1:【python】多线程(笔记)(1) 链接2:【python】多线程(笔记)(2)Queue队列 0.问题描述 两个线程,但是不同延时时长,导致数据输出…

MyBatis框架-开发方式+参数传递+#{}、${}+返回值处理+查询结果封装为对象+resultType

一、开发方式 MyBatis-Dao层Mapper接口化开发 二、注意事项 1、Mapper接口与Mapper.xml映射文件要满足4个对应 (1)Mapper接口的全类名必须与Mapper映射文件中的namespace相同 (2)Mapper接口中的每一个方法名在Mapper映射文件…

961题库 北航计算机 操作系统 附答案 选择题形式

有题目和答案,没有解析,不懂的题问大模型即可,无偿分享。 第1组 习题 计算机系统的组成包括( ) A、程序和数据 B、处理器和内存 C、计算机硬件和计算机软件 D、处理器、存储器和外围设备 财务软件是一种&#xff…

iOS——类与对象底层探索

类和对象的本质 当我们使用OC创建一个testClass类并在main函数创建它的实例对象的时候,OC的底层到底是什么样的呢? 首先,我们要了解OC对象的底层结构,那么我们就得知道:OC本质底层实现转化其实都是C/C代码。 使用下面…

11Linux学习笔记

Linux 实操篇 目录 文章目录 Linux 实操篇1.rtm包(软件)1.1 基本命令1.2 基本格式1.3安装rtm包1.4卸载rtm包 2.apt包2.1 基本命令结构2.2 常用选项2.3常用命令 1.rtm包(软件) 1.1 基本命令 1.2 基本格式 1.3安装rtm包 1.4卸载r…

④单细胞学习-cellchat细胞间通讯

目录 1,原理基础 流程 受体配体概念 方法比较 计算原理 2,数据 3,代码运行 1,原理基础 原文学习Inference and analysis of cell-cell communication using CellChat - PMC (nih.gov) GitHub - sqjin/CellChat: R toolk…