事務報錯沒有顯示回滾導致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.pswp.cn/bicheng/17714.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17714.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17714.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

簡單句語法

簡單句是指包含一個主語和一個謂語的句子,它表達一個完整的思想。簡單句是構成更復雜句子的基礎。 簡單句的兩種基本結構 簡單句可以分為兩種基本結構: 主謂結構: 描述主語所做的動作或行為,也就是 “做什么”。 主系結構: 描述主語的狀態…

Python2和Python3對utf8的實現方式有什么區別?

# -*- coding: utf8 -*- 是一個特殊的文件頭部注釋,通常出現在Python 2的源代碼文件的開頭。這個注釋告訴Python解釋器,該源文件使用的是UTF-8編碼。這對于包含非ASCII字符(例如中文字符、特殊符號等)的Python源代碼文件來說非常重…

探索未來設計新境界,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 相…

Java Lambda 會影響性能嗎?

# 測試代碼LamdaTest.java import java.util.*;class LamdaTest {static volatile List<Integer> integers new ArrayList<Integer>();// 普通 for 循環測試public static int forLoopInteger() {int total 0;for (int i 0; i < integers.size(); i) {total…

驅動未來:IT行業的現狀與發展趨勢

前言 隨著技術的不斷進步&#xff0c;IT行業已成為推動全球經濟和社會發展的關鍵力量。從云計算、大數據、人工智能到物聯網、5G通信和區塊鏈&#xff0c;這些技術正在重塑我們的生活和工作方式。本文將探討IT行業的現狀和未來發展趨勢&#xff0c;并邀請行業領袖、技術專家和…

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

清華深&港科&深先進&Tencent AAAI24https://github.com/mayuelala/FollowYourPose 問題引入 本文的任務是根據文本來生成高質量的角色視頻&#xff0c;并且可以通過pose來控制任務的姿勢&#xff1b;當前缺少video-pose caption數據集&#xff0c;所以提出一個兩…

Java的上下轉型與多態

上下轉型 首先&#xff0c;定義一個父類Person // 父類 class Person {public void run(){System.out.println("person 中的 run");}public void eat(){System.out.println("Person 中的 eat");}}接著定義一個繼承自父類的子類Student: // 子類 class S…

拿捏數據結構- 鏈式二叉樹

鏈式二叉樹的概念&#xff1a; 鏈式二叉樹解決的是非完全二叉樹解決不了的問題 什么意思呢&#xff0c;簡單的說就是&#xff0c;鏈式二叉樹 可以是下面三種二叉樹 但是非鏈式二叉樹只能是前兩種 鏈式二叉樹的存儲 節點結構&#xff1a;首先定義一個結構體或類來表示二叉樹的節…

機器學習-7-機器學習中常用的可視化方式總結

參考通透!!監督學習和無監督學習全總結! 參考機器學習中的可視化 1 監督學習和無監督學習 監督學習和無監督學習,它們之間的主要區別在于訓練數據的標簽信息是否提供。 1.1 概述 一、監督學習(Supervised Learning): (1)標簽信息: 監督學習使用帶有標簽的訓練數據。這…

單元測試的實現方式

單元測試的實現方式包括&#xff1a;人工靜態檢查、動態執行跟蹤 人工靜態檢查 人工靜態檢查是一種單元測試實現方式&#xff0c;它主要依賴開發人員的人工代碼審查和靜態分析工具來識別潛在的代碼問題。 代碼審查&#xff1a;開發人員通過仔細檢查代碼來發現潛在的問題。他…

不怕YOLOv10高歌猛進,我有YOLOv8穩扎穩打

YOLOv10 出來有幾天時間了&#xff0c;這次我沒有選擇第一時間出文章解析&#xff0c;如此頻繁的發布數字版本的 YOLO 著實讓人頭疼&#xff0c;雖然數字的更新并非舊版技術的過時&#xff0c; 但是這肯定會讓很多在校同學增加很多焦慮情緒。這里還是請大家辯證看待。 v10 這次…

解密消息隊列的復制魔法:RocketMQ vs Kafka

解密消息隊列的復制魔法&#xff1a;RocketMQ vs Kafka 今天我們來聊聊一個在消息隊列世界中至關重要的主題&#xff1a;消息復制。消息復制不僅能防止消息丟失&#xff0c;還能確保系統的高可用性。即使某個節點宕機了&#xff0c;其他節點依然可以繼續工作。那么&#xff0c…

區間選點問題-貪心-C++

問題&#xff1a; 給定 &#x1d441; 個閉區間 [ai,bi]&#xff0c;請你在數軸上選擇盡量少的點&#xff0c;使得每個區間內至少包含一個選出的點。 輸出選擇的點的最小數量。 位于區間端點上的點也算作區間內。 輸入格式 第一行包含整數 &#x1d441;&#xff0c;表示區間數…

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…