分布式锁之Redis实现

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 一.分布式锁简介    在分布式系统之前,系统中的锁还是单服务器上的锁,比如锁住一个进程中的多线程访问同一资源。如使用synchronized来实现。随着系统的发展,到后来分布式应用,有可能同一资源被多台服务器上的不同进程竞争,这种情况下,出现了今天讨论的分布式锁。

一.分布式锁简介

    在分布式系统之前,系统中的锁还是单服务器上的锁,比如锁住一个进程中的多线程访问同一资源。如使用synchronized来实现。随着系统的发展,到后来分布式应用,有可能同一资源被多台服务器上的不同进程竞争,这种情况下,出现了今天讨论的分布式锁

    目前主流的分布式锁实现方式主要有下面三种:

  • 基于数据库的索引和行锁,数据库乐观锁
  • 基于Redis的单线程原子操作:setNX
  • 基于Zookeeper的临时有序节点

    这里主要介绍Redis的实现。锁需要满足下面的条件:

  • 互斥性,只有一个客户端能占有锁,具有排他
  • 无死锁,即使发生有客户端无法解锁,也能保证后续其他客户端能加锁,应该有超时或者异常情况下,释放锁

二.Redis实现

1.使用setnx实现

    先上代码:

long now = System.currentTimeMillis();
// 使用 setNx 加锁,保证操作的原子性
boolean result = SUCCESS.equals(jedis.setnx(lockName,String.valueOf(now + expire*1000)));
// 下面的if主要是解决死锁问题
if(!result){
    String timestamp = jedis.get(lockName);
    // 如果设置过期时间失败的话,再通过value的时间戳来和当前时间戳比较,防止出现死锁
    if(timestamp!=null && Long.parseLong(timestamp)<now){
        // 通过 getSet 在发现锁过期未被释放的情况下,避免删除了在这个过程中有可能被其余的线程获取到了锁
        //锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间
        String oldValue = jedis.getSet(lockName,String.valueOf(now + expire*1000));
        if(oldValue!=null && oldValue.equals(timestamp)){
            result = true;
            jedis.expire(lockName,expire);
        }
    }
}
if(result){
    jedis.expire(lockName,expire);
}
return result;

    使用 setnx 实现加锁,其中key是锁,value是锁的过期时间。加了时间戳比较,防止出现死锁情况。
    细心的朋友可能也会发现还是存在下面的问题:

  • 时间戳同步问题。客户端自己生成过期时间,所以需要强制要求分布式下每个客户端的时间必须同步
  • 当锁过期的时候,如果多个客户端同时执行 getSet ,最终只有一个客户端可以加锁,但是这个客户端的锁的过期时间可能被其他客户端覆盖
  • 锁不具备拥有者标识,即任何客户端都可以解锁

    所以就有了第2种实现方式。

2.使用set "NX" "PX"实现

    实现如下:

public static final String SET_IF_NOT_EXIST = "NX";
public static final String SET_WITH_EXPIRE_TIME = "PX";
// "NX" 当key不存在时,我们进行set操作;若key已经存在,则不做任何操作
//"PX" 给key加一个过期时间
String result = jedis.set(lockName, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expire*1000);
if(LOCK_SUCCESS.equals(result)){
    return true;
}
return false;

3.解锁实现

    解锁的时候,主要思想就是在redis里面执行一段Lua脚本。

    为什么使用Lua脚本?

    保证操作的原子性。eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。另外,开源Redisson中的分布式锁就是Lua实现,我后面会有一篇文章分析Redisson分布式锁的实现。

// KEYS[1] lockName 锁名称 ARGV[1] requestId
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockName), Collections.singletonList(requestId));
if (SUCCESS.equals(result)) {
    return true;
}
return false;

三.其他Redis实现

    Redisson提供了一种可重入的分布式锁的实现--RedissonLock。

    使用示例:

RLock dlock = client.getLock(lockName);
boolean result = false;
try {
    // expire 等待时间 leaseTime 超时时间
    result = dlock.tryLock(expire, 20, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    if (dlock.isLocked()) {
        dlock.unlock();
    }
}
return result;

    相关的实现和示例代码放到了Github上,redis-usage.

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
NoSQL 算法 安全
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
Redlock 算法-主从redis分布式锁主节点宕机锁丢失的问题
152 0
|
1月前
|
NoSQL 关系型数据库 MySQL
分布式锁(redis/mysql)
分布式锁(redis/mysql)
55 1
|
23天前
|
NoSQL Java Redis
如何通俗易懂的理解Redis分布式锁
在多线程并发的情况下,我们如何保证一个代码块在同一时间只能由一个线程访问呢?
34 2
|
1月前
|
人工智能 监控 NoSQL
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
【万字长文 一文搞定】Redis:从新手村到大师殿堂的奥德赛之旅 9种实现分布式锁的全技术指南
80 4
|
NoSQL Java 关系型数据库
浅谈Redis实现分布式锁
浅谈Redis实现分布式锁
|
存储 canal 缓存
|
NoSQL PHP Redis
redis实现分布式锁
redis实现分布式锁
140 0
redis实现分布式锁
|
消息中间件 负载均衡 监控
【Redis】Redis实现分布式锁
分布式锁无论在开发中还是面试里都是老八股了,本篇文章整理江湖上常见的Redis分布式锁解决方案
240 0
|
NoSQL Redis 数据库
用redis实现分布式锁时容易踩的5个坑
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 近有不少小伙伴投入短视频赛道,也出现不少第三方数据商,为大家提供抖音爬虫数据。 小伙伴们有没有好奇过,这些数据是如何获取的,普通技术小白能否也拥有自己的抖音爬虫呢? 本文会全面解密抖音爬虫的幕后原理,不需要任何编程知识,还请耐心阅读。
用redis实现分布式锁时容易踩的5个坑
|
消息中间件 NoSQL Java
基于Redis实现分布式锁
基于Redis实现分布式锁
239 0
基于Redis实现分布式锁

热门文章

最新文章