问题场景:
假设用户A的账户余额是 100
现在有两个事务 a,b
a 事务内容是 用户A消费 30 元
b 事物内容是 用户A消费 60 元
如果现在 a 和 b 互相不设防,无论 a 和 b 是什么事务隔离级别(除了序列化),最终结果都可能是错误的
模拟:
1. 读提交情况下,这种情况没有正确可言
2. a, b 均是 可重复读级别
a 读取余额100
b 读取余额100
a 写入余额70 b 也想写入余额40(被a加的行锁阻塞)
b 写入余额40成功
最终余额40,正确余额应该是 10
a 读取余额 100 b 读取余额 100
a 写入余额 70
b 写入余额 40
同上
这种问题的本质是什么呢?可以类比SMP环境,即多核心处理器环境下的多线程/进程并发问题
将 a 和 b 比作是 两个线程/进程,一致性读就是只要一个线程/进程 他把对应的内容的缓存行读入自己的高速缓存
无论以后其他线程怎么改这个内容,他都只会读自己的高速缓存里的内容,所以每次读到的都不是最新值,当然也就存在经典的写覆盖问题了
3. 任意一个事务使用 读提交,也是不可以的,任一未提交,都会读到同一个值,然后在此基础上修改,产生写覆盖
a 读取余额100
b 读取余额100
a 写入余额70 b 也想写入余额40(被a加的行锁阻塞)
b 写入余额40成功
此类问题的本质是 对一个数据的读写不是原子的,中间可能有其他事务插进来 读/写
策略1,使用悲观锁
用悲观锁来保证事务 修改该数据的原子性
a select 余额 ... for update (加写锁)
a 写入余额70 b 读取(阻塞)
a 结束,释放所有锁 b 读取余额 70
b 写入余额 10
策略2,使用乐观锁
步骤1.a select 余额 ,读到100 b select 余额,读到 100
b update 余额 = 40 where 余额 = 100
a update 余额 = 70 where 余额 = 100(失败,回到步骤1重新读取余额,然后再次尝试)