分布式锁是什么东西?看了这个你就懂了

引子

很久很久以前,那会我还是一个刚刚接触java分布式开发的小菜鸟,有一次接到一个小需求:每天定时为一些用户发送消息。

听完需求,一合计,这个简单,分分钟就搞定了,不就是一个定时任务嘛。然而当我把功能上到测试环境却测试不通过,因为每一次发送消息的时候都会给一个人一次性发送3条一模一样的消息。因为这个bug我第一次接触到了分布式锁的概念,今天我们就来一起分析一下分布式锁。

什么是分布式锁

分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干扰来保证一致性。

下图展示了分布式锁在工作中的基本流程:

分布式锁是什么东西?看了这个你就懂了

分布式锁系统流程

那么一个好的分布式锁需要具备哪些特性呢?

  • 互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。

  • 安全性:锁只能被持有该锁的客户端删除,不能由其它其它客户端删除。

  • 死锁:获取锁的客户端因为某些原因(如down机等)而未能释放锁,其它客户端再也无法获取到该锁。

  • 容错:当部分节点(redis节点等)down机时,客户端仍然能够获取锁和释放锁。

  • 高效:分布式锁往往会成为性能的瓶颈,分布式锁的性能将会大大的影响系统的性能。

所以选择一个好的分布式锁控制中心就是尤为关键了,目前比较常用的有以下几种方案:

  • 基于数据库实现

  • 基于缓存实现

  • 基于zookeeper实现

下面我们就一一分析这三种实现方式以及他们各自的特点。

基于数据库实现

在这个方案中,需要在数据库中创建一张分布式锁信息表,其中一个字段设置为唯一主键,获取锁的时候就插入数据,释放锁的时候就删除这条数据,这个唯一主键中存放的就是分布式锁的唯一标识。

该方案的特点:

  • 依赖于数据库,实现方式简单。

  • 无法做到锁的超时释放,如果出现死锁将会一直持续下去。

基于缓存实现

常用的缓存系统有:redis,memcache,tair等。我们以redis为例介绍一下依赖于缓存的分布式锁是如何实现的。

我们可以把分布式锁的唯一标识作为redis存储的key。当客户端需要获取锁的时候,就用锁的key到redis中get一下值。如果存在值,就表示该锁不可用,获取失败;如果获取不到值,表示该锁可用,获取成功。获取到锁以后,需要向缓存中写入任意值(比如写入1),如果需要也可以设置超时时间。

该方案的特点:

  • 有些缓存系统并不能够很好的提供具有原子性的操作。

  • 具有失效时间,如果出现死锁,能够自动的释放。

  • 锁的失效时间需要小心设置,过长或果断都可能引起问题。

基于zookeeper实现

客户端需要获取锁的时候,在zookeeper上的与锁唯一表示对应的节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。

该方案的特点:

  • 当发生宕机的时候,锁会第一时间就被释放。

  • 网络不稳定的时候可能会出现锁被错误释放的问题。

总结

上面几种方案,各有各的特点,在方案的选择上需要根据实际系统业务来选择。


分享到:


相關文章: