我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Redis实现分布式锁要注意哪些事项

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Redis实现分布式锁要注意哪些事项

本文小编为大家详细介绍“Redis实现分布式锁要注意哪些事项”,内容详细,步骤清晰,细节处理妥当,希望这篇“Redis实现分布式锁要注意哪些事项”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁

最近看分布式锁的过程中看到一篇不错的文章,特地的加工一番自己的理解:

Redis分布式锁实现的三个核心要素:

1.加锁

最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名,value为当前线程的线程ID。

比如想要给一种商品的秒杀活动加锁,可以给key命名为 “lock_sale_ID” 。而value设置成什么呢?我们可以姑且设置成1。加锁的伪代码如下:

setnx(key,1)当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁,当其他线程执行setnx返回0,说明key已经存在,该线程抢锁失败。

2.解锁

有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指令,伪代码如下:

del(key)释放锁之后,其他线程就可以继续执行setnx命令来获得锁。

3.锁超时

锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程再也别想进来。

所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx不支持超时参数,所以需要额外的指令,伪代码如下:

expire(key, 30)综合起来,我们分布式锁实现的第一版伪代码如下:

if(setnx(key,1) == 1){
    expire(key,30)
    try {
        do something ......
    }catch()  {  }  finally {
       del(key)
    }

}

因为上面的伪代码中,存在着三个致命问题:

1. setnx和expire的非原子性

设想一个极端场景,当某线程执行setnx,成功得到了锁:

setnx刚执行成功,还未来得及执行expire指令,节点1 Duang的一声挂掉了。

if(setnx(key,1) == 1){  //此处挂掉了.....
    expire(key,30)
    try {
        do something ......
    }catch()
  {
  }
  finally {
       del(key)
    }
 
}

这样一来,这把锁就没有设置过期时间,变得“长生不老”,别的线程再也无法获得锁了。

怎么解决呢?setnx指令本身是不支持传入超时时间的,Redis 2.6.12以上版本为set指令增加了可选参数,伪代码如下:set(key,1,30,NX),这样就可以取代setnx指令

2. 超时后使用del 导致误删其他线程的锁

又是一个极端场景,假如某线程成功得到了锁,并且设置的超时时间是30秒。

如果某些原因导致线程A执行的很慢很慢,过了30秒都没执行完,这时候锁过期自动释放,线程B得到了锁。

随后,线程A执行完了任务,线程A接着执行del指令来释放锁。但这时候线程B还没执行完,线程A实际上删除的是线程B加的锁

怎么避免这种情况呢?可以在del释放锁之前做一个判断,验证当前的锁是不是自己加的锁。

至于具体的实现,可以在加锁的时候把当前的线程ID当做value,并在删除之前验证key对应的value是不是自己线程的ID。

加锁:
String threadId = Thread.currentThread().getId()
set(key,threadId ,30,NX)
doSomething.....
 
解锁:
if(threadId .equals(redisClient.get(key))){
    del(key)
}

但是,这样做又隐含了一个新的问题,if判断和释放锁是两个独立操作,不是原子性

我们都是追求极致的程序员,所以这一块要用Lua脚本来实现:

String luaScript = 'if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end';

redisClient.eval(luaScript , Collections.singletonList(key), Collections.singletonList(threadId));

这样一来,验证和删除过程就是原子操作了。

3. 出现并发的可能性

还是刚才第二点所描述的场景,虽然我们避免了线程A误删掉key的情况,但是同一时间有A,B两个线程在访问代码块,仍然是不完美的。

怎么办呢?我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续航”。

当过去了29秒,线程A还没执行完,这时候守护线程会执行expire指令,为这把锁“续命”20秒。守护线程从第29秒开始执行,每20秒执行一次。

当线程A执行完任务,会显式关掉守护线程。

另一种情况,如果节点1 忽然断电,由于线程A和守护线程在同一个进程,守护线程也会停下。这把锁到了超时的时候,没人给它续命,也就自动释放了。

memcache实现分布式锁

首页top 10, 由数据库加载到memcache缓存n分钟
微博中名人的content cache, 一旦不存在会大量请求不能命中并加载数据库
需要执行多个IO操作生成的数据存在cache中, 比如查询db多次
问题
在大并发的场合,当cache失效时,大量并发同时取不到cache,会同一瞬间去访问db并回设cache,可能会给系统带来潜在的超负荷风险。我们曾经在线上系统出现过类似故障。

解决方法

if (memcache.get(key) == null) {
// 3 min timeout to avoid mutex holder crash
if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
value = db.get(key);
memcache.set(key, value);
memcache.delete(key_mutex);
} else {
 
sleep(50);
retry();
}
}

在load db之前先add一个mutex key, mutex key add成功之后再去做加载db, 如果add失败则sleep之后重试读取原cache数据。为了防止死锁,mutex key也需要设置过期时间。伪代码如下

Zookeeper实现分布式缓存

Zookeeper的数据存储结构就像一棵树,这棵树由节点组成,这种节点叫做Znode

Znode分为四种类型:

  • 1.持久节点 (PERSISTENT)

默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在 。

  • 2.持久节点顺序节点(PERSISTENT_SEQUENTIAL)

所谓顺序节点,就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号:

Redis实现分布式锁要注意哪些事项

  • 3.临时节点(EPHEMERAL)

和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除:

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

  • 4.临时顺序节点(EPHEMERAL_SEQUENTIAL)

顾名思义,临时顺序节点结合和临时节点和顺序节点的特点:在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号;当创建节点的客户端与zookeeper断开连接后,临时节点会被删除。

Zookeeper分布式锁恰恰应用了临时顺序节点。具体如何实现呢?让我们来看一看详细步骤:

  • 获取锁

首先,在Zookeeper当中创建一个持久节点ParentLock。当第一个客户端想要获得锁时,需要在ParentLock这个节点下面创建一个临时顺序节点 Lock1

Redis实现分布式锁要注意哪些事项

之后,Client1查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock1是不是顺序最靠前的一个。如果是第一个节点,则成功获得锁。

Redis实现分布式锁要注意哪些事项

这时候,如果再有一个客户端 Client2 前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock2

Redis实现分布式锁要注意哪些事项

Client2查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock2是不是顺序最靠前的一个,结果发现节点Lock2并不是最小的。

于是,Client2向排序仅比它靠前的节点Lock1注册Watcher,用于监听Lock1节点是否存在。这意味着Client2抢锁失败,进入了等待状态。

Redis实现分布式锁要注意哪些事项

这时候,如果又有一个客户端Client3前来获取锁,则在ParentLock下载再创建一个临时顺序节点Lock3

Redis实现分布式锁要注意哪些事项

Client3查找ParentLock下面所有的临时顺序节点并排序,判断自己所创建的节点Lock3是不是顺序最靠前的一个,结果同样发现节点Lock3并不是最小的。

于是,Client3向排序仅比它靠前的节点Lock2注册Watcher,用于监听Lock2节点是否存在。这意味着Client3同样抢锁失败,进入了等待状态。

Redis实现分布式锁要注意哪些事项

这样一来,Client1得到了锁,Client2监听了Lock1Client3监听了Lock2。这恰恰形成了一个等待队列,很像是Java当中ReentrantLock所依赖的AQS(AbstractQueuedSynchronizer)

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

  • 释放锁

释放锁分为两种情况:

1.任务完成,客户端显示释放

当任务完成时,Client1会显示调用删除节点Lock1的指令。

Redis实现分布式锁要注意哪些事项

2.任务执行过程中,客户端崩溃

获得锁的Client1在任务执行过程中,如果Duang的一声崩溃,则会断开与Zookeeper服务端的链接。根据临时节点的特性,相关联的节点Lock1会随之自动删除。

Redis实现分布式锁要注意哪些事项

由于Client2一直监听着Lock1的存在状态,当Lock1节点被删除,Client2会立刻收到通知。这时候Client2会再次查询ParentLock下面的所有节点,确认自己创建的节点Lock2是不是目前最小的节点。如果是最小,则Client2顺理成章获得了锁。

Redis实现分布式锁要注意哪些事项

同理,如果Client2也因为任务完成或者节点崩溃而删除了节点Lock2,那么Cient3就会接到通知。

Redis实现分布式锁要注意哪些事项

最终,Client3成功得到了锁。

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

Zookeeper和Redis分布式锁的比较

下面的表格总结了Zookeeper和Redis分布式锁的优缺点:

Redis实现分布式锁要注意哪些事项

Redis实现分布式锁要注意哪些事项

读到这里,这篇“Redis实现分布式锁要注意哪些事项”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注亿速云行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Redis实现分布式锁要注意哪些事项

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

java加锁时要注意哪些事项

在Java中,当我们使用锁来保护共享资源时,我们需要注意以下几个事项:1.选择合适的锁类型:Java提供了多种锁类型,如synchronized关键字、ReentrantLock等。我们需要根据具体的需求选择合适的锁类型。2.锁的粒度:
2023-10-24

Ceph分布式存储集群搭建要注意哪些事项

今天小编给大家分享一下Ceph分布式存储集群搭建要注意哪些事项的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Ceph是可靠的
2023-06-27

docker中redis安装要注意哪些事项

在安装Redis时,需要注意以下事项:选择合适的Docker镜像:可以选择官方提供的Redis镜像,也可以选择其他第三方维护的镜像。建议使用官方镜像,以确保安全和稳定性。确定版本:根据自己的需求选择合适的Redis版本,最新版本通常包含最新
docker中redis安装要注意哪些事项
2024-04-09

ubuntu中安装redis要注意哪些事项

在Ubuntu中安装Redis时,需要注意以下事项:检查Ubuntu版本:确保你的Ubuntu系统是最新的版本,并且已经更新到最新的软件包。使用官方源安装:建议使用官方源安装Redis,这样可以确保获得最新版本的Redis,并且能够方便地更
ubuntu中安装redis要注意哪些事项
2024-03-11

redis导入数据要注意哪些事项

确保数据格式正确:导入数据前,需要确认数据的格式和结构是否符合Redis支持的数据类型。例如,如果要导入的数据是字符串类型,需要保证每条数据都是以key-value形式存储。数据清洗和转换:在导入数据之前,可能需要对数据进行清洗和转换,以确
redis导入数据要注意哪些事项
2024-03-11

mysql悲观锁使用要注意哪些事项

使用MySQL悲观锁时需要注意以下事项:需要确保事务的隔离级别设置为Serializable,以确保事务之间的并发访问不会造成数据的不一致性。在使用SELECT … FOR UPDATE语句时,要注意锁定的范围,避免锁定过大的范围导致性能下
mysql悲观锁使用要注意哪些事项
2024-05-08

网页布局需要注意哪些事项

这篇文章主要介绍“网页布局需要注意哪些事项”,在日常操作中,相信很多人在网页布局需要注意哪些事项问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”网页布局需要注意哪些事项”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-10

Redis实现分布式锁的方法有哪些

今天小编给大家分享一下Redis实现分布式锁的方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1. 单机数据一致性单
2023-07-02

Java分页查询要注意哪些事项

在Java分页查询时,需要注意以下事项:1. 设置每页显示的数据量:在进行分页查询时,需要设置每页显示的数据量,以控制查询结果的数量。通常会使用一个变量来表示每页显示的数据量,然后在查询语句中使用该变量进行限制。2. 设置当前页码:在分页查
2023-09-15

mysql分割数据要注意哪些事项

在进行MySQL数据分割时,一些需要注意的事项包括:确定分割的准则:首先需要明确分割数据的准则,例如按时间、地域、业务等因素进行分割。数据一致性:在进行数据分割时,需要确保数据的一致性,避免出现数据丢失或不完整的情况。数据迁移方式:选择合适
mysql分割数据要注意哪些事项
2024-04-09

docker中redis安装配置要注意哪些事项

在安装和配置Redis时,需要注意以下事项:确保使用官方提供的Redis镜像来安装Redis,避免使用不安全或不稳定的第三方镜像。在Docker容器中运行Redis时,应该映射Redis配置文件和数据目录到宿主机上,以便数据持久化和配置备份
docker中redis安装配置要注意哪些事项
2024-04-16

oracle拆分字符串要注意哪些事项

在Oracle中拆分字符串时,需要注意以下几个事项:使用适当的字符串分隔符:在拆分字符串时,需要明确使用何种分隔符来分割字符串,确保选择的分隔符不会与字符串中的其他字符混淆。考虑字符串中可能存在多个分隔符的情况:有些情况下,字符串中可能存在
oracle拆分字符串要注意哪些事项
2024-04-09

oracle创建分区表要注意哪些事项

在创建分区表时,需要注意以下事项:分区类型:选择适合数据的分区类型,可以根据时间、范围、列表、哈希等不同的分区类型进行选择。分区键:选择合适的分区键,分区键是用于划分数据的依据。根据业务需求选择合适的分区键,可以是日期、地域、状态等。分区策
oracle创建分区表要注意哪些事项
2024-04-09

mysql拆分字符串要注意哪些事项

在 MySQL 中拆分字符串时,需要注意以下事项:使用内置函数:MySQL 提供了一些内置函数来处理字符串,如 SUBSTRING、LEFT、RIGHT、CONCAT、REPLACE 等,可以使用这些函数来实现字符串拆分操作。使用正则表达式
mysql拆分字符串要注意哪些事项
2024-04-09

Redis序列化数据存储要注意哪些事项

在进行Redis数据序列化存储时,需要注意以下事项:序列化格式:选择合适的序列化格式,如JSON、MessagePack、Protobuf等,根据数据的复杂程度和数据大小选择合适的序列化格式。序列化性能:考虑序列化和反序列化的性能开销,选择
Redis序列化数据存储要注意哪些事项
2024-04-29

编程热搜

目录