mysql隔离级别 事务并发 - lslgithub151/ideas GitHub Wiki

事务与并发

并发主要是针对服务器而言,服务器并发指的是多个用户同时访问数据库中的同一字段的行为。

生活中的并发

1. 在线考试或报名系统

半夜登录系统报名比白天登录系统报名要容易,网页反应速度也要快一些,这就是由于晚上的并发用户数比较小的原因

2. 订票系统

某航班只有一张机票,假定有1w个人打开网站来订票,只会有一个人订票成功

Mysql结构图

01

  1. Server层:mysql系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断, sql解析,行计划优化等; 各个存储引擎提供的功能都集中在这一层,如存储过程,触发器,视图等。
  2. 存储引擎层:负责存储和获取所有存储在MySQL中的数据,目前懒投资使用的是InnoDB引擎

事务

访问并可能更新数据库中各种数据项的一个程序执行单元 02 事务原子性:保证事务内的操作要么全部成功,要么全部失败回滚

思考:
  1. 如果没有事务会怎么样?
  2. 中间修改的数据,别的事务可以查询到吗?

锁是在执行多线程时用于强制限制资源访问的同步机制

  • 排他锁(写锁):对于多个不同的事务,对同一个资源只能有一把锁
  • 共享锁(读锁):共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁

四种隔离级别

1. 未提交读:一个事务还没提交时,它做的变更就能被别的事务看到;
存在问题:脏读
eg:两个事务,事务B修改k=1为k=2,在未提交前,事务A读取t表的k=2,事务B又回滚并提交,结果事务A读取到了不准确的数据。
2. 读提交:一个事务提交之后,它做的变更才会被其他事务看到
存在问题:虚读
eg:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了。
3、可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的,读的是当时的快照
存在问题:幻读
eg:两个事务,事务A修改整个表的k都为k=0,事务B向表中插入一行k=3的数据并提交,当事务A重复查询时,像出现幻觉一样发现有一个k没有变成0
注意⚠️:懒投资使用的是innoDB 引擎,默认第三种隔离级别
4、序列化:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”,当出现读写锁冲突的时候,后访问的事务必须等前一个数据执行完成,才能继续执行
存在问题:等待时间
eg:两个事务,事务A修改k=2,同时事务B修改k=1,后一个事务需要等前一个事务提交后再继续执行。
依次使用“未提交读”、“读提交”、“可重复读”的隔离级别练习:

03

结果如下:

未提交读 读提交 可重复读
K1=2 K1=1 K1=1
K2=2 K2=2 K2=1
K3=2 K3=2 K3=2
升级版练习题(根据可重复读的隔离级别)

结果:K1=3 发现问题:如果根据可重复读的级别,不应该等于2吗,为什么K1=3?事务A为什么第二次读到了新数据?

多版本并发控制&快照

  • 每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号;每一个事务在启动的时候,都有一个唯一的递增的版本号
  • select 的时候会首先读创建版本号小于或等于当前事务版本号, 并且删除版本号为空或者大于当前事务的版本号;
  • inssert 更新创建版本号;
  • delete 会更新删除版本号;
  • update 更新创建版本号为当前系统版本号, 更新删除版本号为之前的创建版本号

05

  • 事务A(10,9)中的10:事务启动的唯一版本号
  • 事务A(10,9)中的9:获取到的当前已提交的版本号

mysql快照读&当前读

  1. 快照读(snapshot read)
  • 简单的select操作(不包括 select ... lock in share mode, select ... for update)
  1. 当前读(current read)
  • select ... lock in share mode
  • select ... for update
  • insert
  • update
  • delete

案例分析

  • 抢同一个项目

06 sell()方法是减少项目金额。这种事务代码可以防止并发,但是如果多个用户同时购买,可能会阻塞到这里,又要执行该行之后的很多操作,可能会超时,出现类似序列化的问题,需要等待释放锁后再继续执行

07 优化:将sell()方法移到事务提交之前,可以优化

  • 两个用户抢一个智选项目(此项目剩余金额仅够一个用户购买)

08 模拟两个用户抢一个智选项目,只能有一个用户购买成功。

  • 每个用户只可以领取一张优惠券

09 不能避免并发,多个线程情况下可以发多个券

10 不能避免并发,在查询时,查到用户只发了一张券,不满足>1,也可能发多张券 11 可以避免并发

  • 每个用户只可以领取一张优惠券(获取“越转越赚”券)

12 代码中没有对于事务的并发控制,所以同一个用户并发领券可以领取多张券

13 修改代码加入并发处理后,可以控制只领取1张券