1
buaasoftdavid 228 天前 2
可重读级别好处是可以大大提高数据库的并发。读的数据是假(旧)的概率不高,而且即使是假(旧)的,在一些应用下也不会对应用照成影响,在这样的应用下可重读的作用是巨大的。比如你 12306 定火车票的时候经常查询的时候余票有 1 张,但是付款的时候显示没有余票了。这个查询余票的功能就是可重复读,读到了假(旧)的数据也没事。最后下单的时候的隔离级别高就行。查询余票的隔离级别低可以让更多人同时去查询。
|
2
xlzyxxn 228 天前
这就叫事务
|
3
haython 228 天前
@buaasoftdavid 在 MySQL 中,4 个隔离级别中,可重复读级别性能排名第 3 ,除去”读未提交“和”串行化“这 2 个基本不会使用的级别,可重复读性能最差
|
4
leonshaw 228 天前 1
可重读并不是读的数据是假的或者老版本的,而是在事务中对同一个数据多次读取结果是一致的。
|
5
waytodelay 228 天前
可重读提高并发吗? RR 加的锁比 RC 多啊
@buaasoftdavid |
6
lichao 228 天前
这个要先申明是哪个数据库,例如 MySQL 和 PG 对可重复读的行为就不太一样
|
7
k7262140 228 天前
|
8
sigma65535 228 天前
可重复读有两种读模式,一种快照读,一种当前读。不想读旧数据,用当前读
|
9
abccccabc 228 天前
面试官最喜欢问这个问题了,那么我有一个问题:这个事务隔离机制,有人改动过 mysql 默认配置的值吗?
|
10
demoshengxw 228 天前 2
我以前也不理解可重复读的现实意义是什么,直到我看到一个例子后面就忘不了了 例子如下:假设你在管理一个个人银行账户表。一个表存了账户余额,一个表存了账单明细。到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。
|
11
buaasoftdavid 228 天前
2.第二个方面,MySQL 中,A 事务在执行过程中,要多次使用某条记录,我怕它两次访问查出来结果不一样,反正都是同一条记录,我咋不只查一次,一直就用第一次查出来的。
-------- 比如飞机选座,一个 A 用户先查询座位图,花 10 分钟做决定,选了一个位置 X ,提交。在这 10 分钟内,B 用户查询座位图,选 X ,提交。这个时候如果 A 只查一次然后提交(也就是不可重复读),那 A 和 B 会选同一个座位(冲突)。但实际上在可重复读级别下会再读一次,然后 A 事务提交失败的。 |
12
cnsdytedison 228 天前 via Android
@abccccabc 有,不过原因是从 oracle 迁移到 mysql 有一些本来可以的写法会导致死锁。为了减少改动所以主要是通过改这个来解决也最。
|
13
waytodelay 228 天前
@demoshengxw 我的理解也是对账之类的才走这个 RR 。其他的业务为了并发还是用 RC
|
14
buaasoftdavid 228 天前
@haython 大大提高并发当然是相对于可窜行化级别来说的。
|
15
leichnX OP @demoshengxw 认真读了你的例子,我感觉在这个例子中也没有什么数据需要获取两次呀,中途插入了啥也不会有影响吧。把获取对账需要的数据当作事务 A, 它执行两个操作,1 获取用户两个月的余额快照记录,2 获取用户本月账单明细
select * from account where userId = 10 and time > 上个月; select * from order where userId = 0 and time >= 这个月 1 号; |
16
watzds 228 天前
隔离就是看不到其他事务的操作,如果两次读取结果不一样,肯定要么其他事务修改了数据,要么是自己改的(这种情况是可见的),这个实际影响是各种各样的,比如
A() { 是否发通知 = B(); //检查余额,判断是否欠费 C(是否发通知); //发送欠费通知邮件,邮件内容中包含余额 } A 方法整个是一个事务,B C 方法内部都有读数据库操作,如果重复读结果不一样,发送的通知内容中余额可能是 100 非欠费 |
17
leonshaw 228 天前
> 那么此时我这个 A 事务还有必要用旧数据往下走吗,走出来的结果能符合预期吗
这种问题要靠锁(乐观锁)解决,更低的隔离级别并不能保证最后一次读之后到提交前不会有其它改动。 |
18
watzds 228 天前 2
@watzds #16 当然也可以只读一次,后续复用,但是这样对代码有要求,可能根据不同情况,复用的内容也不一样,那多麻烦,一不小心就出错,那还叫事务吗
另外 A() { 是否发通知 = B(); //检查余额,判断是否欠费 //这时刚好其他事务充值成功 C(是否发通知,余额); //发送欠费通知邮件,邮件内容中包含余额和最近 N 次充值记录 } 就算余额被你复用了,但是充值记录读取到最新的,导致看起来明明刚充值,还是欠费 |
19
me1onsoda 228 天前
我一直不理解可重读这个场景有什么存在的必要。
为什么多次查询同一条记录,想多次使用就存起来呗 |
20
git00ll 228 天前 1
@leichnX 你需要查询 account 和 order 两张表,查询 account 后你会获取到一个余额,然后再查询 order 时你肯定希望此时查询的明细就是查询 account 那一瞬间对应的 order ,不然如果 order 被修改了,你的 order 和 account 会对不齐。
这里的可重复读不止针对同一条数据的可重复读,如你所说业务上也许不会在事物内针对同一条数据查两遍, 但是跨表的可重复读是很有用的。 |
22
leichnX OP @git00ll 这两次查询查两张表,可重读会帮忙把它们时间间隙卡的很短吗,以前还真没了解过这个。它会和 Java 多线程一样使得两个查询之间线程不会中断吗?
|
23
git00ll 228 天前
@leichnX 不是时间间隙卡的很短。 当事物内进行第一条 sql 查询的时候就会生成一个快照版本号,并且对所有的表生效。相当于给所有的表生成了快照。 所以能够保证后续的查询(无论哪张表)都是那一瞬间的快照值,因此能够保证读一致性。
|
24
leichnX OP @git00ll 仔细想想感觉不对啊,比如手动执行 sql 的时候,先启动事务,在执行第一条查询查表 1 ,等五秒再执行第二条查询查表 2 ,这中间别人要是插了数据进表 2 去,肯定是会查出来的,什么隔离级别也会查到插进去的新数据。
|
25
git00ll 228 天前 1
|
26
leichnX OP @git00ll 又测了一下,你说的是对的,我开始拿 SERIALIZABLE 测了一遍,结果和我说的一样,我想当然的以为可重读也是会读到新插数据或改的数据。然而可重读读到的还真是旧数据,这很牛逼,很重要。
事务 A SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN SELECT * FROM 表 1 ; 转过去执行事务 B SELECT * FROM 表 2 ; //发现读到的是旧数据 commit; 事务 B SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN UPDATE 表 2 SET Name='22' |
28
luoqeng 228 天前
|
29
vczyh 228 天前 1
可复重读隔离级别内在含义是可以保证一致性,当你第一次执行 select 语句的时候就决定了接下来你能读到什么数据,这些数据跟其他事务是否修改没有关系。
|
30
orzwalker111 228 天前 1
mvcc 保证了 rr 下可重复读,mvcc 是 undo log 链和 readview 视图实现的,这个视图是在事务开启后执行第一个快照读时,基于 [整个库] 生成的快照。也就是说这个快照中的“账单、明细这两个表数据已经不会变了“
10 楼所举的例子很合适,不要局限于”单张表在一个事务中的多次查询“这种场景 |
31
leichnX OP @orzwalker111 不要局限于”单张表在一个事务中的多次查询“这种场景 这句话说的太对了,然而从学校到社会,可重读这个问题大多数人都是拿单张表说事儿,太容易让人困惑了
|