跳到主要内容

数据库事务

事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求

这些操作要么同时成功,要么同时失败。

默认 MysQL 的事务是自动提交的,当执行一条 DML 语句,MySQL 会立即隐式地提交事务。

事务操作

事务演示如下,如果 2-3 步骤之间报错终端,那么张三就丢了 1000;而李四没收到 1000;

-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额 -1000
update account set money = money - 1000 where name = '张三';
-- 3. 将李四账户余额 +1000
update account set money = money + 1000 where name = '李四';

因此我们需要把上述代码整合成一个事务,完全执行后才提交。

-- 查看事务提交方式
SELECT @@AUTOCOMMIT;
-- 设置事务提交方式,1 为自动提交,0 为手动提交,该设置只对当前会话有效
SET @@AUTOCOMMIT = 0;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;

第二种方式

-- 开启事务:
START TRANSACTIONBEGIN TRANSACTION;
-- 提交事务:
COMMIT;
-- 回滚事务:
ROLLBACK;

调整事务之后,重新运行 sql

-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额 -1000
update account set money = money - 1000 where name = '张三';
-- 3. 将李四账户余额 +1000
update account set money = money + 1000 where name = '李四';
commit;

事务四大特性

四大特性 ACID:

  • 原子性 (Atomicity):事务是不可分割的最小操作但愿,要么全部成功,要么全部失败。
  • 一致性 (Consistency):事务完成时,必须使所有数据都保持一致状态。
  • 隔离性 (Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性 (Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

并发事务问题

问题描述
脏读一个事务读到另一个事务还没提交的数据
不可重复读一个事务先后读取同一条记录,但两次读取的数据不同
幻读一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在

脏读

如下图,事务 A 更新 1,但还未提交,此时被事务 B 查去了,这就导致可能事务 A 最终决定不提交,但是事务 B 拿出来后当真了,所以这种现象叫做脏读。

img

不可重复读

如下图,事务 A 查询第一次后,事务 B 更新了这条数据,事务 A 查询第二次时发现跟第一次查询的结果不一样,这种现象叫做不可重复读。

img

幻读

幻读是在解决 [不可重复读] 的基础上产生的新问题,如下图,事务 A 读取 id 为 1 的数据为空,事务 B 插入 id 为 1 的数据,之后事务 A 想要插入这条数据发现插入不了(比如被主键约束了),然后事务 A 重新查询还是找不到 id 为 1 的数据(因为我们解决了[不可重复读],所以查询出来的结果跟第一次查是一致的)。

img

事务隔离级别

MySQL 默认的事务隔离级别是 Repeatable Read

Oracle 默认的事务隔离级别是 Read committed

隔离级别脏读不可重复读幻读
Read uncommitted
Read committed×
Repeatable Read(默认)××
Serializable×××

√ 表示在当前隔离级别下该问题会出现,

Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差。

-- 查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
-- 设置事务隔离级别:
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
-- SESSION 是会话级别,表示只针对当前会话有效
-- GLOBAL 表示对所有会话有效