Skip to content

[!quote] 事务 事务 是一组 SQL 语句

  • 事务处理可以回退 INSERT, UPDATE, DALETE。不能回退 SELECT, CREATE, DROP

事务的四大特性【ACID】

  • 原子性 Atomicity:事务要么完全执行,要么完全不执行
  • 一致性 Consistency:事务完成时,必须使所有数据都保持一致状态
  • 隔离性 Isolation:保证事务在不受并发操作影响的独立环境下运行(DBMS 的隔离级别有很多,用户可以自行选择)
  • 持久性 Durability:事务一旦 COMMIT / ROLLBACK,则数据永久改变

❤️ 事务控制机制

修改自动提交

  • @@autocommit = 1 事务的提交方式是自动提交【默认】
  • @@autocommit = 0 事务需要手动提交
sql
SELECT @@autocommit;

---
1
sql
SET @@autocommit = 0;

INSERT INTO test_work
VALUES (3, 'greenteck', '888');
-- 此时数据未更新

COMMIT;
-- 此时数据更新

-- 如果出错了可以使用 `ROLLBACK;` 回滚事务

显式事务

[!hint] 基本思路

  • 如果执行过程中没有发生错误,则 COMMIT 整组 SQL 语句;
  • 如果发生错误,则 ROLLBACK,将数据库恢复到正常状态;
  • START TRANSACTION; 开始事务
  • COMMIT; 结束事务并提交
  • ROLLBACK; 回退到事务处理之前
  • SAVEPOINT 保留点名; 设置保留点:允许在事务中的特定位置进行部分回滚,而不必回滚整个事务
sql
START TRANSACTION;    //第一步先执行此语句

SAVEPOINT point1;    //设置一个保留点

UPDATE table SET id = 188;       //-------------------
UPDATE table SET name = Tom;      //然后执行这组语句体
UPDATE table SET age = 18;       //-------------------

---

COMMIT;      //如果这组语句体都执行成功,则可以执行COMMIT来提交

ROLLBACK;     //如果有语句执行失败了,则执行ROLLBACK来回退

ROLLBACK TO point1;  //返回到保留点point1

❤️ 并发事务问题

[!quote] 数据库的隔离级别

DBMS 有多种隔离级别,隔离级别越高,性能越低

  • 读未提交:允许事务读取其他未提交事务的更改【可能发生脏读,不可重复读,幻读】,最低的隔离级别
    • 虽然隔离级别低,但是并发性能高
  • 读已提交:事务只能读取其他事务已提交的更改【可能发生不可重复读,幻读
  • 可重复读 默认:在事务开始时读取的数据,在事务结束时仍然可以读取到相同的数据【可能发生幻读
  • 串行化:事务将按顺序执行,就像它们是串行的一样,最高的隔离级别
    • 会显著降低并发性能
    • 会增加系统资源的消耗
    • 容易死锁
sql
-- 查看当前会话的隔离级别
SELECT @@SESSION.transaction_isolation;

-- 修改当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL 隔离级别;
-- 隔离级别:
-- READ UNCOMMITTED
-- READ COMMITTED
-- REPEATABLE READ
-- SERIALIZABLE

-- 查看全局隔离级别
SELECT @@GLOBAL.transaction_isolation;

-- 修改全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL level;

脏读

[!quote] 脏读 脏读 就是事务 A 读到了 事务 B 还没有提交的数据

不可重复读

[!quote] 不可重复读 事务 A【一组操作】先后读取同一条记录,但两次读取的数据不同【可能因为在两次读取之间,有事务 B 修改了此记录,并提交了

幻读

[!quote] 幻读 事务 A 没有查询到数据 B,紧接着事务 C 插入了数据 B,接着在事务 A 插入数据 B 时,发现数据 B 已经存在了【就像是出现了幻觉

mermaid
sequenceDiagram
    事务A->>数据库: 查询数据B,没有

    事务C->>数据库: 插入数据B,成功
	事务A-->>数据库: 插入数据B,出错了,数据已存在
    事务A->>数据库: 查询数据B,没有