用 Redis 实现互斥锁
在多线程执行的程序下,无可避免的会遇到资源竞争的问题,为了解决资源竞争的问题,引入互斥锁应该是公认的解决方案吧,而 Redis 的特性(基于内存,有生存期)我觉得非常适合用来实现互斥锁。
背景
我遇到的问题是在对 Lucene 更新索引的时候,原来采取的策略是将某个数据表增量的数据通过一个线程定期去更新和 flush 索引文件,但由于业务的扩展,原来的业务变成了从2个不同的数据表用2个不同的定期线程去更新。
这个时候问题来了,在 flush 到文件系统中的时候,虽然 Lucene 本身创建了一个 write.lock
来避免文件冲突,但是由于线程中运行的程序在 flush 过程中可能因为异常导致线程直接中断,导致 write.lock
文件没有被及时清除和处理,后续的索引更新线程全部作废。
解决方案
虽然原则上这应该是线程异常处理范畴的问题,不过同时存在下面这2个问题
write.lock
文件没有被及时释放- 如果使用程序全局变量的锁又无法很好地管理和监测
而利用 Redis 的特性去实现互斥锁可以很好地解决这2个问题,原理非常简单
- 需要加锁的时候,生成一个 lock_key,expire time 设置为估计该线程执行大概的时间,内容可以是空也可以将是哪个线程或什么时候产生的信息放到内容中
- 锁的判定,判定上面生成的 lock_key 是否存在
- 线程资源竞争的部分执行完后, del lock_key 释放资源
这样一来,由于
- Key 是有生存期的,即使线程挂掉没有及时释放,但锁在一定时限内还是会自动释放,不影响后续竞争该资源的线程的执行
- 锁的状态可以通过 Redis 的管理工具及时监控得到
- Redis 基于内存,读写效率都很快
- Redis 本身就是单线程模型的实现,比手动在程序中实现单例的锁更简洁