分布式锁接口
分布式锁概述
随着业务复杂度提升和流量增长,集群部署和微服务化已逐渐成为标配。分布式系统会面临共享资源、同步访问等问题,在不同的进程必须以互斥的方式操作共享资源时,分布式锁是非常有用的原语。Martin Kleppmann(英国剑桥大学的分布式系统研究员)认为一般使用分布式锁有两个场景:
- 效率:使用分布式锁可以避免不同节点重复相同的工作,这些工作会浪费资源。比如用户付了钱之后有可能不同节点会发出多封短信。
- 正确性:加分布式锁同样可以避免破坏正确性,比如多个节点机器对同一个订单操作不同的流程有可能会导致该笔订单最后状态出现错误,造成损失。
比如在生产中,我们会遇到:
场景一:出现缓存击穿后,一段时间内需要同样资源的请求大量到来,此时如果没有对需要加载的资源做互斥约束的话,会对DB产生较大压力。
场景二:交易场景下订单支付结果回调以及取消订单时维护订单状态机正确性的场景。
在实际使用中,发现大家存在因不确定何时使用分布式锁,使用方式错误等导致的线上问题。因此本文梳理了分布式锁的解决方案并进行对比,结合历史出现的coe分析生产中使用分布式锁的各类问题和处理建议。
分布式锁目标
在单进程下,或者说在同一JVM中,由于Java内存模型设计及处理器对指令重排序的优化,在并发场景下存在原子性、可见性、有序性的问题,进而带来多线程下的数据安全问题。针对该问题,Java为我们提供了如synchronized、Lock、AtomicInteger等同步原语。但在生产环境往往使用分布式系统,实际上是多进程场景下的数据安全问题,Java提供的关键字和工具类无法解决,分布式锁(Distributed Locks)是解决该问题的方案之一。
结合本地锁的特征,分布式锁首先要保证:
- 互斥:同一个锁在被持有的时间段内只能被一个线程持有
- 锁超时:保证持有锁的线程出现异常时(例如Client失效,宕机等),锁不会被永久占用
此外,在一些场景中我们还希望分布式锁实现:
阻塞/非阻塞:和ReentrantLock一样支持lock和trylock
可重入:像ReentrantLock一样的可重入锁
公平/非公平:是否需要按照请求加锁的顺序获得锁
性能:在分布式的高并发场景下还需要考虑性能
分布式锁接口
目前已投放为例子的投放数据:
public interface Lock {
void lock();
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void lockInterruptibly() throws InterruptedException;
void unlock();
String getName();
// Condition newCondition();
}