http://blog.panic.so/share/2015/06/13/%E5%B9%B6%E5%8F%91%E7%B3%97%E4%BA%8B/
数据库并发操作
Mysql支持并发的能力很强大,产品线上一个用户数据记录的表已经保存了5千万条记录,仍然能够很好的提供数据查询速度, 当然前提是建立正确的索引,关于索引推荐美团的这篇文章:MySQL索引原理及慢查询优化。
单一记录写并发
问题源自于一条产品线上的一个类似抽奖红包的逻辑,每次用户中奖之后,需要做一个中奖金额total自增的操作,这样来限 制媒体能发出去的红包的总金额,而这个total值被存储在mysql的一条记录中,每次有用户中奖,就需要更新这条记录,这样 随着用户量的增加,很快就出现问题了。当我们开启是一个事务来修改这条记录时,其他同样的事务就会被阻塞,这样使得对 这条记录的并发更新变的非常慢,导致mysql链接一直被占用不能释放,最后mysql的链接被占满,使得数据库无法连接。
类似的逻辑应该可以在内存中进行,例如上文提到的利用全局map来实现,即便是加了锁,相信对于业务server来说要达到更新 map的性能瓶颈也不大可能。
超卖问题
用户消费积分的场景,需要判断用户积分是否充足并减去消费的金额数,而在并发场景下,如果用户两次消费请求同时到来,可 能会产生race condition,最终可能用户积分被消费成了负数。
解决方法一种是在业务逻辑上将每个消费请求先入队列,再由单独的线程逐个处理这些请求,从而转化为了串行处理过程。 类似上文提到的map的封装的原理。
另一个方法是利用mysql的行锁来解决,innodb提供了select … for update语句来锁住某一行,在事务中执行 该语句之后,在该事务提交之前,其他相同的select … for update会被阻塞直到当前事务提交,而正常的select语句可以 执行,这样利用数据库特性将并行变成了串行,避免了race condition带来的问题。
并发场景下很多问题会变得复杂些,其实在很多对性能要求不高的场景下,用串行逻辑来解决问题,可能更加简单也不容易 出问题,而在一些高并发的场景下,就需要考虑更多的内容。