Memcached 和 Redis 分布式锁方案

分布式缓存,能解决单台服务器内存不能无限扩张的瓶颈。在分布式缓存的应用中,会遇到多个客户端同时争用的问题。这个时候,需要用到分布式锁,得到锁的客户端才有操作权限。

Memcached 和 Redis 是常用的分布式缓存构建方案,下面列举下基于Memcached 和 Redis 分布式锁的实现方法。

Memcached 分布式锁

Memcached 可以使用 add 命令,该命令只有KEY不存在时,才进行添加,或者不会处理。Memcached 所有命令都是原子性的,并发下add 同一个KEY ,只会一个会成功。

利用这个原理,可以先定义一个 锁 LockKEY ,add 成功的认为是得到锁。并且设置[过期超时] 时间,保证宕机后,也不会死锁

在具体操作完后,判断是否此次操作已超时。如果超时则不删除锁,如果不超时则删除锁。

伪代码:

 1          if (mc.Add("LockKey", "Value", expiredtime))
 2             {
 3                 //得到锁
 4                 try
 5                 {
 6                     //do business  function
 7
 8                     //检查超时
 9                     if (!CheckedTimeOut())
10                     {
11                         mc.Delete("LockKey");
12                     }
13                 }
14                 catch (Exception e)
15                 {
16                     mc.Delete("LockKey");
17                 }
18
19             }

Redis 分布式锁

Redis  没有add 命令,但有SETNX(SET if Not eXists)若给定的 key 已经存在,则 SETNX不做任何动作。设置成功,返回 1 。设置失败,返回 0

SETNX 命令不能设置过期时间,需要再使用 EXPIRE 命令设置过期时间。

伪代码:

            int lockResult = rd.SETNX("LockKey", "Value");
            if (lockResult == 1)
            {
                //[1]得到锁

                //[2]设置超时过期时间
                rd.EXPIRE("LockKey", expiredtime);

                try
                {
                    //do business  function

                    //检查超时
                    if (!CheckedTimeOut())
                    {
                        rd.DEL("LockKey");
                    }
                }
                catch (Exception e)
                {
                    rd.DEL("LockKey");
                }

            }

   这种做法,有一个很大的潜在风险。[1]得到锁后,再执行[2] 设置过期时间。如果在这期间出现宕机,则会导致没有设置过期时间。按Redis 的默认缓存过期策略,这个锁将不会释放,产生死锁。

所以不推荐用这种做法,应该用其它方式来实现锁的超时过期策略:

1:SETNX  value 值=当前时间+过期超时时间,返回1 则获得锁,返回0则没有获得锁。转2。

2:GET 获取 value 的值 。判断锁是否过期超时。如果超时,转3。

3:GETSET(将给定 key 的值设为 value ,并返回 key 的旧值),GETSET  value 值=当前时间+过期超时时间, 判断得到的value 如果仍然是超时的,那就说明得到锁,否则没有得到锁。

从2并发进到3 的操作,会多次改写超时时间,但这个不会有什么影响。

伪代码:

            string expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
            int lockResult = rd.SETNX("LockKey", expiredtime);
            bool getLock = false;
            if (lockResult == 1)
            {
                //得到锁
                getLock = true;
            }
            else
            {
                string curExpiredtime = rd.GET("LockKey");

                //检查锁超时
                if (CheckedLockTimeOut(expiredtime))
                {
                    expiredtime = DateTime.Now.AddMinutes(LockTimeoutMinutes).ToString();
                    string newExpiredTime = GETSET(expiredtime);
                    if (CheckedLockTimeOut(newExpiredTime))
                    {
                        //得到锁
                        getLock = true;
                    }
                }
            }
            if (getLock)
            {
                try
                {
                    //do business  function

                    //检查超时
                    if (!CheckedTimeOut())
                    {
                        rd.DEL("LockKey");
                    }
                }
                catch (Exception e)
                {
                    rd.DEL("LockKey");
                }
            }

个人觉得这种做法,还是不完美。 

ZooKeeper的分布式锁,下篇再学习探讨。

参考:

郑昀, 电商课题V:分布式锁

jeffkit, 用Redis实现分布式锁

时间: 2024-08-01 16:43:55

Memcached 和 Redis 分布式锁方案的相关文章

Redis分布式锁的实现原理

一.写在前面 现在面试,一般都会聊聊分布式系统这块的东西.通常面试官都会从服务框架(Spring Cloud.Dubbo)聊起,一路聊到分布式事务.分布式锁.ZooKeeper等知识. 所以咱们这篇文章就来聊聊分布式锁这块知识,具体的来看看Redis分布式锁的实现原理. 说实话,如果在公司里落地生产环境用分布式锁的时候,一定是会用开源类库的,比如Redis分布式锁,一般就是用Redisson框架就好了,非常的简便易用. 大家如果有兴趣,可以去看看Redisson的官网,看看如何在项目中引入Red

【分布式缓存系列】集群环境下Redis分布式锁的正确姿势

一.前言 在上一篇文章中,已经介绍了基于Redis实现分布式锁的正确姿势,但是上篇文章存在一定的缺陷——它加锁只作用在一个Redis节点上,如果通过sentinel保证高可用,如果master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况: 客户端1在Redis的master节点上拿到了锁 Master宕机了,存储锁的key还没有来得及同步到Slave上 master故障,发生故障转移,slave节点升级为master节点 客户端2从新的Master获取到了对应同一个资源的锁 于是,客

对比各类分布式锁缺陷,抓住Redis分布式锁实现命门

近两年来微服务变得越来越热门,越来越多的应用部署在分布式环境中,在分布式环境中,数据一致性是一直以来需要关注并且去解决的问题,分布式锁也就成为了一种广泛使用的技术. 常用的分布式实现方式为Redis,Zookeeper,其中基于Redis的分布式锁的使用更加广泛. 但是在工作和网络上看到过各个版本的Redis分布式锁实现,每种实现都有一些不严谨的地方,甚至有可能是错误的实现,包括在代码中,如果不能正确的使用分布式锁,可能造成严重的生产环境故障. 本文主要对目前遇到的各种分布式锁以及其缺陷做了一个

死磕 java同步系列之redis分布式锁进化史

问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API. 本章我们将介绍如何基于redis实现分布式锁,并把其实现的进化史从头到尾讲明白,以便大家在面试的时候能讲清楚

PHP实现Redis分布式锁

 锁在我们的日常开发可谓用得比较多.通常用来解决资源并发的问题.特别是多机集群情况下,资源争抢的问题.但是,很多新手在锁的处理上常常会犯一些问题.今天我们来深入理解锁. 一.Redis 锁错误使用之一我曾经见过有的项目把查询结果存储到 Redis 当中时的伪代码如下: $redis    = new \Redis('127.0.0.1', 6379); $cacheKey = 'query_cache'; $result   = $redis->get($cacheKey); if ($resu

redis分布式锁深度剖析

转自https://www.jianshu.com/p/d0caf5d99e56 redis分布式锁的实现主要是基于redis的setnx 命令(setnx命令解释见:http://doc.redisfans.com/string/setnx.html),我们来看一下setnx命令的作用: redis-setnx.png 1.redis分布式锁的基本实现 redis加锁命令: SETNX resource_name my_random_value PX 30000 这个命令的作用是在只有这个ke

redis分布式锁深度剖析(超时情况)

1.redis分布式锁的基本实现 redis加锁命令: SETNX resource_name my_random_value PX 30000 这个命令的作用是在只有这个key不存在的时候才会设置这个key的值(NX选项的作用),超时时间设为30000毫秒(PX选项的作用) 这个key的值设为“my_random_value”.这个值必须在所有获取锁请求的客户端里保持唯一. SETNX 值保持唯一的是为了确保安全的释放锁,避免误删其他客户端得到的锁.举个例子,一个客户端拿到了锁,被某个操作阻塞

Redis分布式锁的实现

来自 https://www.cnblogs.com/Eugene-Jin/p/10801260.html  1.概述 分布式锁一般有三种实现方式:1.基于数据库实现分布式锁:2. 基于缓存(Redis,memcached,tair)实现分布式锁: 3.基于Zookeeper实现分布式锁.本片博客将介绍基于缓存实现分布式锁,这里主讲分别针对Redis 2.6.12 之前版本和Redis 2.6.12 及之后版本分布式锁的实现. 2.基于Redis(Redis 2.6.12版本之前)分布式锁的实现

收藏慢慢看系列:简洁实用的Redis分布式锁用法

在微服务中很多情况下需要使用到分布式锁功能,而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2.x以上版本中使用Redis时,其客户端库已经默认使用lettuce.所以本文将直接介绍在Spring Boot2.x以上项目中快速使用Redis分布式锁的功能的方法,希望能够更新你的知识库! Redis分布式锁原理概述 实际上Redis服务本身并不提供分布式锁这样的机制,但是作为全局Key-Valu