数据库的事物和隔离级别

本文详细解析了数据库事务的ACID特性:原子性、一致性、隔离性和持久性,以及事务隔离级别的设置如何防止并发操作带来的脏读、不可重复读和幻读等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事物的基本属性(ACID)

  1. 原子性: 事务涉及的多步操作要么全部执行, 要么就回滚导致全部不执行
  2. 一致性: 事务执行的中间过程, 对其他事务不可见,
  3. 隔离性: 事务和事务之间独立执行, 互不干扰
  4. 持久性: 事务提交后, 事务更新的数据将被写入数据库中, 不可再回滚

很多人区分不了原子性和一致性, 我是这样理解的, 正是因为事务的多步操作只能要么全部执行, 要么全部不执行, 才保证了事物的一致性, 使得数据库中的数据不会停留在事物执行的中间转态, 两者只是描述的方面不一样而已.

事物的隔离级别

事物的隔离级别的设置, 是防止查询操作时带来问题, 不同的隔离级别是能够解决不同层次的问题的

  1. 读未提交: 可以读取还未提交的事物的结果
  2. 读已提交: 可以读取已经提交的事物的结果
  3. 可重复读: 可以重复读取数据, 而不担心数据被删除
  4. 串行化: 事物不能并发执行, 只能串行
    下面我们来看这不同的隔离级别可能在并发的情况下产生哪些问题

数据库事务并发存在的问题

并发的概念百度百科: 并发

  1. 第一个问题: 脏读

为了解释什么是脏读, 这里我们假设此时此刻数据库中正在执行着A事务和B事务, 而此时的隔离级别是读未提交, 假设A读取的数据是B在事物执行过程中产生的, 而B事物因为执行失败 回滚了,
此时A读取到的数据就是数据库中不存在的 脏数据

  1. 第二个问题: 不可重复读

同理, 我们假设此时此刻数据库中正在执行着A事务和B事务, 此时事物的隔离级别是读已提交,
事物A读取数据后, 事物B对事物A读取的数据进行了 更新, 从而导致事物A下次读取到的数据和上
一次读取到的不一致, 称为 不可重复读

  1. 第三个问题: 幻读

假设此时此刻数据库中正在执行着A事务和B事务, 事物A对数据库进行了查询, 得到查询集, 事物B执行时向数据库中插入了数据, 且这些数据满足事物A的查询条件, 也就是说A在下一次查询的时候, 会发现查询集中多了数据, 称为幻读

事物隔离级别脏读不可重复读幻读
读未提交yesyesyes
读已提交noyesyes
可重复读nonoyes
串行nonono

解释隔离级别和并发问题的关系

关于并发产生的问题, 我们首先想到的解决方法就是锁, 而在数据库中, 锁就分为了 读锁写锁, 它们之间, 相互排斥, 加了读锁就只能事务进行读, 其他事务就不能对读取到的数据进行更新了.
加了写锁的数据, 其他事务就不能读取到了. 而且加了写锁的数据数据库中并不存在, 只存在于对数据进行更新的事物中.

  1. 读锁: 事物在进行数据读取的时候, 对读取到的数据加锁, 不允许其他事务更新数据
  2. 写锁: 事务对某数据进行更改的时候, 对要更改的数据加锁, 不允许其他事务读取数据
  • 脏读:
    • 事务B更新数据时, 未对更新的数据加 写锁, 使得A能够访问B更新后的数据, 但是B回滚后, 导致该问题
  • 不可重复读:
    • 事务A对数据读取后, 未对数据加 读锁, 使得事务B能够更新事务A查询集对应的数据库中的数据, 导致该问题.
  • 幻读: 事务A读取数据后, 事务B更新了数据库中的数据, 而这个数据刚好符合事务A的查询条件, 这个问题加什么锁都没办法解决, 是并发存在的问题, 只能用事务的串行来解决.
    我们再来总结一些问题的避免和锁和事务隔离级别的的关系

隔离级别, 锁, 并发产生的问题之间的关系

  1. 读未提交: 也就是读取数据时不加锁, 更新数据时也不加锁, 从而引发 脏读 问题
  2. 读已提交: 读取数据时不加锁, 更新数据时加写锁, 解决了脏读问题, 但是因为读取数据的时候没有加读锁, 因而可能它读取到的数据被另一个事物更改, 导致 不可重复读 问题.
  3. 可重复读: 读时加读锁, 更新时加写锁, 这样就避免了, 脏读, 和不可重复读的问题. 但是可能会导致 幻读
  4. . 串行: 幻读 的问题跟锁没有关系, 就算有关系, 那也是数据库级别的锁, 在一个事务对数据库进行读取或更新时, 不允许另一个事务对数据库进行任何操作.

附加:

由于幻读不好理解, 我在这里举个例子:

假设数据库中有这样一张表, 表名是table

idage
11

现在我使用这样一条查询语句, 代表事务A:

select * from table where age>=1;

可以推测出结果应该是这样的:

idage
11

单在A事务还未提交时, 另一个事务B执行了这样一条语句, 并提交了:

insert into table values((id=2, age=3), (id=3, age=0));

此时我们的表变成了这样:

idage
11
23
30

此时相应的根据A的查询条件对应的结果应该是这样:

idage
11
23

但A 最终 得到的结果, 却是这样:

idage
11

这就称为幻读, 这是并发带来的问题, 是指同一时刻有多个事务执行, 导致的事务的查询集和事务的查询条件与数据库的映射不相符, 在这个例子里是少了一条数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值