MySQL并发事务处理实例

来源:互联网 发布:游戏补丁软件 编辑:程序博客网 时间:2024/05/21 17:03

微吐槽

前段时间在一家创业公司工作,两个多月将近三个月,做了三个系统,忙得《权力的游戏》都落下好几集,从未有过的压力,也从未有过的充实。种种原因,我还是离开了这里,这段时间学到了很多,例如领域驱动设计的实践、遇到问题如何思考、如何殴打PM云云。这篇文章写写工作中遇到的关于事务的问题。


问题场景

其中开发的一个系统是排班挂号系统,业务简单的说就是排班产生号源,可供挂号预约。涉及两个表
排班表:schedule(id,start,end,capacity,consume),start表开始时间,end表结束时间,capacity表号源容量,consume表已消费号源数
挂号预约表:reservation(id,scheduleid),scheduleid即关联的排班表id
例如记录schedule(1,2016-07-17 09:00,2016-07-17 10:00,10,3)表示时间2016-07-17 09:00到2016-07-17 10:00有10个号源,已经被预约了3个。显然,consume为10即等于capacity的时候表示已预约满,就不能再继续预约了。在代码中,一个挂号处理逻辑:先要查询这条记录,判断comsume是否小于capacity,true时便可继续挂号,生成一条reservation记录,并且consume加1。其实这个问题和一些电商系统是一样的,一个商品下单后,要保证商品数量减1,并且不能减成负数。那么,如何保证并发情况下,号源被正常的预约呢?


问题分析与解决

对于两个并发事务同时挂号,会产生以下可能问题(以下情况假设capacity为10)

问题一:


时间

事务A

事务B

T1

开始事务

 

T2

 

开始事务

T3

 

查询号源(consume=8)

T4

 

更新已预约数(consume=9)

T5

查询号源(consume=9)

 

T6

 

撤销事务

T7

更新已预约数(consume=10)

 

T8

提交事务


事务A查询到了事务B所做的未提交的修改,consume被更新为10,实际应该为9,这种情况即产生脏读,将事务隔离级别设为READ_COMMITTED便能解决。


问题二:


时间

事务A

事务B

T1

开始事务

 

T2

 

开始事务

T3

查询号源(consume=9)

 

T4

 

查询号源(consume=9)

T5

 

更新已预约数(consume=10)

T6

 

提交事务

T7

更新已预约数(consume=10)

 

T8

提交事务


这种情况就是只有最后一个号源的时候,两个人(事务)同时都预约到了。所有应该避免在查询号源和更新已预约数两个操作之间有其他事务对该号源进行更新,故一个事务开始后就应该对当前号源进行锁定,相当于多个事务是顺序执行的,对应的事务隔离级别为SERIALIZABLE

issue

留个问题:把事务隔离级别设置为最高的SERIALIZABLE确实能解决上述问题,简单粗暴,但是随着并发量的上升,系统性能肯定大大降低。只考虑从事务的角度解决这个问题的话,REPEATABLE_READ级别是否也能解决呢?

1 0
原创粉丝点击