数据库事务基础知识

前言

本文主要对数据库事务的一些基础概念进行总结,目的是为理解 Spring 的事务管理展开铺垫,并不涉及底层事务的实现,文中所提到的事务均为单机事务。

事务定义

维基百科对事务的定义如下:

  • 数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

简单来说就是可以将多条 SQL 作为一个整体进行执行。

事务特性

但是并不是说多条 SQL 一起执行就是事务了,事务通常具有 ACID 4 个特性。维基百科对 ACID 特性的描述如下。

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

我们常用一个转账的例子来理解事务。小王和小张各有100元,现在需要将小王的100元转账给小张。对于转账这个例子,需要分成两步完成,先将小王的100元转出,然后将这100元转入到小张的账户上。体现的 ACID 特性如下。

  • 原子性:小王的100元转出和小张的100元转入两个操作是作为一个整体的,要么转出和转入都执行,要么转出和转入都不执行。如果不满足原子性,那么小王的100元成功转出后,小张确没有收到这100元,那么小王的100元就找不到了。
  • 一致性:转账前小王和小张一共有200元,转账后小王和小张的余额(状态)虽然发生变化,但总金额仍然是200。
  • 隔离性:对于转账这个例子,至少需要保证事务提交前,小王和小张的余额不会发生变化。
  • 持久性:只要事务已经提交,那么小王和小张的余额就真的发生了变化,后续即便数据库宕机也是这样。

事务隔离级别

隔离性是事务的其中一个特性,它解决的是对数据库并发操作时一个事务的提交何时对另一个事务可见的问题。主要分为4种隔离级别。

读未提交(read uncommitted)

读未提交表示事务A可以读取到事务B未提交的事务。

以转账为例,读未提交级别下,事务A将小王的钱刚转入还未转入到小张的账户,事务B就发现小王少了100块钱。

那么它有什么并发问题呢?读未提交隔离级别下,事务A会读取到事务B未提交的结果,这称为脏读,也就是说读取的数据是脏的,有问题的。

读已提交(read committed)

读已提交表示事务A可以读取到事务B已经提交的事务。这是为了解决脏读问题。

以转账为例,读已提交隔离级别下,事务A将转入转出两个操作完成并成功提交事务后,事务B才会发现小王少了100块,小张多了100块。

在读已提交隔离级别下,考虑这样一个场景:事务A将小王账户余额减100,小张余额加100并提交了事务。事务B此时查询到小王余额为0,小张余额为200,此时事务B还未提交。事务C又将小张的余额转入100到小王账户。事务B又读取这两个账户,发现小王和小张的余额又变成了100。事务B对于同一记录的多次读取产生了不同的结果,这称为不可重复读。

可重复读(repeatable read)

可重复读表示事务A多次读取相同的记录时,不管读取多少遍,读取的结果都是一样的。这是为了解决不可重复读问题。

以转账为例,在可重复读隔离级别下,事务A开启后,读取到的小王和小张各有100元,在事务A提交前,不管读取多少遍这两个账户的记录,读取到的都是各100元。

那么可重复读就是完美的吗?仍然不是。系统对账时,事务A刚开始读取到只有小王和小张两个账户,后来事务B又创建了一个小李的账户,事务A再次读取系统中的账户数据会发现多了一个小李的账户,好像发生了幻觉一样,这称为幻读。

不可重复读和幻读很类似,不同的是不可重复读侧重的是对同一记录的读取,而幻读侧重于描述读取到了新增的记录。

序列化(serializable)

序列话是最高的事务隔离级别,事务按照顺序进行执行,前面的事务执行结束后面的事务才能继续执行。

隔离级别小结

不同的事务隔离级别越高,性能也就越差,因此并不是事务隔离级别设置的越高越好。MySQL 默认的事务隔离级别是可重复读,而 Oracle 默认的事务隔离级别则是读已提交。

总结事务隔离级别与并发问题关系如下。

事务隔离级别脏读不可重复读幻读
读未提交
读已提交×
可重复提交××
序列化×××

MySQL 事务

默认情况下,每条 SQL 都会使用一个事务进行执行,SQL 执行结束事务自动提交。

查询是否自动提交事务:

-- 查询当前会话是否自动提交事务
select @@autocommit;

-- 查询全局是否自动提交事务
select @@global.autocommit;

更改是否自动提交事务:

-- 设置当前会话是否自动提交事务;0表示关闭自动提交;1表示开启自动提交
set session autocommit = 0;

-- 设置全局是否自动提交事务;0表示关闭自动提交;1表示开启自动提交
set global autocommit = 0;

查询事务隔离级别:

-- 8.0 之前查询当前会话事务隔离级别
select @@tx_isolation;
-- 8.0 之后查询当前会话事务隔离级别
select @@transaction_isolation;
-- 8.0之前查询全局的事务隔离级别
select @@global.tx_isolation;
-- 8.0之后查询全局的事务隔离级别
select @@global.transaction_isolation;

设置会话事务隔离级别:

-- read uncommitted 读未提交
-- read committed 读已提交
-- repeatable read 可重复读
-- serializable 序列化

-- 设置当前会话事务隔离级别
set session transaction isolation level read committed;
-- 设置全局事务隔离级别
set global transaction isolation level repeatable read;

启用事务:
自动提交及事务隔离级别设置之后,我们就可以启用事务了。

begin; -- 启用事务

commit; -- 提交事务

rollback; -- 回滚事务

保存点

保存点(savepoint)是数据库事务中一个不常用概念,类似于快照,在事务中创建保存点之后,事务可以回滚到保存点而不影响保存点前面的操作,从而避免了回滚整个事务,因此也可以将保存点理解为嵌套事务。Spring 事务传播行为中的 PROPAGATION_NESTED 就是依赖保存点。
关于保存点的相关 SQL 如下。

savepoint name; -- 定义一个保存点

rollback to name; -- 回滚到保存点

-- 保存点会在事务结束后自动释放,手动释放保存点如下
release savepoint name;
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鹏cool

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值