分布式锁的实现,基于Redis实现分布式锁

news2024/11/27 12:25:36

分布式锁的实现,基于Redis实现分布式锁

  • 前言
  • 基于mysql实现分布式锁
  • 基于Reids实现分布式锁
    • 基于Redis的具体实现
    • 问题1 :线程误删锁
    • 解决方案一:在删除锁时进行校验
    • 问题2:释放锁的检验和释放不具有原子性
    • 解决方案:基于lua脚本使检验和删除操作具有原子性
    • 其他问题


前言

对于一些业务来说,比如商品或者优惠卷的抢杀,我们常常需要进行一些加锁的操作比如synchronized等来避免商品的超卖问题,但是对于大型的购物平台,都是使用了分布式的系统,将服务部署在多台服务器中,让请求通过轮询算法发送到多个服务器中,用来减少服务器的压力,但是使用了分布式系统也就会导致单机锁的失效,比如synchronized来讲,它是基于JVM的监视器锁,线程想要进入被其修饰的代码块就要获取JVM的监视器锁,但是对于多个服务器,它们有着不同的JVM,监视器锁也就不同,也就导致了多个服务器的线程可以同时进入被其修饰的代码块中,此时我们就需要来实现一个分布式锁,可以同时阻塞多个服务器的线程。


基于mysql实现分布式锁

我们可以使用mysql的数据库表来实现,添加几个字段,例如id、lock_name、thread_id分表表示锁的id、锁的名称、加锁的服务器的线程id,给lock_name加唯一约束,线程需要获取锁就需要尝试在数据库插入锁,如果数据库中存在相同的锁,就不能插入也就导致获取锁失败,成功插入锁就获取到了锁,这是基于mysql的锁机制(插入数据时会添加排他性的行级锁)来保证并发安全。
特点:
可用性:高可用
性能:一般
安全性:当mysql连接断开时,会发生线程安全问题

基于Reids实现分布式锁

我们还可以基于Redis实现分布式锁,Redis的缓存可以被多个服务器所共享,我们可以基于Redis的set nx实现分布式锁,当要存储的值不存在时才能插入成功,否则插入失败,还可以使用参数ex设置线程持有锁的超时时间,防止线程一直持有锁。
特点:
可用性:高可用
性能:比mysql更好
安全性:好,可以给锁设置过期时间

基于Redis的具体实现

public class RedisLock implements ILock {


    private final static String NAME = "lock:";

    private String key;

    private StringRedisTemplate redisTemplate;

    public RedisLock(String key, StringRedisTemplate redisTemplate) {
        this.key = key;
        this.redisTemplate=redisTemplate
}
    @Override
    public boolean getLock(Long timeOut) {
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(NAME + key, “1”, timeOut, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

    @Override
    public void releaseLock() {
        redisTemplate.delete(NAME_key)   
    }
}


如上,我们封装了lock+key的锁的获取和释放方法,这样看起来觉得实现非常简单,实际有很多问题存在


问题1 :线程误删锁

有一个用户的不同服务器的不同线程,分别是线程一和线程二,线程一先获取到了锁,线程一由于业务阻塞超过了锁的过期时间,导致锁被释放,此时线程二获取到了锁,开始执行业务,线程一又继续执行业务执行完毕后删除锁,把线程二持有的锁给释放了,其他线程又获取到了锁,就发生了线程安全问题

解决方案一:在删除锁时进行校验

解决问题一很容易想到,我们可以添加锁时给其value值设置成服务器线程的唯一标识,每次删除锁时都会检验是否是当前线程持有的锁

public class RedisLock implements ILock {


    private final static String NAME = "lock:";

    private String key;

    private StringRedisTemplate redisTemplate;

    public RedisLock(String key, StringRedisTemplate redisTemplate) {
        this.key = key;
        this.redisTemplate=redisTemplate
}
    @Override
    public boolean getLock(Long timeOut) {
        String uid = PREFIX_ID + Thread.currentThread().getId();
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(NAME + key, uid, timeOut, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

    @Override
    public void releaseLock() {
        String id = PREFIX_ID + Thread.currentThread().getId();
        String uid=redisTemplate.opsForValue().get(NAME+key);
        if (id.equals(uid)){
         redisTemplate.delete(NAME+key)      
        }
}

问题2:释放锁的检验和释放不具有原子性

在解决方法一和方法二后,其实还有一个问题,显然在释放锁的操作中,校验和释放不具有原子性,这样就可能会发生这样一个问题,例如线程一和线程二是同一个用户的不同的线程请求,线程一获取到了锁,执行完业务准备释放锁,在释放锁校验成功后,发生了长时间的阻塞(比如FullGC会阻塞所有线程),导致线程的持有锁的时间超过了锁的过期时间,导致锁被释放,被线程二成功获取到了锁,开始执行,此时线程一恢复后释放了锁,此时又有其他线程可以获取到锁,就发生了线程安全的问题。

解决方案:基于lua脚本使检验和删除操作具有原子性

创建lua脚本,lua语言具有原子性

if(redis.call('get',KEYS[1]==ARGV[1])) then
    --如果相同删除
    return redis.call('del',KEYS[1])
end
return 0


public class RedisLock implements ILock {


    private final static String NAME = "lock:";

    private String key;

    private StringRedisTemplate redisTemplate;

    private final static String PREFIX_ID = UUID.randomUUID().toString(true) + "_";

    private final static DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static {
        UNLOCK_SCRIPT=new DefaultRedisScript<Long>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("noLock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    public RedisLock(String key, StringRedisTemplate redisTemplate) {
        this.key = key;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean getLock(Long timeOut) {
        String id = PREFIX_ID + Thread.currentThread().getId();
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(NAME + key, id, timeOut, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

    @Override
    public void releaseLock() {
        String id = PREFIX_ID + Thread.currentThread().getId();
        redisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(NAME+key),id);
    }
}

其他问题

基于Redis实现的分布式锁还具有其他问题,比如不可重入、不能多次尝试获取锁、超时释放、主从一致性问题,我们在下一篇文章来介绍这些问题产生的原因以及解决方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

摄影:相机控色

摄影&#xff1a;相机控色 白平衡&#xff08;White Balance&#xff09;白平衡的作用&#xff1a; 白平衡的使用环境色温下相机色温下总结 白平衡偏移与包围白平衡包围 影调 白平衡&#xff08;White Balance&#xff09; 人眼看到的白色&#xff1a;会自动适应环境光线。 相…

性能监控利器:Ubuntu 22.04 上的 Zabbix 安装与配置指南

简介 今天我们来聊聊如何在 Ubuntu 22.04 上安装和配置 Zabbix。我们会用到 PostgreSQL 作为数据库后端&#xff0c;Nginx 作为 Web 服务器&#xff0c;并用 Let’s Encrypt SSL 证书来保驾护航。 什么是 Zabbix&#xff1f; Zabbix 是一个开源的网络监控和管理解决方案&…

队列基本实现

模板 int queue[10010]; int hh1,tt0; void push1(int x) {queue[tt]x; } void pop1() {if(hh>tt){cout<<"ERR_CANNOT_POP"<<endl;}else{hh;} } int query1() {if(hh>tt){cout<<"ERR_CANNOT_QUERY"<<endl;}return queue[hh…

【ArcGISPro】使用AI模型提取要素-提取车辆(目标识别)

示例数据下载 栅格数据从网上随便找一个带有车辆的栅格数据 f094a6b1e205cd4d30a2e0f816f0c6af.jpg (1200799) (588ku.com) 添加数据

GitLab|数据迁移

注意&#xff1a;新服务器GitLab版本需和旧版本一致 在旧服务器执行命令进行数据备份 gitlab-rake gitlab:backup:create 备份数据存储在 /var/opt/gitlab/backups/ 将备份数据传输到新服务器的/var/opt/gitlab/backups/下&#xff0c;并修改文件权限&#xff08;下载前和上传…

UE5 5.1.1创建C++项目,显示error C4668和error C4067的解决方法

因为工作要求&#xff0c;没法使用最新 5.5版本的ue5 而是要用ue5.1和5.2版本。 但是我在安装下载了visual studio2022后&#xff0c;使用 ue5.1编辑器 创建C项目&#xff0c;爆出如下错误。 error C4668: ?????__has_feature?????ΪԤ?????꣬???0????…

网络安全概论

一、 网络安全是一个综合性的技术。在Internet这样的环境中&#xff0c;其本身的目的就是为了提供一种开放式的交互环境&#xff0c;但是为了保护一些秘密信息&#xff0c;网络安全成为了在开放网络环境中必要的技术之一。网络安全技术是随着网络技术的进步逐步发展的。 网络安…

51单片机基础01 单片机最小系统

目录 一、什么是51单片机 二、51单片机的引脚介绍 1、VCC GND 2、XTAL1 2 3、RST 4、EA 5、PSEN 6、ALE 7、RXD、TXD 8、INT0、INT1 9、T0、T1 10、MOSI、MISO、SCK 11、WR、RD 12、通用IO P0 13、通用IO P1 14、通用IO P2 三、51单片机的最小系统 1、供电与…

DASCTF 2024 10月 Reverse 完成笔记 附题目

题目链接: https://github.com/Airrcat/long_long/tree/main/DASCTF_2024_10 ezre 查PE 32位无壳 开始分析 看起来很像加壳了 字符串未有暴露信息&#xff0c;但是段中有一个themida 发现是一个壳&#xff0c;直接去找脱壳机 一些脱壳工具&#xff08;Magicmida)是…

JavaScript 中 arguments、类数组与数组的深入解析

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;什么是 arguments 对象2.1 arguments 的定义2.2 arguments 的特性2.3 使用场景 &#x1f4af;深入了解 arguments 的结构3.1 arguments 的内部结构arguments 的关键属性 3.2 类数组…

Kafka 工作流程解析:从 Broker 工作原理、节点的服役、退役、副本的生成到数据存储与读写优化

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

数位之和c++

题目描述 小杨有 n个正整数&#xff0c;他认为一个正整数是美丽数字当且仅当该正整数每一位数字的总和是 7 的倍数。 小杨想请你编写一个程序判断 n 个正整数哪些是美丽数字。 输入 第一行包含一个正整数 n&#xff0c;代表正整数个数。 之后n 行&#xff0c;每行包含一个…

同三维T4000S系列高清SDI字符叠加器

同三维T4000S系列高清SDI字符叠加器 两个型号&#xff1a; 同三维T4000S-2U (2U机箱&#xff0c;可插1-16张叠加模块) 同三维T4000S1 &#xff08;单路&#xff09; 产品简介 “HD-SDI字符叠加器”可在HD-SDI视频图象信号上叠加日期、时间及中英文字符信息。广泛用于安防监…

重生之我在学环境变量

环境变量 基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但 是照样可以链接成功&#…

Flink学习连载文章4-flink中的各种转换操作

首先&#xff0c;先搞一个模板代码&#xff0c;方便后面的操作 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! "")package ${PACKAGE_NAME};#end #parse("File Header.java") import org.apache.flink.streaming.api.environment.StreamExecutionEnv…

fastadmin实现站内通知功能

实现效果如下 application/admin/view/common/header.html <style>#notificationMenu {display: none;position: absolute;top: 40px;right: 0;background: #fff;border-radius: 6px;padding: 10px 0;width: 300px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);z-inde…

默语博主的推荐:探索技术世界的旅程

这是第一位推荐的博主默语 引言&#xff1a; CSDN中的默语博主是一个值得关注和学习的技术大拿。他的博客内容不仅涵盖了各种热门的技术领域&#xff0c;还能够帮助读者深入了解技术背后的原理和应用。在这篇类博客的内容中&#xff0c;我们将探索默语博主推荐的几篇博客&#…

【漏洞复现】|智互联SRM智联云采系统quickReceiptDetail SQL注入漏洞

漏洞描述 智互联(深圳)科技有限公司SRM智联云采系统针对企业供应链管理难题&#xff0c;及智能化转型升级需求&#xff0c;智联云采依托人工智能、物联网、大数据、云等技术&#xff0c;通过软硬件系统化方案&#xff0c;帮助企业实现供应商关系管理和采购线上化、移动化、智能…

【数据分析】认清、明确

1、什么是数据分析。 - 通过对大量的数据进行科学的分析。 - 得出结论&#xff0c;提出建议&#xff0c;辅助公司企业的决策。2、数据分析分为几步。 - 1.明确目的! - 2.收集数据!自己的数据! 自动化采集的数据! - 3.数据处理! - 4.数据分析!数据分析(业务)数据挖掘(代码算法…