文档章节

Redis实现锁

秋风醉了
 秋风醉了
发布于 2016/04/14 16:17
字数 886
阅读 149
收藏 2
点赞 1
评论 0

Redis实现锁

虽然很多的Redis用户都对锁(lock),加锁(locking)以及锁超时(lock timeouts)有所了解,但大部分使用Redis实现的锁只是基本上正确,它们发生故障的时间和方式通常都难以预料。下面列出了一些导致锁出现不正确行为的原因,以及锁在不正确运行时的症状:

  • 持有锁的进程因为操作时间过长而导致锁被自动释放,但进程本身并不知道,甚至还可能会错误的释放掉了其他进程持有的锁。

  • 一个持有锁并打算执行长时间操作的进程已经崩溃,但其他想要获取锁的进程不知道哪个进程持有着锁,只能白白浪费时间等待锁被释放

  • 在一个进程持有的锁过期之后,其他多个进程同时尝试去获取锁,并且都获得了锁。

  • 上面提到的第一个种情况和第三种情况同时出现,导致有多个进程获得了锁,而每个进程都以为自己是唯一一个获得锁的进程。

以上几种情况是redis锁使用过程中有可能出现的问题,在redis锁的使用过程中应该要注意。


Redis实现基本的加锁和锁的释放

为了对数据进行排他性访问,程序首先要做的就是获取锁。SETNX命令天生就适合用来实现锁的获取功能,这个命令只在键不存在的情况下为键设置值,而锁要做的就是将一个随机生成的128位UUID设置为键的值,并使用这个值来防止锁被其他进程取得。

如果程序在尝试获取锁的时候失败,那么它将不断的进行重试,知道成功的取得锁获取超时,代码如下,

/**
 * 获取锁
 *
 * @param conn
 * @param lockName
 * @param acquireTimeout 获取锁直到超时
 * @return
 */
public String acquireLock(Jedis conn, String lockName, long acquireTimeout) {
    String identifier = UUID.randomUUID().toString();

    long end = System.currentTimeMillis() + acquireTimeout;
    //获取锁的过程超时后,直接返回null
    while (System.currentTimeMillis() < end) {
        if (conn.setnx("lock:" + lockName, identifier) == 1) { //key-value设置成功
            return identifier; //返回锁的标识符
        }

        //如果没有设置成功,即获取锁失败,sleep一毫秒继续重试,直到获取锁的过程超时

        try {
            Thread.sleep(1);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    return null;
}

以上代码实现了加锁过程,那么如何释放锁,

/**
 * 释放锁
 *
 * @param conn
 * @param lockName
 * @param identifier
 * @return
 */
public boolean releaseLock(Jedis conn, String lockName, String identifier) {
    String lockKey = "lock:" + lockName;

    while (true) {
        conn.watch(lockKey);
        //判断锁的标识,如果相等,说明标识符对应的线程正在持有该锁,可以释放
        if (identifier.equals(conn.get(lockKey))) {
            Transaction trans = conn.multi();
            trans.del(lockKey);
            List<Object> results = trans.exec();
            if (results == null) {
                continue;
            }
            return true;
        }

        //不再监视lockKey
        conn.unwatch();
        break;
    }

    //释放锁失败
    return false;
}


带有超时限制的锁

如下代码所示,

/**
 * 获取锁,锁有超时特性
 *
 * @param conn
 * @param lockName
 * @param acquireTimeout
 * @param lockTimeout
 * @return
 */
public String acquireLockWithTimeout(
        Jedis conn, String lockName, long acquireTimeout, long lockTimeout) {
    String identifier = UUID.randomUUID().toString();
    String lockKey = "lock:" + lockName;
    int lockExpire = (int) (lockTimeout / 1000);

    long end = System.currentTimeMillis() + acquireTimeout;

    //循环获取锁,直到获取成功或超时
    while (System.currentTimeMillis() < end) {
        if (conn.setnx(lockKey, identifier) == 1) { //设置成功
            //设置成功后设置锁的过期时间
            conn.expire(lockKey, lockExpire);
            return identifier;
        }

        //当获取锁失败后,检查过期时间
        //如果返回-1 表示该锁没有设置过期
        if (conn.ttl(lockKey) == -1) {
            //设置一个过期时间
            conn.expire(lockKey, lockExpire);
        }

        try {
            Thread.sleep(1);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    // null indicates that the lock was not acquired
    return null;
}

======END======

© 著作权归作者所有

共有 人打赏支持
秋风醉了
粉丝 223
博文 581
码字总数 411013
作品 0
东城
程序员
基于Redis实现简单的分布式锁

摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问。分...

Float_Luuu ⋅ 2016/05/18 ⋅ 0

Spring-data-redis + redis 分布式锁(二)

分布式锁的解决方式 基于数据库表做乐观锁,用于分布式锁。(适用于小并发) 使用memcached的add()方法,用于分布式锁。 使用memcached的cas()方法,用于分布式锁。(不常用) 使用redis的setnx...

xiaolyuh ⋅ 2017/11/16 ⋅ 0

基于 Redis 实现简单的分布式锁

本文作者:伯乐在线 -Float_Lu 。未经作者许可,禁止转载! 欢迎加入伯乐在线专栏作者。 摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞...

伯乐在线 ⋅ 2016/05/21 ⋅ 0

如何在springcloud分布式系统中实现分布式锁?

转载请标明出处: http://blog.csdn.net/forezp/article/details/68957681 本文出自方志朋的博客 最近在看分布式锁的资料,看了 Josial L的《Redis in Action》的分布式锁的章节。实现思路是...

forezp ⋅ 2017/04/03 ⋅ 0

每日一博 | 基于 redis 的 spring boot 分布式锁实现

每日一博 | 基于 redis 的 spring boot 分布式锁实现 Harries Blog™2018-01-030 阅读 ACESpringAppbeanAPIbuildAOPActionbug 随着现在 分布式 架构越来越盛行,在很多场景下需要使用到 分布...

Harries Blog™ ⋅ 01/03 ⋅ 0

Redis与Zookeeper实现分布式锁的区别

简介 一般而言,大多数系统实现分布式锁服务都会优先使用Redis;但阅读Zookeeper时可知,Zookeeper的一个很重要应用方向就是分布式锁。那么两者实现分布式锁服务的区别是什么呢。 实现难度 ...

沈渊 ⋅ 2017/10/15 ⋅ 0

Redis分布式锁 基于GETSET SETNX REDISSON 的实现

Redis分布式锁的应用 有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多...

Lien6o ⋅ 2017/09/21 ⋅ 0

分布式锁实现汇总

[TOC] 分布式锁实现汇总 很多时候我们需要保证同一时间一个方法只能被同一个线程调用,在单机环境中,Java中其实提供了很多并发处理相关的API,但是这些API在分布式场景中就无能为力了。也就...

Wang_Coder ⋅ 2017/12/01 ⋅ 0

使用Redis实现分布式锁及其优化

使用Redis实现分布式锁及其优化 Mz的博客2017-11-0128 阅读 RedisJava分布式 目前实现分布式锁的方式主要有数据库、Redis和Zookeeper三种,本文主要阐述利用Redis的相关命令来实现分布式锁。...

Mz的博客 ⋅ 2017/11/01 ⋅ 0

关于redis分布式锁的初步研究与分享

导言: 前段时间项目中用到了分布式锁,所以就对分布式锁进行了一些研究,首先当然是去看redis的分布式锁实现,这里说明一下,分布式锁的实现可以有两种比较简单的方式来实现,一种是redis的...

zhangkay ⋅ 2017/12/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

20.zip压缩 tar打包 打包并压缩

6月25日任务 6.5 zip压缩工具 6.6 tar打包 6.7 打包并压缩 6.5 zip压缩工具: zip支持压缩目录 zip压缩完之后原来的文件不删除 不同的文件内容其实压缩的效果不一样 文件内有很多重复的用xz压...

王鑫linux ⋅ 1分钟前 ⋅ 0

double类型数据保留四位小数的另一种思路

来源:透析公式处理,有时候数据有很长的小数位,有的时候由在四位以内,如果用一般的处理方法,那么不足四位的小树会补充0到第四位,这样子有点画蛇添足的感觉,不太好看。所以要根据小数的...

young_chen ⋅ 7分钟前 ⋅ 0

Python 优化 回溯下降算法

使用sympy构造表达式,实现回溯下降算法 完整代码 from matplotlib import pyplot as pltimport numpy as npfrom mpl_toolkits.mplot3d import Axes3Dfrom sympy import *import mat......

阿豪boy ⋅ 12分钟前 ⋅ 0

Django配置163邮箱出现 authentication failed(535)错误解决方法

最近用Django写某网站,当配置163邮箱设置完成后,出现535错误即:smtplib.SMTPAuthenticationError: (535, b'Error: authentication failed') Django初始配置邮箱设置 EMAIL_HOST = "smtp.1...

陈墨轩_CJX ⋅ 13分钟前 ⋅ 0

用接口模拟可伸缩枚举(34)

1、枚举的可伸缩性最后证明都不是什么好点子 扩展类型的元素是基本类型实例,基本类型的实例却不是扩展类型的元素,很混乱 目前还没有很好的方法来枚举基本类型的所有元素,及其扩展 可伸缩性...

职业搬砖20年 ⋅ 17分钟前 ⋅ 0

Ubuntu18.04 IDEA快捷键无法使用

IDEA默认的回退到上一视图的快捷键是Ctrl + Alt + Left,在ubuntu中这个快捷键被占用了,在16.04中可以在界面中取消这个快捷键,但是18.04就看不到了,可以使用以下命令解决 gsettings set ...

Iceberg_XTY ⋅ 21分钟前 ⋅ 0

如何解决s权限位引发postfix及crontab异常

一、问题现象 业务反馈某台应用服务器,普通用户使用mutt程序发送邮件时,提示“postdrop warning: mail_queue_enter: create file maildrop/713410.6065: Permission denied”,而且普通用法...

问题终结者 ⋅ 34分钟前 ⋅ 0

Unable to load database on disk

由于磁盘空间满了以后,导致zookeeper异常退出,清理磁盘空间后,zk启动报错,信息如下: 2018-06-25 17:18:46,904 INFO org.apache.zookeeper.server.quorum.QuorumPeerConfig: Reading co...

刀锋 ⋅ 53分钟前 ⋅ 0

css3 box-sizing:border-box 实现div一行多列

<!DOCTYPE html><html><head><style> div.container{ background:green; padding:10px 10px;}div.box{box-sizing:border-box;-moz-box-sizing:border-box; /* Fir......

qimh ⋅ 58分钟前 ⋅ 0

Homebrew简介和基本使用

一、Homebrew是什么 Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径...

说回答 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部