在业务开发过程中,显示的开启事务并且在事务处理过程中对不同的情况进行显示的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系数据库内核机制