数据库事务隔离级别问题

44 天前
 leichnX
可能我这个问题提的有点儿肤浅,大佬们别嘲笑。我有个疑问,可重读级别下,读的数据可能是个假的,是个老版本的,这到底有啥作用。
2146 次点击
所在节点    数据库
31 条回复
buaasoftdavid
44 天前
可重读级别好处是可以大大提高数据库的并发。读的数据是假(旧)的概率不高,而且即使是假(旧)的,在一些应用下也不会对应用照成影响,在这样的应用下可重读的作用是巨大的。比如你 12306 定火车票的时候经常查询的时候余票有 1 张,但是付款的时候显示没有余票了。这个查询余票的功能就是可重复读,读到了假(旧)的数据也没事。最后下单的时候的隔离级别高就行。查询余票的隔离级别低可以让更多人同时去查询。
xlzyxxn
44 天前
这就叫事务
haython
44 天前
@buaasoftdavid 在 MySQL 中,4 个隔离级别中,可重复读级别性能排名第 3 ,除去”读未提交“和”串行化“这 2 个基本不会使用的级别,可重复读性能最差
leonshaw
44 天前
可重读并不是读的数据是假的或者老版本的,而是在事务中对同一个数据多次读取结果是一致的。
waytodelay
44 天前
可重读提高并发吗? RR 加的锁比 RC 多啊
@buaasoftdavid
lichao
44 天前
这个要先申明是哪个数据库,例如 MySQL 和 PG 对可重复读的行为就不太一样
k7262140
44 天前
sigma65535
44 天前
可重复读有两种读模式,一种快照读,一种当前读。不想读旧数据,用当前读
abccccabc
44 天前
面试官最喜欢问这个问题了,那么我有一个问题:这个事务隔离机制,有人改动过 mysql 默认配置的值吗?
demoshengxw
44 天前
我以前也不理解可重复读的现实意义是什么,直到我看到一个例子后面就忘不了了 例子如下:假设你在管理一个个人银行账户表。一个表存了账户余额,一个表存了账单明细。到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。
buaasoftdavid
44 天前
2.第二个方面,MySQL 中,A 事务在执行过程中,要多次使用某条记录,我怕它两次访问查出来结果不一样,反正都是同一条记录,我咋不只查一次,一直就用第一次查出来的。
--------
比如飞机选座,一个 A 用户先查询座位图,花 10 分钟做决定,选了一个位置 X ,提交。在这 10 分钟内,B 用户查询座位图,选 X ,提交。这个时候如果 A 只查一次然后提交(也就是不可重复读),那 A 和 B 会选同一个座位(冲突)。但实际上在可重复读级别下会再读一次,然后 A 事务提交失败的。
cnsdytedison
44 天前
@abccccabc 有,不过原因是从 oracle 迁移到 mysql 有一些本来可以的写法会导致死锁。为了减少改动所以主要是通过改这个来解决也最。
waytodelay
44 天前
@demoshengxw 我的理解也是对账之类的才走这个 RR 。其他的业务为了并发还是用 RC
buaasoftdavid
44 天前
@haython 大大提高并发当然是相对于可窜行化级别来说的。
leichnX
44 天前
@demoshengxw 认真读了你的例子,我感觉在这个例子中也没有什么数据需要获取两次呀,中途插入了啥也不会有影响吧。把获取对账需要的数据当作事务 A, 它执行两个操作,1 获取用户两个月的余额快照记录,2 获取用户本月账单明细

select *
from account where userId = 10 and time > 上个月;

select *
from order where userId = 0 and time >= 这个月 1 号;
watzds
44 天前
隔离就是看不到其他事务的操作,如果两次读取结果不一样,肯定要么其他事务修改了数据,要么是自己改的(这种情况是可见的),这个实际影响是各种各样的,比如



A() {
是否发通知 = B(); //检查余额,判断是否欠费

C(是否发通知); //发送欠费通知邮件,邮件内容中包含余额
}

A 方法整个是一个事务,B C 方法内部都有读数据库操作,如果重复读结果不一样,发送的通知内容中余额可能是 100 非欠费
leonshaw
44 天前
> 那么此时我这个 A 事务还有必要用旧数据往下走吗,走出来的结果能符合预期吗

这种问题要靠锁(乐观锁)解决,更低的隔离级别并不能保证最后一次读之后到提交前不会有其它改动。
watzds
44 天前
@watzds #16 当然也可以只读一次,后续复用,但是这样对代码有要求,可能根据不同情况,复用的内容也不一样,那多麻烦,一不小心就出错,那还叫事务吗

另外

A() {
是否发通知 = B(); //检查余额,判断是否欠费

//这时刚好其他事务充值成功

C(是否发通知,余额); //发送欠费通知邮件,邮件内容中包含余额和最近 N 次充值记录
}

就算余额被你复用了,但是充值记录读取到最新的,导致看起来明明刚充值,还是欠费
me1onsoda
44 天前
我一直不理解可重读这个场景有什么存在的必要。
为什么多次查询同一条记录,想多次使用就存起来呗
git00ll
44 天前
@leichnX 你需要查询 account 和 order 两张表,查询 account 后你会获取到一个余额,然后再查询 order 时你肯定希望此时查询的明细就是查询 account 那一瞬间对应的 order ,不然如果 order 被修改了,你的 order 和 account 会对不齐。


这里的可重复读不止针对同一条数据的可重复读,如你所说业务上也许不会在事物内针对同一条数据查两遍, 但是跨表的可重复读是很有用的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1026735

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX