巧用Hash,避免使用锁
08 September 2017
业务需求
如下图,业务A 往队列写消息,业务B 负责消费产生的消息,每条消息都会有一个用户id,更新存储A、B中与用户相关的信息,更新操作:
- 根据用户id从存储A获取信息valueA
- 根据valueA更新存储B中的信息
- 更新用户在存储A中的信息为valueB
由于对存储A、B的更新操作无法保证事务性,所以当业务B开了多个消费者的时候,就可能并发地更新同一个用户信息,从而导致用户的信息出现错乱。
问题解决
锁
首先想到的解决方案是通过锁来实现,比如使用redis,以用户id作为key,然后执行set userid '' EX 2 NX'
,成功执行了set命令的消费者获得锁,当更新完用户信息后,可以执行delete userid
来将锁释放掉。
然而,使用锁之后,发现消费者的吞吐量下降了很多,于是就想有没办法不用锁。
认识问题本质
问题产生的根本原因在于多个消费者并行地更新用户的信息,加锁是为了让每个用户信息的更新操作能够被串行执行。
所以只要能够使得对同一个用户的更新操作串行地执行,问题就解决了。在不加锁的情况下,可以想办法使得同一个用户的消息都入到同一个队列,从而只被一个消费者消费。
Hash
那么,问题就变成如何将同一个用户的消息入到同一个队列里了,因为每个用户消息里都会有一个用户id,所以可以利用这个用户id的hash值,然后取模,使得同一个用户的消息映射到同一个队列上,从而避免使用锁。