Redis分布式锁如何创建?

2020-12-22 11:07发布

Redis分布式锁如何创建?他的原理是什么?redis的指令有那些?

Redis分布式锁如何创建?他的原理是什么?redis的指令有那些?

4条回答
云鹰的指尖故事
2楼 · 2020-12-22 19:52

(1)、我们要知道问什么要使用分布式锁? 

在分布式系统中,出现多线程并发访问临界资源的时候,会出现并发问题,比如: 我们在分布式系统实现商品的秒杀,在这种情况下如果不做特殊的处理, 秒杀的商品就是临界资源,很多的线程同时访问秒杀商品,就会出现并发访问的问题,说白了就是明明秒杀的商品有100个,但是却被客户秒到了超过100个的商品。

(2)、我们如果解决这个问题呢?我们可以添加分布式锁来解决这个问题,

好,接下来我来介绍下分布式锁的原理: 具体包括加锁、解锁、锁超时处理

加锁

在沙滩上踩一脚,留下自己的脚印,就对应了加锁操作。其他进程或者线程,看到沙滩上已经有脚印,证明锁已被别人持有,则等待。

解锁

把脚印从沙滩上抹去,就是解锁的过程。

锁超时

为了避免死锁,我们可以设置一阵风,在单位时间后刮起,将脚印自动抹去。

(3)、分布式锁的实现有很多,比如基于数据库、memcachedRedis、系统文件、zookeeper等。它们的核心的理念跟上面的过程大致相同。

在这里我们通过单节点Redis实现一个简单的分布式锁。

a、加锁

加锁实际上就是在redis中,给Key键设置一个值,如果设置值成功,则表示客户端获得了锁。

b、解锁

解锁的过程就是将Key键删除

下面给大家提供一个基于redis的分布式锁类库:

Redis分布式锁实现原理:

利用redis在同一时刻操作一个键的值只能有一个进程的特性,如果能设值成功就获取到锁。解锁,就是删除指定的键。为防止死锁可以设置锁超时时间,如果锁超时就释放锁。

具体代码如下:

image.png

image.png

好了,如上就是redis分布式锁的创建,原理,以及相关指令,答案仅供参考!




加锁实际上就是,在redis中使用SET key value [EX seconds] [PX milliseconds] NX命令给Key键设置一个值,为了避免死锁,还要给定一个过期时间。如执行以下命令:

其中:
lock_key为resource_name。
random_value 为key的值(一个随机值),这个值在所有的客户端必须是唯一的,所有同一key的获取者(竞争者)这个值都不能一样。可以用snowflake算法生成分布式唯一id
NX表示只在key不存在时,才对key进行设置操作; PX 5000表示设置key的过期时间为5000毫秒。 即这个命令仅在不存在key的时候才能被执行成功(NX选项),并且这个key有一个5秒的自动失效时间(PX属性)



py大白
4楼 · 2020-12-25 12:38

在分布式系统中,出现多线程并发访问临界资源的时候,会出现并发问题,比如: 我们在分布式系统实现商品的秒杀,在这种情况下如果不做特殊的处理, 秒杀的商品就是临界资源,很多的线程同时访问秒杀商品,就会出现并发访问的问题,说白了就是明明秒杀的商品有100个,但是却被客户秒到了超过100个的商品。

(2)、我们如果解决这个问题呢?我们可以添加分布式锁来解决这个问题,

好,接下来我来介绍下分布式锁的原理: 具体包括加锁、解锁、锁超时处理

加锁

在沙滩上踩一脚,留下自己的脚印,就对应了加锁操作。其他进程或者线程,看到沙滩上已经有脚印,证明锁已被别人持有,则等待。

解锁

把脚印从沙滩上抹去,就是解锁的过程。

锁超时

为了避免死锁,我们可以设置一阵风,在单位时间后刮起,将脚印自动抹去。

(3)、分布式锁的实现有很多,比如基于数据库、memcached、Redis、系统文件、zookeeper等。它们的核心的理念跟上面的过程大致相同。

在这里我们通过单节点Redis实现一个简单的分布式锁。

a、加锁

加锁实际上就是在redis中,给Key键设置一个值,如果设置值成功,则表示客户端获得了锁。

b、解锁

解锁的过程就是将Key键删除

下面给大家提供一个基于redis的分布式锁类库:

Redis分布式锁实现原理:

利用redis在同一时刻操作一个键的值只能有一个进程的特性,如果能设值成功就获取到锁。解锁,就是删除指定的键。为防止死锁可以设置锁超时时间,如果锁超时就释放锁。


某知名啥也不是博主
5楼 · 2021-05-12 13:53

分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。

基于Redis的分布式锁


可靠性

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。

  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。

  4. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

代码实现

组件依赖

首先我们要通过Maven引入Jedis开源组件,在pom.xml文件加入下面的代码:


    redis.clients
    jedis
    2.9.0

 

加锁代码

Talk is cheap, show me the code。先展示代码,再带大家慢慢解释为什么这样实现:

public class RedisTool {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

可以看到,我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:

  • 第一个为key,我们使用key来当锁,因为key是唯一的。

  • 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。

  • 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;

  • 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。

  • 第五个为time,与第四个参数相呼应,代表key的过期时间。

总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。

心细的童鞋就会发现了,我们的加锁代码满足我们可靠性里描述的三个条件。首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,代表加锁的客户端请求标识,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。由于我们只考虑Redis单机部署的场景,所以容错性我们暂不考虑


解锁代码

还是先展示代码,再带大家慢慢解释为什么这样实现:

public class RedisTool {

    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String 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(lockKey), Collections.singletonList(requestId));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

    }

}

可以看到,我们解锁只需要两行代码就搞定了!第一行代码,我们写了一个简单的Lua脚本代码,上一次见到这个编程语言还是在《黑客与画家》里,没想到这次居然用上了。第二行代码,我们将Lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行。

那么这段Lua代码的功能是什么呢?其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述操作是原子性的 。那么为什么执行eval()方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:

简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。


相关问题推荐

  • 回答 27

    DDL      create table 创建表    alter table  修改表   drop table 删除表   truncate table 删除表中所有行    create index 创建索引   drop index  删除索引 当执行DDL语句时,在每一条语句前后,oracle都将提交当前的事...

  • 回答 23

    java开发应用数据库比较主流的有下面这三种:    1.MySQL   MySQL是最受欢迎的开源SQL数据库管理系统,它由MySQL AB开发、发布和支持。MySQL AB是一家基于MySQL开发人员的商业公司,它是一家使用了一种成功的商业模式来结合开源价值和方法论的第二代开...

  • 回答 23

    1,DML(DataManipulationLanguage):数据操作语言,用来定义数据库记录(数据)2,DCL(DataControlLanguage):数据控制语言,用来定义访问权限和安全级别;3,DQL(DataQueryLanguage):数据查询语言,用来查询记录(数据);4,DDL(DataDefinitionLang...

  • 回答 11

    数据库三级模式:1、外模式,外模式又称子模式或用户模式,对应于用户级,外模式是从模式导出的一个子集,包含模式中允许特定用户使用的那部分数据。用户可以通过外模式描述语言来描述、定义对应于用户的数据记录(外模式),也可以利用数据操纵语言(Data Manip...

  • 回答 11

    模式、外模式、内莫斯,亦称逻辑模式,是数据库中全体数据的逻辑结构和特征的描述,是所有用户的公共数据视图。模式描述的是数据的全局逻辑结构。 外模式涉及的是数据的局部逻辑结构,通常是模式的子集 内模式,亦称存储模式,是数据库在数据系统内部的表示...

  • 回答 16

    为了避免上面出现的几种情况,在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。未授权读取(Read Uncommitted):允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个数据则不允许同时进行写操作,但允许其他事...

  • 回答 5

    from是个关键词,表示要从哪个表查询。。

  • 回答 12

    数据库池连接数量一直保持一个不少于最小连接数的数量,当数量不够时,数据库会创建一些连接,直到一个最大连接数,之后连接数据库就会等待。

  • 回答 7

    仅用慢日志文件,如何快速获取分时报告?如果有监控系统,获取分时报告(每小时慢查询的条数报告)不难,如果只有慢日志文件,就会有点费劲。实验:通过 pt-query-digest --timeline 功能,可以输出带时间戳的慢查询条目用 sed 将 timeline 报告滤出安装 term...

  • 回答 9
    已采纳

    MySql优化的一般步骤:1.通过show status 命令了解各种sql的执行效率  SHOW STATUS提供msyql服务器的状态信息  一般情况下,我们只需要了解以Com开头的指令  show session status like ‘Com%’:显示当前的连接的统计结果  show global status like ...

  • 回答 4

    有可能是你输入的密码有问题,如果你的密码字母在前数字在后,那么你输入前面的字母后敲回车再输入数字就可以了,可以试一下

  • 回答 5

    工程目录sql语句CREATE TABLE `user` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `username` varchar(32) NOT NULL COMMENT '用户名称',  `birthday` date DEFAULT NULL COMMENT '生日',  `sex` char(1) DEFAUL...

  • 回答 6

    事实上MySQL 能承受的数据量的多少主要和数据表的结构有关,并不是一个固定的数值。表的结构简单,则能承受的数据量相对比结构复杂时大些。据D.V.B 团队以及Cmshelp 团队做CMS 系统评测时的结果来看,MySQL单表大约在2千万条记录(4G)下能够良好运行,经过数...

  • 回答 5

    事实上MySQL 能承受的数据量的多少主要和数据表的结构有关,并不是一个固定的数值。表的结构简单,则能承受的数据量相对比结构复杂时大些。据D.V.B 团队以及Cmshelp 团队做CMS 系统评测时的结果来看,MySQL单表大约在2千万条记录(4G)下能够良好运行,经过数...

  • 回答 6

    mysql哪个版本比较稳定MySQL的选择要取决于用途的,mysql5.5或者5.7 的版本,网上资源较多mysql的版本如下:1. MySQL Community Server 社区版本,开源免费,但不提供官方技术支持。2. MySQL Enterprise Edition 企业版本,需付费,可以试用30天。3. MySQL C...

  • 回答 4

    事实上MySQL 能承受的数据量的多少主要和数据表的结构有关,并不是一个固定的数值。表的结构简单,则能承受的数据量相对比结构复杂时大些。据D.V.B 团队以及Cmshelp 团队做CMS 系统评测时的结果来看,MySQL单表大约在2千万条记录(4G)下能够良好运行,经过数...

没有解决我的问题,去提问