分布式锁实现

本文是笔者对看到过和使用过相关分布式锁的整理和总结,分布式锁主要的实现有以下三种方式

  • 基于数据库表的分布式锁实现
  • 基于redis实现的分布式锁
  • 基于zookeeper实现的分布式是锁

1、背景

在单机多线程环境下,为了实现锁,可以使用java提供的synchronized和java.util.concurrent.locks.Lock。随着大规模分布式系统的兴起,一个应用需要部署在多台机器上,多台机器之间怎么保证访问顺序性、同一时刻操作的幂等性,是分布式锁需要考虑的问题。下面举一个笔者在实际工作中遇到分布式锁的场景
分布式锁使用场景
背景是统计每5分钟的数据量,统计数据的工程部署在多台机器上,当统计数据到来时,随机选择在一台机器上进行统计,每台机器都可以对缓存数据进行累加更新,每5min钟对统计数据进行落库,缓存中的键为整点5min时间戳,值为这5min之内的累加的统计数据,当对缓存进行更新时,需要使用分布式锁进行更新。如果在更新的时候没有使用分布式锁,当两台机器同时进行更新,将会出现丢失一条信息的情况。

2、基于数据库分布式锁

数据库实现分布式锁有两种方式:

  • 数据库事务
  • 数据库表的唯一性约束
(1)数据库事务

数据库事务有一个特点是当一个事务更新数据库中一行操作,另一个事务更新同一行的时候会失败,利用这一特征,事务可以保证同一时间只有一个操作,数据库事务分布式锁实现流程如下图所示
数据库事务实现的分布式锁

(2)数据库表的唯一性约束

利用数据库唯一性约束,获取锁时insert一条数据,当插入成功时表示获取到了锁,执行程序逻辑操作,执行完成之后,删除对应的数据,表示释放了对应的锁,结束;当插入失败时,表示获取锁失败,结束。如下图所示,获取锁的操作流程
唯一性约束实现的锁

(3)优缺点

  • 优点
    严格互斥
  • 缺点
    依赖数据库的性能

3、基于redis实现的分布式锁

redis提供了一些原子性操作如SETNX、GET、GETSET、DEL等,由这些操作可以实现分布式锁,如下图所示分布式锁的实现流程图
redis实现分布式锁
redis中SETNX(key, value)原语操作提供了如果存在key则返回0,如果不存在则返回1,利用这一特性可以实现分布式锁,value为对应的超时时间和获取锁的业务方id(t | id),在SETNX失败后,调用GET原语操作,获取id和超时时间查看超时时间是否超时,如果未超时,则结束;如果超时,则调用GETSET原语操作重新设置t和id,同时判断获取的id和GET操作获取的id是否相等,如果相等则表示获取到了锁,进行相应的逻辑操作,不相等则结束。在删除锁时,需要判断,是否是当前正在使用的锁(id)和未超时,满足这两个条件,则可以删除锁。

4、基于Zookeeper实现的分布式锁

zk(zookeeper)是分布式集群处理技术的一种解决方案,其客户端curator提供了对zk集群的各种操作。其中包括分布式锁的相关操作,其中org.apache.curator.framework.recipes.locks.InterProcessMutex实现了分布式可重入锁的实现,它提供的两个方法acquire()和release()分别实现了获取锁和释放锁的操作。
InterProcessMutex封装的锁实现,当客户端断掉时,锁节点会自动删除。zk提供了watch功能,当节点删除时,会通知其他客户端去获取锁。

5、总结

数据库实现分布式锁方式有很大的局限性,受到数据库性能的影响,redis实现的分布式锁较为复杂,最好的实现分布式锁的方式是基于zk实现的分布式锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值