首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >redis实现分布式锁

redis实现分布式锁

作者头像
周杰伦本人
发布于 2022-10-25 07:46:24
发布于 2022-10-25 07:46:24
42600
代码可运行
举报
文章被收录于专栏:同步文章同步文章
运行总次数:0
代码可运行

redis 分布式锁的实现

思路: 1)、先判断没有,2)、再给里面放值

1、代码第一阶段;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        public void hello(){
       
        获取和设置值必须是原子的
          String lock =  getFromRedis("lock");get("lock")
          if(lock == null){
              setRedisKey("lock","1");
              执行业务
              delRedisKey("lock")
              return ;
          }else{
             hello();自旋
          }
        }

问题:加锁的原子性不能保证 解决:使用redis的setnx 2、代码第二阶段 setnx->set if not exist:原子操作。判断带保存。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       public void hello(){
           1、获取到锁
           Integer lock = setnx("lock',"111"); 0代表没有保存数据,说明已经有人占了。1代表占可坑成功
           if(lock!=0){
               执行业务逻辑
               释放锁、删除锁
               del("lock")
           }else{
               等待重试
               hello();
           }
       }

问题:如果由于各种问题(未捕获的异常、断电等)导致锁没释放。其他人永远获取不到锁。 解决:加个过期时间。 3、代码第三阶段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
          public void hello(){
             超时和加锁必须原子
              Integer lock = setnx("lock',"111");
              if(lock!=null){
                  expire("lock",10s);
                  执行业务逻辑
                  释放锁
                  del("lock')
              }else{
                  hello();
              }
         
          }

问题:刚拿到锁,机器炸了,没来得及设置超时。 解决:加锁和加超时也必须是原子的。

4、代码第四阶段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       public void hello(){
           String result = setnxex("lock","111",10s);
           if(result=="ok"){
               加锁成功
               执行业务逻辑
               del("lock")
           }else{
               hello();
           }
       }

问题:如果业务逻辑超时,导致锁自动删除,业务执行完又删除一遍锁。至少多个人都获取到了锁。 解决:每个线程加锁设置不同的值 删锁时对比 保证删的是自己加的锁 5、代码第五阶段。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       public void hello(){
          String token = UUID;
          String result = setnxex("lock",token,10s);
          if(result == "ok"){
              执行业务
      
              删锁,保证删除自己的锁
              if(get("lock")==token){
                  del("lock")
              }
          }else{
              hello();
          }
       }

问题:我们获取锁的时候,锁的值正在给我们返回。锁过期。redis删除了锁。但是我们拿到了值,而且对比成功(此时此刻正好有人又获取)。我们还删除了锁。至少两个线程又进入同一个代码。 原因:删锁不是原子 解决: lua脚本进行删除。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        String script =
            "if redis.call('get', KEYS[1]) == ARGV[1] then
                    return redis.call('del', KEYS[1])
             else
                    return 0
             end";
      
       jedis.eval(script, Collections.singletonList(key), Collections.singletonList(token));

1、分布式锁的核心(保证原子性)

  1. 加锁。占坑一定要是原子的。(判断如果没有,就给redis中保存值)
  2. 锁要自动超时。
  3. 解锁也要原子。

最终的分布式锁的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        @Lock
        public void hello(){
            String token = uuid;
            String lock = redis.setnxex("lock",token,10s);
            if(lock=="ok"){
                执行业务逻辑
                脚本删除锁
            }else{
                hello();自旋。
            }
        }

RedisTemplate和Jedis客户端2选一

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Autowired
    JedisPool jedisPool;
	public void incrDistribute(){
       

        Jedis jedis = jedisPool.getResource();
        try {
            String token = UUID.randomUUID().toString();
            // 三秒过期
            String lock = jedis.set("lock", token, SetParams.setParams().ex(3).nx());
            if (lock!=null&&lock.equalsIgnoreCase("OK")) {
                String num = jedis.get("num");
                Integer i = Integer.parseInt(num);
                i=i+1;
                jedis.set("num", i.toString());

                //删除锁
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                jedis.eval(script, Collections.singletonList("lock"),Collections.singletonList(token));
                logger.info("删除锁成功:{}"+Thread.currentThread().getId());
            }else {
                try {
                    Thread.sleep(1000);
                    incrDistribute();
                } catch (InterruptedException e) {
                    logger.error("线程睡眠异常:{}",e);
                }
            }
        } finally {
            jedis.close();
        }



    }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Autowired
    StringRedisTemplate redisTemplate;
    public void incrDistribute(){
		//1、加锁
        String token = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", token, 3, TimeUnit.SECONDS);
        if (lock) {
            ValueOperations<String, String> stringStringValueOperations = redisTemplate.opsForValue();
            String num = stringStringValueOperations.get("num");
            if (num !=null) {
                Integer i = Integer.parseInt(num);
                i=i+1;
                stringStringValueOperations.set("num", i.toString());
            }

            //删除锁
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            DefaultRedisScript<Long> script1 = new DefaultRedisScript<Long>(script,Long.class);
            redisTemplate.execute(script1, Arrays.asList("lock"),token);
            logger.info("删除锁成功:{}"+Thread.currentThread().getId());
        }else {
            incrDistribute();
        }
	}

问题:如果业务执行超时 锁被自动删除也是个问题 其他线程获取到锁进来了也是个问题 解决: 使用守护线程 守护线程做加时操作 保证过期时间够用 本线程业务执行完成后 守护线程加时操作自然结束 redisson的看门狗监听也可以

redis实现分布式锁有各种问题 建议使用Redisson

锁的更多考虑 1)、自旋。 自旋次数。 自旋超时。 2)、锁设置 锁粒度;细;记录级别; 1)、各自服务各自锁 2)、分析好粒度,不要锁住无关数据。一种数据一种锁,一条数据一个锁。 3)、锁类型


使用jedis springboot整合redis

application.properties

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
spring.redis.host=192.168.217.130
spring.redis.jedis.pool.max-active=20
spring.redis.jedis.pool.max-idle=5

pom文件进入jar包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.0.1</version>
        </dependency>

添加配置类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xiepanpan.locks.lockstest.config;


import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author: xiepanpan
 * @Date: 2020/2/20
 * @Description: jedis 配置类
 */
@Configuration
public class JedisConfig {

    @Bean
    public JedisPool jedisPoolConfig(RedisProperties properties) {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        RedisProperties.Pool pool = properties.getJedis().getPool();

        //设置配置
        jedisPoolConfig.setMaxIdle(pool.getMaxIdle());
        jedisPoolConfig.setMaxTotal(pool.getMaxActive());

        JedisPool jedisPool = new JedisPool(jedisPoolConfig,properties.getHost(),properties.getPort());
        return jedisPool;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
尚医通-预约挂号
添加com.frx01.yygh.order.mapper.OrderInfoMapper,添加配置类,扫描mapper包
用户9615083
2022/12/30
3.5K0
尚医通-预约挂号
尚医通-数据字典
何为数据字典?数据字典就是管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等,由于该系统大量使用这种数据,所以我们要做一个数据管理方便管理系统数据,一般系统基本都会做数据管理。
用户9615083
2022/12/30
1K0
尚医通-数据字典
尚医通-数据接口
Map转换为Hospital对象时,预约规则bookingRule为一个对象属性,rule为一个数组属性,因此在转换时我们要重新对应的set方法,不然转换不会成功
用户9615083
2022/12/30
1.6K0
尚医通-数据接口
尚医通-后台接口
医院设置主要是用来保存开通医院的一些基本信息,每个医院一条信息,保存了医院编号(平台分配,全局唯一)和接口调用相关的签名key等信息,是整个流程的第一步,只有开通了医院设置信息,才可以上传医院相关信息。
用户9615083
2022/12/30
1.2K0
尚医通-后台接口
尚医通-搭建环境
打开项目并点击菜单栏上的【VCS】--》【Import into version control】--》【Create Git Repository】创建本地仓库
用户9615083
2022/12/30
6160
尚医通-搭建环境
尚医通-技术点-整合服务网关
API网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
用户9615083
2022/12/30
2860
尚医通-技术点-整合服务网关
尚医通-客户端平台
服务端渲染又称SSR (Server Side Render)是在服务端完成页面的内容,而不是在客户端通过AJAX获取数据。
用户9615083
2022/12/30
5.9K0
尚医通-客户端平台
微服务项目:尚融宝(33)(服务调用(1))
下载地址: https://github.com/alibaba/nacos/releases
一个风轻云淡
2022/11/15
2220
微服务项目:尚融宝(33)(服务调用(1))
element树形结构表格与懒加载
  如果完全用后端实现的话,那写个递归把所有数据按照层次结构查询完并封装好即可。但element的table组件给我们封装好了一些东西,我们只需要在这里根据上级id查询子数据列表即可。
别团等shy哥发育
2023/02/25
1.4K0
element树形结构表格与懒加载
微服务项目:尚融宝(17)(后端搭建:数据字典)
何为数据字典?数据字典负责管理系统常用的分类数据或者一些固定数据,例如:省市区三级联动数据、民族数据、行业数据、学历数据等,数据字典帮助我们方便的获取和适用这些通用数据。
一个风轻云淡
2022/11/15
8270
微服务项目:尚融宝(17)(后端搭建:数据字典)
就医保险管理系统 毕业设计 JAVA+Vue+SpringBoot+MySQL
基于JAVA+Vue+SpringBoot+MySQL的就医保险管理系统,包含了科室档案模块、医生档案模块、预约挂号模块、我的挂号模块,还包含系统自带的用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典管理、文件管理、图表展示等基础模块,就医保险管理系统基于角色的访问控制,给挂号管理员、患者角色使用,可将权限精确到按钮级别,您可以自定义角色并分配权限,系统适合设计精确的权限约束需求。
Designer 小郑
2023/12/01
3200
就医保险管理系统 毕业设计 JAVA+Vue+SpringBoot+MySQL
微服务项目:尚融宝(38)(核心业务流程:申请借款额度(2))
一直在前端查找,但是一直找不到结果,前端也拿到了数据,但是一直弄不出来,我把注意力放在后端用户,单独拿起表单的数据可以跳转过去,最后才发现,原来是前端的数据是拿到了,但是后端的这个参数是不一样的,(key value)对不上号
一个风轻云淡
2022/11/15
2380
微服务项目:尚融宝(38)(核心业务流程:申请借款额度(2))
1-5 MyBatisPlus集成
  接下来我们完成一个品牌的CRUD操作、我们会集成MyBatisPlus来实现。
用户4919348
2021/01/14
1K0
1-5 MyBatisPlus集成
基于SpringCloudalibaba+SSM+Mybatisplus实现在线教育讲师管理后端
基于SpringCloudalibaba+SSM+Mybatisplus实现在线教育讲师管理后端
陶然同学
2023/02/27
5440
基于SpringCloudalibaba+SSM+Mybatisplus实现在线教育讲师管理后端
mybatis code helper安装与使用
注意: 这里使用的mybatisplus, mapper.xml无需更改, 简单的增删改查已经为我们自动生成, 如下代码
时间静止不是简史
2022/01/05
1.6K0
mybatis code helper安装与使用
SpringBoot入门建站全系列(三十四)使用Drools规则引擎做排班系统
Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。
品茗IT
2020/05/28
2.6K0
微服务项目:尚融宝(41)(核心业务流程:借款额度审批)
列表的结果需要关联查询,数据字典的数据也需要展示对应的文本内容而不是值,除了定义VO的方式,我们也可以使用扩展实体类的方式 
一个风轻云淡
2022/11/15
4090
微服务项目:尚融宝(41)(核心业务流程:借款额度审批)
我是如何替换Spring Cloud Netflix的?
如果你正在寻找一个Spring Cloud Netflix的替代方案,建议可以看下这篇和Spring Cloud Alibaba相关的文章。
养码场
2018/12/26
1.7K0
服务注册、发现和远程调用
本篇文章介绍如何完成一个简单的服务注册、发现和远程调用的 Demo,通过该 Demo 来学习和了解关于 Spring Cloud 相关的知识。
码农UP2U
2021/10/14
4810
服务注册、发现和远程调用
医疗项目中所用到的技术点——以MyBatis-Plus为技术案例
惠医疗即为网上预约挂号系统,网上预约挂号是近年来开展的一项便民就医服务,旨在缓解看病难、挂号难的就医难题,许多患者为看一次病要跑很多次医院,最终还不一定能保证看得上医生。网上预约挂号全面提供的预约挂号业务从根本上解决了这一就医难题。随时随地轻松挂号!不用排长队!
用户10196776
2023/10/17
5070
推荐阅读
相关推荐
尚医通-预约挂号
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档