上周双11,客户要跟风做个问卷答题领爱奇艺会员的活动。
客户那边和爱奇艺的合同是5000个会员,让我们做好并发控制,如果超过了我们要赔钱......
我们这边的流程是先查询数据库记录条数,如果小于5000,则调用爱奇艺的接口,
将返回数据与答题答案一起存在库中,如果大于5000,则只记录答题答案不调用
爱奇艺的接口。
由于公司基于数据库的查询有一套自己的方式,这里为了保证查询,调用爱奇艺,
新增数据都在一个事物内,这里采用了JDBC的方式连接数据库,手动设置提交或者回滚。
起初接到这个问题想通过单例模式来解决,后来因为客户要弄分布式服务就转换了思路,
采用“悲观锁”来解决问题,即通过对数据库上锁的方式来解决。
初步做法是
select * from xxx for update //通过for update锁住该表
//执行爱奇艺接口 insert xxxx //执行添加记录方法 commit() //提交事物
然后模拟并发,我们发现(A为先执行的程序,B为后执行的程序):
程序的执行顺序确实是按照我们预期的那样,待A commti之后 B才开始的查询,但是B 查询到的数据始终和 A查询到的条数相同。
这点让我很费解,起初以为是jdbc预查询导致的,后面用普通的查询语句一样还是这种情况,于是我做了如下模拟:
1):第一个用户进来调用for update语句查询
2)另外开个客户端也调用这个语句
3)此时显示正在执行,表示该表已经被第一个锁住,然后此时我在第一个锁住的客户端中添加了一条数据后提交
4)再看刚才正在执行的窗口
神奇的发现还是只有3条数据,没有刚才新增的数据。那么问题就是出在这里。
于是这时决定在调用查询之前先将表锁住,锁住后在去查询,调用接口,插入数据,提交,通过先锁表来保证真正的查询的数据为数据库的真实数据。
即
select * from xxx for update//通过for update锁住表 select count(*) from xxx //执行爱奇艺接口 insert xxxx //执行添加记录方法 commit() //提交事物
后解决。