操作环境:mysql5.6
CREATE TABLE `ydy_test` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
select * from information_schema.innodb_trx\G;
start transaction;
Insert into ydy_test values(1); --另一个会话中没有记录--
start transaction;--另一个会话中记录1--
Insert into ydy_test values(2);--另一个会话中记录1--
Commit;--另一个会话中记录1、2--
Insert into ydy_test values(3);--另一个会话中记录1、2、3--
Rollback;--另一个会话中记录1、2、3--
truncate ydy_test;
start transaction;
Insert into ydy_test values(1); --另一个会话中没有记录--
start transaction;--另一个会话中记录1--
Insert into ydy_test values(2);--另一个会话中记录1--
rollback;--另一个会话中记录1--
Insert into ydy_test values(3);--另一个会话中记录1、3--
commit;--另一个会话中记录1、3--
truncate ydy_test;
set autocommit = 0;
start transaction;
Insert into ydy_test values(1);--另一个会话中没有记录--
start transaction;--另一个会话中记录1--
Insert into ydy_test values(2);--另一个会话中记录1--
Commit;--另一个会话中记录1、2--
Insert into ydy_test values(3);--另一个会话中记录1、2--
Rollback;--另一个会话中记录1、2--
truncate ydy_test;
set autocommit = 0;
start transaction;
Insert into ydy_test values(1); --另一个会话中没有记录--
start transaction;--另一个会话中记录1--
Insert into ydy_test values(2);--另一个会话中记录1--
rollback;--另一个会话中记录1--
Insert into ydy_test values(3);--另一个会话中记录1--
commit;--另一个会话中记录1、3--
truncate ydy_test;
set autocommit = 0;
start transaction;
Insert into ydy_test values(1); --另一个会话中没有记录--
start transaction;--另一个会话中记录1--
Insert into ydy_test values(2);--另一个会话中记录1--
rollback;--另一个会话中记录1--
Insert into ydy_test values(3);--另一个会话中记录1--
rollback;--另一个会话中记录1--
详解:
1、start transaction;
2、Insert into ydy_test values(1); --另一个会话中没有记录--
3、start transaction;--另一个会话中记录1--
4、Insert into ydy_test values(2);--另一个会话中记录1--
5、Commit;--另一个会话中记录1、2--
6、Insert into ydy_test values(3);--另一个会话中记录1、2、3--
7、Rollback;--另一个会话中记录1、2、3--
在2执行完成后,事务建立;
在3执行后,自动把外层事务提交
在4执行后,重新建立新事务
在5执行后,新事务被提交
6已经执行在没有事务的基础上了。如果会话中@@autocommit=1,那么6就直接提交了,和7rollback没什么关系了。
结论:1、在一个会话中,mysql是无法启动两个事务的,更不会存在父事务和子事务(个人理解,希望大神指正);
2、start transaction并没有真正的开启事务,真正开启事务是在DML语句;
3、DDL语句会隐式提交事务,mysql8已有部分DDL支持原子性。
可以用savepoint和rollback to来实现[假]嵌套事务
thinkphp中就是采用savepoint和rollback to
/thinkphp/library/think/db/Connection.php
/**
* 启动事务
* @access public
* @return bool|mixed
* @throws \Exception
*/
public function startTrans()
{
$this->initConnect(true);
if (!$this->linkID) {
return false;
}
++$this->transTimes;
try {
if (1 == $this->transTimes) {
$this->linkID->beginTransaction();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepoint('trans' . $this->transTimes)
);
}
} catch (\Exception $e) {
if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans();
}
throw $e;
} catch (\Error $e) {
if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans();
}
throw $e;
}
}
/**
* 用于非自动提交状态下面的查询提交
* @access public
* @return void
* @throws PDOException
*/
public function commit()
{
$this->initConnect(true);
if (1 == $this->transTimes) {
$this->linkID->commit();
}
--$this->transTimes;
}
/**
* 事务回滚
* @access public
* @return void
* @throws PDOException
*/
public function rollback()
{
$this->initConnect(true);
if (1 == $this->transTimes) {
$this->linkID->rollBack();
} elseif ($this->transTimes > 1 && $this->supportSavepoint()) {
$this->linkID->exec(
$this->parseSavepointRollBack('trans' . $this->transTimes)
);
}
$this->transTimes = max(0, $this->transTimes - 1);
}
thinkphp5.0实验
Unit.php
public function test() {
// 启动事务
$this->model->startTrans();
try{
$res = $this->model->modifyById(61, ['cn_name'=>'aaa']);
$trans = $this->model->query("select * from information_schema.innodb_trx");
write_log(json_encode($trans));
$res = $this->userModel->test();
$res = $this->model->modifyById(62, ['cn_name'=>'bbb']);
$trans = $this->model->query("select * from information_schema.innodb_trx");
write_log(json_encode($trans));
// throw new Exception("1");
// 提交事务
$this->model->commit();
} catch (\Exception $e) {
// 回滚事务
$this->model->rollback();
}
}
User.php
public function test() {
// 启动事务
$this->startTrans();
try{
$res = $this->modifyById(362, ['name'=>'ccc']);
$trans = $this->query("select * from information_schema.innodb_trx");
write_log(json_encode($trans));
throw new Exception("ddd");
// if($res === false) {
// throw new \Exception("inner");
// }
// 提交事务
$this->commit();
} catch (\Exception $e) {
// 回滚事务
$this->rollback();
}
}
日志结果
2021-04-09 16:33:44[{"trx_id":"21D3DE","trx_state":"RUNNING","trx_started":"2021-04-09 16:33:44","trx_requested_lock_id":null,"trx_wait_started":null,"trx_weight":3,"trx_mysql_thread_id":87304,"trx_query":"select * from information_schema.innodb_trx","trx_operation_state":null,"trx_tables_in_use":0,"trx_tables_locked":0,"trx_lock_structs":2,"trx_lock_memory_bytes":376,"trx_rows_locked":1,"trx_rows_modified":1,"trx_concurrency_tickets":0,"trx_isolation_level":"REPEATABLE READ","trx_unique_checks":1,"trx_foreign_key_checks":1,"trx_last_foreign_key_error":null,"trx_adaptive_hash_latched":0,"trx_adaptive_hash_timeout":10000}]
2021-04-09 16:33:44[{"trx_id":"21D3DE","trx_state":"RUNNING","trx_started":"2021-04-09 16:33:44","trx_requested_lock_id":null,"trx_wait_started":null,"trx_weight":3,"trx_mysql_thread_id":87304,"trx_query":"select * from information_schema.innodb_trx","trx_operation_state":null,"trx_tables_in_use":0,"trx_tables_locked":0,"trx_lock_structs":2,"trx_lock_memory_bytes":376,"trx_rows_locked":1,"trx_rows_modified":1,"trx_concurrency_tickets":0,"trx_isolation_level":"REPEATABLE READ","trx_unique_checks":1,"trx_foreign_key_checks":1,"trx_last_foreign_key_error":null,"trx_adaptive_hash_latched":0,"trx_adaptive_hash_timeout":10000}]
2021-04-09 16:33:44[{"trx_id":"21D3DE","trx_state":"RUNNING","trx_started":"2021-04-09 16:33:44","trx_requested_lock_id":null,"trx_wait_started":null,"trx_weight":3,"trx_mysql_thread_id":87304,"trx_query":"select * from information_schema.innodb_trx","trx_operation_state":null,"trx_tables_in_use":0,"trx_tables_locked":0,"trx_lock_structs":2,"trx_lock_memory_bytes":376,"trx_rows_locked":1,"trx_rows_modified":1,"trx_concurrency_tickets":0,"trx_isolation_level":"REPEATABLE READ","trx_unique_checks":1,"trx_foreign_key_checks":1,"trx_last_foreign_key_error":null,"trx_adaptive_hash_latched":0,"trx_adaptive_hash_timeout":10000}]
结论,代码中嵌套start transaction,但是在一个进程中框架始终只有一个事务。