潘瑞峰的个人博客

数据库事务

潘瑞峰 mysql事务transaction

事务

数据库事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。它的特性有原子性、一致性、隔离性(read uncommitted, read committed, repeatable read and serializable)和持久性。这些特性在网上或者书本上有很详细的介绍,这里就不一一介绍了。

嵌套事务

本文主要想探索数据库中嵌套事务相关的问题,以mysql和InnoDB为载体。

实验

set autocommit=0

-- 场景1
start transaction;
    insert into test(id,name) values(1,'dog');
    start transaction;
        insert into test(id,name) values(2,'cat');
    commit;
rollback;
select * from test;
+----+------+
| id | name |
+----+------+
|  1 | dog  |
|  2 | cat  |
+----+------+


-- 场景2
delete from test;
start transaction;
    insert into test(id,name) values(1,'dog');
    start transaction;
        insert into test(id,name) values(2,'cat');
    rollback;
commit;
select * from test;
+----+------+
| id | name |
+----+------+
|  1 | dog  |
+----+------+

场景2似乎是正确的,外层的事务被提交了,而内层的事务被回滚了。但是在场景1中,并没有回滚外层事务。

结论

在mysql的“Historical Note”:

transactions could be arbitrarily nested, but when the parent transaction was committed or aborted, all its child (nested) transactions were committed or aborted as well. Commit of a nested transaction, in turn, made its changes visible, but not durable: it destroyed the nested transaction, so all the nested transaction's changes would become visible to the parent and to other currently active nested transactions of the same parent.

mysql是支持嵌套事务的,而且嵌套事务是会根据parent transaction的状态迁移的。

那为什么在实验中并没有出现上文描述的情况呢?那是因为“Current Situation”:

the DDL statement always commits the current transaction (if any) before proceeding

所以当遇到DDL的时候,会自动提交。网上有人总结了一些会自动提交的语句,如下:

ALTER FUNCTION    
ALTER PROCEDURE    
ALTER TABLE    
BEGIN    
CREATE DATABASE    
CREATE FUNCTION    
CREATE INDEX    
CREATE PROCEDURE    
CREATE TABLE    
DROP DATABASE    
DROP FUNCTION    
DROP INDEX    
DROP PROCEDURE    
DROP TABLE    
UNLOCK TABLES    
LOAD MASTER DATA    
LOCK TABLES    
RENAME TABLE    
TRUNCATE TABLE    
SET AUTOCOMMIT=1    
START TRANSACTION   

Savepoint

尽量不要用嵌套事务,一般来说如果需要实现局部回滚,会使用savepoint。

-- 场景1 回滚第一条语句
delete from test;
start transaction;
    savepoint savepoint1;
    insert into test(id,name) values(1,'dog');
    rollback to savepoint savepoint1;
    insert into test(id,name) values(2,'cat');
commit;
select * from test;
+----+------+
| id | name |
+----+------+
|  2 | cat  |
+----+------+


-- 场景2 回滚第二条语句
delete from test;
start transaction;
    insert into test(id,name) values(1,'dog');
    savepoint savepoint1;
    insert into test(id,name) values(2,'cat');
    rollback to savepoint savepoint1;
commit;
select * from test;
+----+------+
| id | name |
+----+------+
|  1 | dog  |
+----+------+

可以看出,通过savepoint,是可以实现局部回滚的。

潘瑞峰
五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。