数据库-事务及其隔离问题


多条语句同时成功或同时不成功,有一条失败会回滚,所有事务操作取消

  • 用 BEGIN, ROLLBACK, COMMIT 来实现
    • BEGIN 或 START TRANSACTION; 开始一个事务
    • ROLLBACK 事务回滚
    • COMMIT 事务确认
  • 直接用 SET 来改变 MySQL 的自动提交模式:
    • SET AUTOCOMMIT=0 禁止自动提交
    • SET AUTOCOMMIT=1 开启自动提交
START TRANSACTION; // 开启事务
INSERT INTO stu (class_id,sname,sex)VALUES(2,'xx','x');
COMMIT;

四大特性

  • 原子性:不能部分执行,事务不能完成也要回滚消除影响
    • 侧重事务本身的职责,不管具体内容,只管能不能完成事务。
  • 一致性:事务操作就是把数据库从一个状态到另一个状态。
    • 侧重事务完成的结果
    • 比如 A 和 B 都有 1000,总共 2000。A 向 B 转账 500,那么必须保持事务发生后 A B 都为 1000
      • A 减少 500 和 B 增加 500
      • 这两个操作必须在事务内全部得到实现
  • 隔离性:多事务并发运行,各自执行各自的,互不影响
  • 持久性:数据库出错也能恢复,或者事务提交后不会再发生意外改变

隔离问题

  • 脏读:A 读取了 B 事务过程中(未提交)的数据,但 B 后面进行了回滚。A 读取到的即为脏数据
  • 不可重复读:相对于update
    • A多次读取同一数据
    • B的事务在A事务 <u> 多次读取的过程中 </u> 进行了更新 <u> 并提交 </u>
    • A多次读取的结果不一致
  • 幻读:相对于 insertdelete操作
    • A将数据库中所有成绩从 具体分数 改为 ABCD 等级
    • B这个时候插入了 具体分数 记录
    • A改结束,发现有一条记录没改过来,像出现幻觉一样

不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增删除

解决 <u> 不可重复读 </u> 的问题只需 <u> 锁住满足条件的 </u> 行,解决 <u> 幻读 </u> 需要锁表 <u> 四种隔离级别 </u>

隔离级别

事务隔离级别 脏读 不可重复读 幻读 说明
读未提交(read-uncommitted) 最低的事务隔离级别,一个事务还没提交时,它做的变更就能被别的事务看到
不可重复读(read-committed) 保证一个事物提交后才能被另外一个事务读取。另外一个事务不能读取该事物未提交的数据。
可重复读(repeatable-read) 多次读取同一范围的数据会返回第一次查询的快照,即使其他事务对该数据做了更新修改。事务在执行期间看到的数据前后必须是一致的。
串行化(serializable) 事务 100% 隔离,可避免脏读、不可重复读、幻读的发生。花费最高代价但最可靠的事务隔离级别。

控制和查询

select @@tx_isolation; // 查询隔离级别
set session transaction isolation level read uncommitted; // 设置隔离级别

PHP 实现

// mysql
mysql_query("COMMIT");//提交事务
mysql_query("ROLLBACK");//至少有一条sql语句执行错误,事务回滚
mysql_query("END");//事务结束

// mysqli
$conn = mysqli_connect('127.0.0.1', 'root', 'root') or die(mysqli_error());  //连接数据库 
mysqli_query($conn, 'BEGIN');    //开启事务
mysqli_query($conn, 'COMMIT');    //提交事务
mysqli_query($conn, 'rollBack');    //回滚事务

//PDO
$pdo->beginTransaction(); //开启事务 
$pdo->commit(); //提交事务  
$pdo->rollBack(); //回滚事务  
$pdo->end(); //结束事务  


// Laravel
// 方法一,transaction方法
DB::transaction(function () {
    DB::table('users')->update(['votes' => 1]);
    DB::table('posts')->delete();
});

// 方法二、手动事务
DB::beginTransaction();  //开启事务
DB::commit();  //事务提交
DB::rollBack();  // 事务回滚 

本文链接:https://ariser.cn/index.php/archives/420/
本站文章采用 知识共享署名4.0 国际许可协议进行许可,请在转载时注明出处及本声明!