深入Redis(一)分布式锁

分布式锁

由于分布式应用在逻辑处理时存在并发问题,比方修改数据,要先读取到内存,在内存中修改后再保存回去,这两个操作是单独的,如果同时进行,就会出现并发问题。

此时就要用到分布式锁来限制程序的并发执行。

本质

本质就是在Redis内占一个位置,若别的进程也想占用该位置,发现有进程在使用该位置,就放弃或等待。

  1. 在Redis中实现依靠setnx(set if not exists)指令,用完了再调用del指令来释放位置。
  2. 在1中,如果逻辑执行到中间出现异常,可能导致del未调用,这就陷入死锁,锁永远得不到释放,因此可以给这个位置增加一个过期时间,这样即使出现异常也能保证位置会被释放。
  3. 在2中,如果在setnxexpire中间出现问题导致进程挂掉,则expire不会执行,同样造成死锁。因此出现了分布式锁的library,但Redis作者从2.8版本开始加入了set指令的拓展参数,因此可以通过set key value ex seconds nx指令来合并nx和expire为原子操作,这就是奥义。

超时问题

如果加解锁之间的逻辑执行时间超出过期时间,则会导致这个锁被其它进程获取,而其它进程执行逻辑时,若第一个逻辑执行完,它将调用解锁操作,则会导致第二个程序还没运行完锁就被释放。

为了解决后一个问题,引入tag标签来标记锁,设置一个随机数作为锁的value,在释放锁时要匹配该随机数,但Redis没有提供匹配的方法,因此需要Lua脚本来处理。(Lua脚本可以保证连续多个指令的原子性执行)

以上并没有完美解决超时问题,只是相对安全一点。

可重入性

可重入性指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,则这个锁就是可重入的。这是为了解决超时问题,若超时判断逻辑是否执行完,未完则再加锁,直到由代码调用解锁。

Redis分布式锁要支持可重入,则需对客户端set方法进行包装,用线程的Treadlocal变量存储当前持有锁的计数。

import redis
import threading

locks = threading.local()
locks.redis = {}

def key_for(user_id):
    return "account_{}".format(user_id)

def _lock(client, key):
    return bool(client.set(key, True, nx=True, ex=5))

def _unlock(client, key):
    client.delete(key)

def lock(client, user_id):
    key = key_for(user_id)
    if key in locks.redis:
        locks.redis[key] += 1
        return True
    ok = _lock(client, key)
    if not ok:
        return False
    locks.redis[key] = 1
    return True

def unlock(client, user_id):
    key = key_for(user_id)
    if key in locks.redis:
        locks.redis[key] -= 1
        if locks.redis[key] <= 0:
            _unlock(client, key)
        return True
    return False

client = redis.StrictRedis()

不推荐使用可重入锁,其加重了客户端的复杂性,调整逻辑结构完全可以不使用可重入锁。

原文地址:https://www.cnblogs.com/ikct2017/p/9498534.html

时间: 2024-08-05 00:32:27

深入Redis(一)分布式锁的相关文章

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

基于Redis实现分布式锁(转载)

原文地址:http://blog.csdn.net/ugg/article/details/41894947 Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令(SET if Not eXists)语法:SETNX key value功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1:若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0. GETSET命令语法:GETSET key value功能:将给定 key 的值设为

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

Redis实现分布式锁

http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性. 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock,我们相信这个算法会比一般的普通方法更加安全可靠.我们也希望社区能一起分析这个算法,

基于redis的分布式锁

<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * https://gist.github.com/nickyleach/3694555 */ pc_base::load_sys_class('cache_redis', '', 0); class dist_key_redis { //锁的超时时间 const TIMEOUT = 20; const SL

转载:基于Redis实现分布式锁

转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分

基于Redis实现分布式锁

http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决

用redis实现分布式锁

分布式部署中不可避免用到分布式锁,目前比较常用的实现方式一般有基于数据库的乐观锁.基于redis的分布式锁和基于zookeeper的分布式锁.本文只说redis的实现方式,使用jedis作为连接器. 比较简单,直接上代码吧. public class PaasLock { private static final String KEY_NNXX = "NX"; private static final String KEY_EXPX = "PX"; private

Redis实现分布式锁原理与实现分析

一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单问题. 在接收消息的时候,消息推送重复.如果处理消息的接口无法保证幂等,那么重复消费消息产生的影响可能会非常大. 类似这种场景,我们有很多种方法,可以使用幂等操作,也