mysql隔离级别 事务并发 - lslgithub151/ideas GitHub Wiki
事务与并发
并发主要是针对服务器而言,服务器并发指的是多个用户同时访问数据库中的同一字段的行为。
生活中的并发
1. 在线考试或报名系统
半夜登录系统报名比白天登录系统报名要容易,网页反应速度也要快一些,这就是由于晚上的并发用户数比较小的原因
2. 订票系统
某航班只有一张机票,假定有1w个人打开网站来订票,只会有一个人订票成功
Mysql结构图

- Server层:mysql系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断, sql解析,行计划优化等; 各个存储引擎提供的功能都集中在这一层,如存储过程,触发器,视图等。
- 存储引擎层:负责存储和获取所有存储在MySQL中的数据,目前懒投资使用的是InnoDB引擎
事务
访问并可能更新数据库中各种数据项的一个程序执行单元
事务原子性:保证事务内的操作要么全部成功,要么全部失败回滚
思考:
- 如果没有事务会怎么样?
- 中间修改的数据,别的事务可以查询到吗?
锁
锁是在执行多线程时用于强制限制资源访问的同步机制
- 排他锁(写锁):对于多个不同的事务,对同一个资源只能有一把锁
- 共享锁(读锁):共享锁指的就是对于多个不同的事务,对同一个资源共享同一个锁
四种隔离级别
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,后一个事务需要等前一个事务提交后再继续执行。
依次使用“未提交读”、“读提交”、“可重复读”的隔离级别练习:

结果如下:
| 未提交读 | 读提交 | 可重复读 |
|---|---|---|
| K1=2 | K1=1 | K1=1 |
| K2=2 | K2=2 | K2=1 |
| K3=2 | K3=2 | K3=2 |
升级版练习题(根据可重复读的隔离级别)
发现问题:如果根据可重复读的级别,不应该等于2吗,为什么K1=3?事务A为什么第二次读到了新数据?
多版本并发控制&快照
- 每一行记录的后面增加两个隐藏列,记录创建版本号和删除版本号;每一个事务在启动的时候,都有一个唯一的递增的版本号
- select 的时候会首先读创建版本号小于或等于当前事务版本号, 并且删除版本号为空或者大于当前事务的版本号;
- inssert 更新创建版本号;
- delete 会更新删除版本号;
- update 更新创建版本号为当前系统版本号, 更新删除版本号为之前的创建版本号

- 事务A(10,9)中的10:事务启动的唯一版本号
- 事务A(10,9)中的9:获取到的当前已提交的版本号
mysql快照读&当前读
- 快照读(snapshot read)
- 简单的select操作(不包括 select ... lock in share mode, select ... for update)
- 当前读(current read)
- select ... lock in share mode
- select ... for update
- insert
- update
- delete
案例分析
- 抢同一个项目
sell()方法是减少项目金额。这种事务代码可以防止并发,但是如果多个用户同时购买,可能会阻塞到这里,又要执行该行之后的很多操作,可能会超时,出现类似序列化的问题,需要等待释放锁后再继续执行
优化:将sell()方法移到事务提交之前,可以优化
- 两个用户抢一个智选项目(此项目剩余金额仅够一个用户购买)
模拟两个用户抢一个智选项目,只能有一个用户购买成功。
- 每个用户只可以领取一张优惠券
不能避免并发,多个线程情况下可以发多个券
不能避免并发,在查询时,查到用户只发了一张券,不满足>1,也可能发多张券
可以避免并发
- 每个用户只可以领取一张优惠券(获取“越转越赚”券)
代码中没有对于事务的并发控制,所以同一个用户并发领券可以领取多张券
修改代码加入并发处理后,可以控制只领取1张券