【Redis系列笔记】分布式锁

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

1. 概述

分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。

分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路

1.1. 前提条件

需要满足一些条件:

可见性:多个线程都能看到相同的结果(此处可见性区别于并发编程的内存可见性)

互斥:互斥是分布式锁的最基本的条件,使得程序串行执行

高可用:程序不易崩溃,时时刻刻都保证较高的可用性

高性能:由于加锁本身就让性能降低,所有对于分布式锁本身需要他就较高的加锁性能和释放锁性能

安全性:安全也是程序中必不可少的一环

1.2. 分类

常见的分布式锁有三种

Mysql:mysql本身就带有锁机制,但是由于mysql性能本身一般,所以采用分布式锁的情况下,其实使用mysql作为分布式锁比较少见

Redis:redis作为分布式锁是非常常见的一种使用方式,现在企业级开发中基本都使用redis或者zookeeper作为分布式锁,利用setnx这个方法,如果插入key成功,则表示获得到了锁,如果有人插入成功,其他人插入失败则表示无法获得到锁,利用这套逻辑来实现分布式锁

Zookeeper:zookeeper也是企业级开发中较好的一个实现分布式锁的方案,由于本套视频并不讲解zookeeper的原理和分布式锁的实现,所以不过多阐述

技术

MySQL

Redis

Zookeeper

互斥

利用mysql本身的互斥锁机制

利用setnx这样的互斥命令

利用节点的唯一性和有序性实现互斥

高可用

高性能

一般

一般

安全性

断开连接,自动释放锁

利用锁超时时间,到期释放

临时节点,断开连接自动释放

1.3. 核心思路

实现分布式锁时需要实现的两个基本方法:获取锁以及释放锁

//获取锁:
//互斥:确保只能有一个线程获取锁O
//添加锁,利用setnx的互斥特性
SETNX lock thread1
//非阻塞:尝试一次,成功返回true,失败返回false
//添加锁,NX是互斥、EX是设置超时时间
SET lock threadl NX EX 1
//释放锁:
//手动释放
//释放锁,删除即可
DEL key
//超时释放:获取锁时添加一个超时时间
//添加锁过期时间,避免服务宕机引起的死锁
EXPIRE lock 10

我们利用redis 的setNx 方法,当有多个线程进入时,我们就利用该方法,第一个线程进入时,redis 中就有这个key 了,返回了1,如果结果是1,则表示他抢到了锁,那么他去执行业务,然后再删除锁,退出锁逻辑,没有抢到锁的哥们,等待一定时间后重试即可

2. Redis分布式锁

资料链接:https://pan.baidu.com/s/1BnQ08PlhJ0WLoBcMMHp75A

提取码:gv96

2.1. Redis实现分布式锁的初级版本

在order-service服务中导入redis的依赖坐标

<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在配置文件中添加redis的配置

spring:
redis:
host: 192.168.200.128
port: 6379

在order-service服务中创建一个setNx锁

package cn.itcast.order.lock;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
/**
 * 创建setnx锁
 */
public class RedisSetNxLock {
    //定义锁的名称
    private final String name;
    private final StringRedisTemplate stringRedisTemplate;
    public RedisSetNxLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }
    //定义当前key的前缀
    private static  final String KEY_PREFIX="lock:";
    /**
     * 获取锁
     *
     * @param timeoutsec key的过期时间
     * @return true表示拿到锁,false表示没有拿到锁
     */
    public boolean tryLock(Long timeoutsec) {
        //2.获取当前线程的id作为value值,保证唯一性
        long threadId = Thread.currentThread().getId();
        /**
         * 1.获取锁
         * setIfAbsent(K key, V value, long timeout, TimeUnit unit)
         *
         key –参数1表示redis中的key
         value – 参数2表示redis中存储的值
         timeout – 参数3表示key的过期时间
         unit – 参数4表示时间单位
         */
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutsec, TimeUnit.MINUTES);
        /**
         * 这个是为了防止类型的拆箱,如果返回值为null的话,boolean类型会报错
         * 意思:如果相等于则返回true,不想等于返回false,如果flag=null的话,也是返回false;
         */
        return Boolean.TRUE.equals(flag);
    }
    /**
     * 释放锁
     *
     */
    public void unLock() {
        //通过手动释放锁
        stringRedisTemplate.delete(KEY_PREFIX+name);
    }
}

改造orderService接口中创建订单的业务代码

package cn.itcast.order.service.impl;
import cn.itcast.feign.client.AccountFeignClient;
import cn.itcast.feign.client.StorageFeginClient;
import cn.itcast.order.entity.Order;
import cn.itcast.order.lock.RedisSetNxLock;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.service.OrderService;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
 *订单业务相关
 */
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private AccountFeignClient accountClient;
    @Resource
    private StorageFeginClient storageClient;
    @Resource
    private  OrderMapper orderMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 下单业务
     * @param order
     * @return
     */
    @Override
    @Transactional
    public Long create(Order order) {
        //获取锁
        RedisSetNxLock redisSetNxLock=new RedisSetNxLock("order:"+order.getUserId(),stringRedisTemplate);
        boolean flag = redisSetNxLock.tryLock( 1200L);
        if(!flag){
            log.error("不允许重复下单");
            return 0L;
        }
        // 创建订单
        orderMapper.insert(order);
        try {
            // 扣用户余额
            accountClient.deduct(order.getUserId(), order.getMoney());
            // 扣库存
            storageClient.deduct(order.getCommodityCode(), order.getCount());
            return order.getId();
        } catch (FeignException e) {
            log.error("下单失败,原因:{}", e.contentUTF8(), e);
            throw new RuntimeException(e.contentUTF8(), e);
        }finally {
            //释放锁
            redisSetNxLock.unLock();
        }
    }
}

测试效果

  • 我们起两个order-service服务,并且端口号分别为8881和8801
  • 然后在创建订单的业务方法中的第一行打上断点
  • 启动方式都为debug方式
  • 在postman中设置两个请求

form表单的参数

userId=user202103032042012
commodityCode=100202003032041
count=2
money=200

线程1:

线程2:

测试结果:debug发现,两个线程只有一个线程能够下单成功,另外一个获取锁失败,则下单失败,并在控制台打印不可重复下单异常信息。

2.2. 基于Redis实现分布式锁误删情况

2.2.1. 误删逻辑说明:

持有锁的线程在锁的内部出现了阻塞,导致他的锁自动释放,这时其他线程,线程2来尝试获得锁,就拿到了这把锁,然后线程2在持有锁执行过程中,线程1反应过来,继续执行,而线程1执行过程中,走到了删除锁逻辑,此时就会把本应该属于线程2的锁进行删除,这就是误删别人锁的情况说明

解决方案:解决方案就是在每个线程释放锁的时候,去判断一下当前这把锁是否属于自己,如果属于自己,则不进行锁的删除,假设还是上边的情况,线程1卡顿,锁自动释放,线程2进入到锁的内部执行逻辑,此时线程1反应过来,然后删除锁,但是线程1,一看当前这把锁不是属于自己,于是不进行删除锁逻辑,当线程2走到删除锁逻辑时,如果没有卡过自动释放锁的时间点,则判断当前这把锁是属于自己的,于是删除这把锁。

2.2.2. 具体实现:

需求:修改之前的分布式锁实现,满足:在获取锁时存入线程标示(可以用UUID表示)在释放锁时先获取锁中的线程标示,判断是否与当前线程标示一致

  • 如果一致则释放锁
  • 如果不一致则不释放锁

核心逻辑:在存入锁时,放入自己线程的标识,在删除锁时,判断当前这把锁的标识是不是自己存入的,如果是,则进行删除,如果不是,则不进行删除。

本次,只需要更改RedisSetNxLock类中的获取锁和释放锁中的代码逻辑,无须更改下单业务逻辑

修改后代码

//定义一个uuid作为前缀
private static  final String ID_PREFIX=UUID.randomUUID().toString().replace("-","");
/**
     * 获取锁
     * @param timeoutsec key的过期时间
     * @return true表示拿到锁,false表示没有拿到锁
     */
public boolean tryLock(Long timeoutsec) {
//2.获取当前线程的id作为value值,保证唯一性
String threadId = ID_PREFIX+"-"+Thread.currentThread().getId();
/**
         * 1.获取锁
         * setIfAbsent(K key, V value, long timeout, TimeUnit unit)
         *
         key –参数1表示redis中的key
         value – 参数2表示redis中存储的值
         timeout – 参数3表示key的过期时间
         unit – 参数4表示时间单位
         */
Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId , timeoutsec, TimeUnit.MINUTES);
/**
         * 这个是为了防止类型的拆箱,如果返回值为null的话,boolean类型会报错
         * 意思:如果相等于则返回true,不想等于返回false,如果flag=null的话,也是返回false;
         */
return Boolean.TRUE.equals(flag);
}
/**
     * 释放锁
     *
     */
public void unLock() {
    //获取线程id
    String threadId = ID_PREFIX+Thread.currentThread().getId();
    //获取redis中的value=线程id
    String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
    //如果两个线程id相等的话,则释放锁
    if(threadId.equals(id)){
        //通过手动释放锁
        stringRedisTemplate.delete(KEY_PREFIX+name);
    }
}

测试结果

  • debug重新启动order-service两个服务,8881和8801
  • 然后在下单业务create方法中的获取锁和释放锁那分别打断点
  • 在unlock释放锁的代码中的第一行打个断点
  • 在postman中启动发起两个服务的请求
  • 第一个线程持有锁后,手动释放锁,第二个线程 此时进入到锁内部,再放行第一个线程,此时第一个线程由于锁的value值并非是自己,所以不能释放锁,也就无法删除别人的锁,此时第二个线程能够正确释放锁,通过这个案例初步说明我们解决了锁误删的问题。

2.3. 基于Redis实现分布式锁的原子性情况

2.3.1. 更为极端的误删逻辑说明:

线程1现在持有锁之后,在执行业务逻辑过程中,他正准备删除锁,而且已经走到了条件判断的过程中,比如他已经拿到了当前这把锁确实是属于他自己的,正准备删除锁,但是此时他的锁到期了,那么此时线程2进来,但是线程1他会接着往后执行,当他卡顿结束后,他直接就会执行删除锁那行代码,相当于条件判断并没有起到作用,这就是删锁时的原子性问题,之所以有这个问题,是因为线程1的拿锁,比锁,删锁,实际上并不是原子性的,我们要防止刚才的情况发生。

2.3.2. 使用Lua语言解决原子性问题


了解Lua语言

Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。Lua是一种编程语言,它的基本语法大家可以参考网站:https://www.runoob.com/lua/lua-tutorial.html,这里重点介绍Redis提供的调用函数,我们可以使用lua去操作redis,又能保证他的原子性,这样就可以实现拿锁比锁删锁是一个原子性动作了,作为Java程序员这一块并不作一个简单要求,并不需要大家过于精通,只需要知道他有什么作用即可。

这里重点介绍Redis提供的调用函数,语法如下:

redis.call('命令名称', 'key', '其它参数', ...)

例如,我们要执行set name jack,则脚本是这样:

# 执行 set name jack
redis.call('set', 'name', 'jack')

例如,我们要先执行set name Rose,再执行get name,则脚本如下:

# 先执行 set name jack
redis.call('set', 'name', 'Rose')
# 再执行 get name
local name = redis.call('get', 'name')
# 返回
return name

写好脚本以后,需要用Redis命令来调用脚本,调用脚本的常见命令如下:

例如,我们要执行 redis.call('set', 'name', 'jack') 这个脚本,语法如下:

# 调用脚本
EVAL "return redis.call('set' ,"name", "jack')" 0
后面的0为脚本需要的key类型的参数个数

如果脚本中的key、value不想写死,可以作为参数传递。key类型参数会放入KEYS数组,其它参数会放入ARGV数组,在脚本中可以从KEYS和ARGV数组获取这些参数:

#调用脚本
EVAL "return redis.call('set',KEYS[1],ARGV[1])" 1 name Rose


代码实现原子性

1、在idea中可以下载lua的插件Luanalysis

2、在order-service服务中的resource下创建一个unlock.lua脚本

编写以下内容:

-- 这里的 KEYS[1] 就是锁的key,这里的ARGV[1] 就是当前线程标示
-- 获取锁中的标示,判断是否与当前线程标示一致
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
    -- 一致,则删除锁
    return redis.call('DEL', KEYS[1])
end
-- 不一致,则直接返回
return 0

3、更改释放锁的代码

//定义静态化常量类
private static final DefaultRedisScript<Long> redisScript;
//执行静态快代码
static {
    redisScript=new DefaultRedisScript<>();
    redisScript.setLocation(new ClassPathResource("unlock.lua"));
    redisScript.setResultType(Long.class);
}
/**
     * 释放锁-利用lua脚本实现原子性
     *
     */
public void unLock() {
    //调用lua脚本
    stringRedisTemplate.execute(redisScript,
                                Collections.singletonList(KEY_PREFIX+name),
                                ID_PREFIX+Thread.currentThread().getId());
}

4、解析execute方法

我们的RedisTemplate中,可以利用execute方法去执行lua脚本,关系如下:


测试效果

  • debug重新启动order-service两个服务,8881和8801
  • 然后在下单业务create方法中的获取锁和释放锁那分别打断点
  • 在unlock释放锁的代码中的第一行打个断点
  • 在postman中启动发起两个服务的请求

第一个线程进来,得到了锁,手动删除锁,模拟锁超时了,其他线程会执行lua来抢锁,当第一个线程利用lua删除锁时,lua能保证他不能删除他的锁,第二个线程删除锁时,利用lua同样可以保证不会删除别人的锁,同时还能保证原子性。

3. Redisson分布式锁

3.1. SetNx实现分布式锁存在以下问题

基于setnx实现的分布式锁存在下面的问题:

重入问题:重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中,可重入锁的意义在于防止死锁,比如HashTable这样的代码中,他的方法都是使用synchronized修饰的,假如他在一个方法内,调用另一个方法,那么此时如果是不可重入的,不就死锁了吗?所以可重入锁他的主要意义是防止死锁,我们的synchronized和Lock锁都是可重入的。

不可重试:是指目前的分布式只能尝试一次,我们认为合理的情况是:当线程在获得锁失败后,他应该能再次尝试获得锁。

超时释放:我们在加锁时增加了过期时间,这样的我们可以防止死锁,但是如果卡顿的时间超长,虽然我们采用了lua表达式防止删锁的时候,误删别人的锁,但是毕竟没有锁住,有安全隐患

主从一致性: 如果Redis提供了主从集群,当我们向集群写数据时,主机需要异步的将数据同步给从机,而万一在同步过去之前,主机宕机了,就会出现死锁问题。

3.2. Redisson介绍

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) 。

使用Redisson可以非常方便将Java本地内存中的常用数据结构的对象搬到分布式缓存redis中。

也可以将常用的并发编程工具如:AtomicLongCountDownLatchSemaphore等支持分布式。

使用RScheduledExecutorService 实现分布式调度服务。

支持数据分片,将数据分片存储到不同的redis实例中。

支持分布式锁,基于Java的Lock接口实现分布式锁,方便开发。

简单来说:

一个基于Redis实现的分布式工具,有基本分布式对象和高级又抽象的分布式服务,为每个试图再造分布式轮子的程序员带来了大部分分布式问题的解决办法。

提供了使用Redis的最简单和最便捷的方法。促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

3.3. Redisson和Jedis、Lettuce区别

  • Jedis:Redis 官方推出的用于通过 Java 连接 Redis 客户端的一个工具包,它提供了全面的类似于 Redis 原生命令的支持,是目前使用最广的一款 java 客户端。
  • Lettuce:一个可扩展的线程安全的 Redis 客户端,通讯框架基于 Netty 开发,支持高级的 Redis 特性,比如哨兵,集群,管道,自动重新连接等特性。从 Spring Boot 2.x 开始, Lettuce 已取代 Jedis 成为首选 Redis 的客户端。
  • Redisson:一款架设在 Redis 基础上,通讯基于 Netty 的综合的、新型的中间件,是企业级开发中使用 Redis 的最佳范本。

总结下来,Jedis 把 Redis 命令封装的非常全面,Lettuce 则进一步丰富了 Api,支持 Redis 各种高级特性。但是两者并没有进一步深化,只给了你操作 Redis 数据库的工具,而 Redisson 则是基于 Redis、Lua 和 Netty 建立起了一套的分布式解决方案,比如分布式锁的实现,分布式对象的操作等等。

在实际使用过程中,Lettuce + Redisson组合使用的比较多,两者相铺相成。

3.4. 具体实现

导入依赖

<dependency>
  <groupId>org.redisson</groupId>
  <artifactId>redisson</artifactId>
  <version>3.13.6</version>
</dependency>

配置文件

@Configuration
public class RedisConfig {
    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.200.196:6379");
        return Redisson.create(config);
    }
}

在下单业务中添加分布式锁

@Resource
private RedissonClient redissonClient;
/**
     * 下单业务
     * @param order
     * @return
     */
@Override
@Transactional
public Long create(Order order) {
//redisson
RLock lock = redissonClient.getLock("lock:" + order.getUserId());
boolean flag = false;
try {
    flag = lock.tryLock(10, 60, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
}
if(!flag){//获取锁失败
    log.error("获取锁失败,不允许执行业务");
    return 0L;
}
// 创建订单
orderMapper.insert(order);
try {
    // 扣用户余额
    accountClient.deduct(order.getUserId(), order.getMoney());
    // 扣库存
    storageClient.deduct(order.getCommodityCode(), order.getCount());
} catch (FeignException e) {
    log.error("下单失败,原因:{}", e.contentUTF8(), e);
    throw new RuntimeException(e.contentUTF8(), e);
}finally {
    //释放锁
    lock.unlock();
}
return order.getId();
}


相关实践学习
基于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 Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
8天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
41 16
|
1月前
|
缓存 NoSQL Java
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
59 3
大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁
|
1月前
|
NoSQL Redis 数据库
计数器 分布式锁 redis实现
【10月更文挑战第5天】
47 1
|
1月前
|
NoSQL 算法 关系型数据库
Redis分布式锁
【10月更文挑战第1天】分布式锁用于在多进程环境中保护共享资源,防止并发冲突。通常借助外部系统如Redis或Zookeeper实现。通过`SETNX`命令加锁,并设置过期时间防止死锁。为避免误删他人锁,加锁时附带唯一标识,解锁前验证。面对锁提前过期的问题,可使用守护线程自动续期。在Redis集群中,需考虑主从同步延迟导致的锁丢失问题,Redlock算法可提高锁的可靠性。
74 4
|
1月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
55 3
|
1月前
|
缓存 NoSQL 算法
面试题:Redis如何实现分布式锁!
面试题:Redis如何实现分布式锁!
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
112 2
基于Redis的高可用分布式锁——RedLock
|
3月前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
1月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
61 4

相关产品

  • 云数据库 Tair(兼容 Redis)