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

news2024/9/25 20:40:29

在业务开发过程中,显示的开启事务并且在事务处理过程中对不同的情况进行显示的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 TABLE

tango=# \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.coloradmin.cn/o/1700911.html

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

相关文章

ROS2入门21讲__第21讲__ROS2应用与进阶攻略

资源汇总 常用框架 自主导航 比如移动机器人基本都会具备的自主导航功能,ROS2提供了完整的自主导航系统框架和各种实现好的算法,即便我们不开发任何代码,也可以在自己的机器人上,使用这套系统,快速实现自主导航的基本…

X-CSV-Reader:一个使用Rust实现CSV命令行读取器

&#x1f388;效果演示 ⚡️快速上手 依赖导入&#xff1a; cargo add csv读取实现&#xff1a; use std::error::Error; use std::fs::File; use std::path::Path;fn read_csv<P: AsRef<Path>>(filename: P) -> Result<(), Box<dyn Error>> {le…

40岁的戴尔在AI时代翻红了

戴尔公司股价创历史新高&#xff0c;市值达1138亿美元&#xff0c;涨幅110%。戴尔向AI押注多年&#xff0c;收购企业转型&#xff0c;成为数据基础设施厂商。AI服务器销售增长&#xff0c;分析师看好戴尔未来发展。 5月24日美股收盘&#xff0c;很多人可能不太关注的戴尔公司股…

华为OD机试【计算最接近的数】(java)(100分)

1、题目描述 给定一个数组X和正整数K&#xff0c;请找出使表达式X[i] - X[i1] … - X[i K 1]&#xff0c;结果最接近于数组中位数的下标i&#xff0c;如果有多个i满足条件&#xff0c;请返回最大的i。 其中&#xff0c;数组中位数&#xff1a;长度为N的数组&#xff0c;按照元…

MP3文件本地存储与下载指南

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言 二、建立存储文件夹 三、获取MP3文件URL并下载 四、优化下载过程 五、总结与…

韩愈,文起八代之衰的儒学巨匠

&#x1f4a1; 如果想阅读最新的文章&#xff0c;或者有技术问题需要交流和沟通&#xff0c;可搜索并关注微信公众号“希望睿智”。 韩愈&#xff0c;字退之&#xff0c;生于唐代宗大历三年&#xff08;公元768年&#xff09;&#xff0c;卒于唐穆宗长庆四年&#xff08;公元82…

Activiti7_使用

Activiti7_使用 一、Activiti7二、绘制工作流三、通过代码部署流程&#xff0c;再对流程进行实例化&#xff0c;完整运行一遍流程即可四、在springbooot中使用 一、Activiti7 为了实现后端的咨询流转功能&#xff0c;学习Activiti7&#xff0c;记录下使用的过程及遇到的问题 二…

力扣 第 399 场周赛 解题报告 | 珂学家 | 调和级数 + 分块DP

前言 T1. 优质数对的总数 I 题型: 签到 class Solution:def numberOfPairs(self, nums1: List[int], nums2: List[int], k: int) -> int:res 0for v1 in nums1:for v2 in nums2:if v1 % (v2 * k) 0:res 1return resT2. 压缩字符串 III 思路: 模拟 感觉引入一个栈&…

通用代码生成器应用场景三,遗留项目反向工程

通用代码生成器应用场景三&#xff0c;遗留项目反向工程 如果您有一个遗留项目&#xff0c;要重新开发&#xff0c;或者源代码遗失&#xff0c;或者需要重新开发&#xff0c;但是希望复用原来的数据&#xff0c;并加快开发。 如果您的项目是通用代码生成器生成的&#xff0c;…

【量算分析工具-概述】GeoServer改造Springboot番外系列三

背景概述 GIS公司做软件产品&#xff0c;往往绕不开的是量算分析工具的开发和使用。例如做的比较好的火星科技的mars3d产品&#xff0c;如下图&#xff0c;但是往往这些工具都是利用Cesium框架进行前端计算的实现的&#xff0c;网上关于这些量算工具算法原理的文章少之又少&…

石英晶体谐振器的频率与电阻温度特性及其影响因素

石英晶体谐振器是一种常用的电子元件&#xff0c;其具有精确的谐振频率&#xff0c;广泛应用于各种电子设备中&#xff0c;如时钟、频率发生器、滤波器等。石英晶体谐振器的频率和电阻温度特性是评价其性能的重要参数。 1. 频率温度特性&#xff1a; 石英晶体谐振器的频率随温…

身为UI设计老鸟,不学点3D,好像要被潮流抛弃啦,卷起来吧。

当前3D原则在UI设计中运用的越来越多&#xff0c;在UI设计中&#xff0c;使用3D元素可以为界面带来以下几个价值&#xff1a; 增强视觉冲击力&#xff1a;3D元素可以通过立体感和逼真的效果&#xff0c;为界面增添视觉冲击力&#xff0c;使得设计更加生动、吸引人&#xff0c;并…

mac电脑用n切换node版本

一、安装 node版本管理工具 “n” sudo npm install -g n二、检查安装成功&#xff1a; n --version三、查看依赖包的所有版本号 比如: npm view webpack versions --json npm view 依赖包名 versions --json四、安装你需要的版本的node sudo n <node版本号> // 例如…

<iframe>标签的使用

前言&#xff1a; 最近做项目需要使用到腾讯位置服务&#xff08;这个之后分享&#xff09;&#xff0c;其中用到了一个之前一直没用到的标签&#xff1a;&#xff1c;iframe&#xff1e;&#xff0c;一时居然不知道这个是干什么用的。今天分享一下。 下面这段代码是我用来测试…

开发者为什么需要“不良代码”

“从未犯过错误的人也从未有过新发现。” — 塞缪尔斯迈尔斯 想象一下场景&#xff1a;苏格兰&#xff0c;1928年。可能在下雨&#xff0c;一位科学家不小心让他的培养皿被霉菌污染了&#xff0c;他并不知道这个错误最终将拯救数百万人的生命&#xff0c;这项伟大的发现就是青…

了解Hive 工作原理:Hive 是如何工作的?

一、概念 1、Hive Apache Hive 是一个分布式的容错数据仓库系统&#xff0c;可实现大规模分析和便于使用 SQL 读取、写入和管理驻留在分布式存储中的PB级数据。 Hive是建立在Hadoop之上的数据仓库框架&#xff0c;它提供了一种类SQL的查询语言—HiveQL&#xff0c;使得熟悉S…

kettle 读取记事本文件给java组件处理

kettle9.4 用到两个组件 文本文件输入 文件内容如下 文本文件输入---文件 文本文件输入---内容 注意事项&#xff1a;分隔符这里&#xff0c;我一直没注意&#xff0c;导致不管怎么读数据都读不到&#xff1b;可以用换行符&#xff0c;可以用其他的&#xff0c;视情况而定&…

[idea/git] 如何把多模块项目提交到一个仓库

一、问题 我使用idea创建项目&#xff0c;依次创建module进行开发&#xff0c;开发完毕之后&#xff0c;在github上创建仓库&#xff0c;配置后发现&#xff0c;在idea里点击提交时&#xff0c;每个模块各自记录commit&#xff0c;并且每个模块都需要配置origin地址。 二、解…

【数据结构】二叉树和堆

文章目录 一、 什么是二叉树二、 二叉树的存储结构顺序存储视图 三、 堆堆的结构及概念大堆和小堆 四、 建堆五、 堆排序六、 topk问题 一、 什么是二叉树 二叉树&#xff0c;作为一种重要的数据结构&#xff0c;由节点组成&#xff0c;每个节点可以有两个子节点&#xff0c;通…

【UE5.1 角色练习】07-AOE技能

目录 效果 步骤 一、准备技能动画 二、准备粒子特效 三、技能蓝图 四、相机震动 前言 在上一篇&#xff08;【UE5.1 角色练习】06-角色发射火球-part2&#xff09;基础上继续实现角色释放AOE技能的功能。 效果 步骤 一、准备技能动画 1. 在项目设置中添加一个操作映…