最基础的账户余额要怎么在 mysql 实现?

问题场景:

假设用户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重新读取余额,然后再次尝试)

    

    

posted @ 2021-02-08 12:15  执生  阅读(660)  评论(0编辑  收藏  举报