2、基于redis实现分布式锁

这篇具有很好参考价值的文章主要介绍了2、基于redis实现分布式锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

2.1. 基本实现

借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。
2、基于redis实现分布式锁,分布式锁,分布式锁

  1. 多个客户端同时获取锁(setnx)
  2. 获取成功,执行业务逻辑,执行完成释放锁(del)
  3. 其他客户端等待重试

改造StockService方法:

 /**
     * redis setnx实现分布式锁,最基本的哪一种 !!!
     */
    public void deduct() {
        // 加锁setnx
        Boolean lock = this.redisTemplate.opsForValue().setIfAbsent("lock", "111");
        if (!lock) {
            // 没有获取到锁,进行重试!!
            try {
                Thread.sleep(50);
                this.deduct();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            try {
                // 1. 查询库存信息
                String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
                // 2. 判断库存是否充足
                if (stockStr != null && stockStr.length() != 0) {
                    Long stock = Long.parseLong(stockStr);
                    if (stock > 0) {
                        redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                    }
                }
            } finally {
                // 解锁
                this.redisTemplate.delete("lock");
            }
        }
    }

使用 jmeter 进行压测
2、基于redis实现分布式锁,分布式锁,分布式锁
查看库存数量
2、基于redis实现分布式锁,分布式锁,分布式锁

上述代码优化,不断重试的过程中一直进行递归,最终导致栈的溢出。

解决


    /**
     *  while循环代替递归,解决不断重试可能导致的栈溢出的问题
     */
    public void deduct() {
        // 加锁setnx
        while (this.redisTemplate.opsForValue().setIfAbsent("lock1", "1")) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                // 1. 查询库存信息
                String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
                // 2. 判断库存是否充足
                if (stockStr != null && stockStr.length() != 0) {
                    Long stock = Long.parseLong(stockStr);
                    if (stock > 0) {
                        redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                    }
                }
            } finally {
                // 解锁
                this.redisTemplate.delete("lock1");
            }
        }
    }

2.2. 防死锁

问题:setnx刚刚获取到锁,当前服务器宕机,导致del释放锁无法执行,进而导致锁无法锁无法释放(死锁)
解决:给锁设置过期时间,自动释放锁。

设置过期时间两种方式:

  • 通过expire设置过期时间(缺乏原子性:如果在setnx和expire之间出现异常,锁也无法释放)
  • 使用set指令设置过期时间:set key value ex 3 nx(既达到setnx的效果,又设置了过期时间)

2、基于redis实现分布式锁,分布式锁,分布式锁

2.3. 防误删

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

解决: setnx获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这个值,判断是否自己的锁
2、基于redis实现分布式锁,分布式锁,分布式锁

问题:删除操作缺乏原子性。
场景:

  1. index1执行删除时,查询到的lock值确实和uuid相等
  2. index1执行删除前,lock刚好过期时间已到,被redis自动释放
  3. index2获取了lock
  4. index1执行删除,此时会把index2的lock删除

解决方案:没有一个命令可以同时做到判断 + 删除,所有只能通过其他方式实现(LUA脚本

2.4. redis中的lua脚本

2.4.1 redis 并不能保证一组命令的原子性

redis采用单线程架构,可以保证单个命令的原子性,但是无法保证一组命令在高并发场景下的原子性

如果redis客户端通过lua脚本把3个命令一次性发送给redis服务器,那么这三个指令就不会被其他客户端指令打断。Redis 也保证脚本会以原子性的方式执行: 当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。 这和使用 MULTI/ EXEC 包围的事务很类似。

2.4.2 lua介绍

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

2.4.3. lua基本语法

a = 5               -- 全局变量
local b = 5         -- 局部变量, redis只支持局部变量
a, b = 10, 2*x      -- 等价于       a=10; b=2*x

流程控制:
if( 布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

2.4.4. redis执行lua脚本 - EVAL指令

在redis中需要通过eval命令执行lua脚本。

格式:

EVAL script numkeys key [key ...] arg [arg ...]
script:lua脚本字符串,这段Lua脚本不需要(也不应该)定义函数。
numkeys:lua脚本中KEYS数组的大小
key [key ...]:KEYS数组中的元素
arg [arg ...]:ARGV数组中的元素

案例1:基本案例

EVAL "return 10" 0

输出:(integer) 10


案例2:动态传参
EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 5 10 20 30 40 50 60 70 80 90
# 输出:10 20 60 70

EVAL "if KEYS[1] > ARGV[1] then return 1 else return 0 end" 1 10 20
# 输出:0

EVAL "if KEYS[1] > ARGV[1] then return 1 else return 0 end" 1 20 10
# 输出:1

传入了两个参数10和20,KEYS的长度是1,所以KEYS中有一个元素10,剩余的一个20就是ARGV数组的元素。


案例3:执行redis类库方法
redis.call()中的redis是redis中提供的lua脚本类库,仅在redis环境中可以使用该类库

set aaa 10  -- 设置一个aaa值为10
EVAL "return redis.call('get', 'aaa')" 0
# 通过return把call方法返回给redis客户端,打印:"10"

注意:**脚本里使用的所有键都应该由 KEYS 数组来传递。**但并不是强制性的,代价是这样写出的脚本不能被 Redis 集群所兼容


案例4:给redis类库方法动态传参 ```shell EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 bbb 20 ``` 2、基于redis实现分布式锁,分布式锁,分布式锁 学到这里基本可以应付redis分布式锁所需要的脚本知识了。

2.5. 使用lua保证删除原子性

删除LUA脚本:

if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end

2、基于redis实现分布式锁,分布式锁,分布式锁

代码实现 StockService:

    /**
     *  解决锁的误删问题
     */
    public void deduct() {
        String uuid = UUID.randomUUID().toString();
        // 加锁setnx
        while (this.redisTemplate.opsForValue().setIfAbsent("lock1", uuid, 20,TimeUnit.SECONDS)) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                // 1. 查询库存信息
                String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
                // 2. 判断库存是否充足
                if (stockStr != null && stockStr.length() != 0) {
                    Long stock = Long.parseLong(stockStr);
                    if (stock > 0) {
                        redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                    }
                }
            } finally {
                // 判断锁是不是被当前线程所持有,是的话,则删除锁
                /*if(uuid.equals(redisTemplate.opsForValue().get("lock1"))) {
                    // 解锁
                    this.redisTemplate.delete("lock1");
                }*/

                // 通过lua脚本来释放锁
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                        "then " +
                        "   return redis.call('del', KEYS[1]) " +
                        "else " +
                        "   return 0 " +
                        "end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList("lock"), uuid);
            }
        }
    }

2.6. 可重入锁

由于上述加锁命令使用了 SETNX ,一旦键存在就无法再设置成功,这就导致后续同一线程内继续加锁,将会加锁失败。

用一段 Java 代码解释可重入:

public synchronized void a() {
    b();
}

public synchronized void b() {
    // pass
}

假设 X 线程在 a 方法获取锁之后,继续执行 b 方法,如果此时不可重入,线程就必须等待锁释放,再次争抢锁。
锁明明是被 X 线程拥有,却还需要等待自己释放锁,然后再去抢锁,这看起来就很奇怪,我释放我自己~

可重入性就可以解决这个尴尬的问题,当线程拥有锁之后,往后再遇到加锁方法,直接将加锁次数加 1,然后再执行方法辑。退出加锁方法之后,加锁次数再减 1,当加锁次数为 0 时,锁才被真正的释放。

2.6.1. 加锁脚本

参照 ReentrantLock 中的非公平可重入锁实现分布式可重入锁: hash + lua脚本

加锁:
1、判断锁是否存在(exists), 不存在则直接获取锁,hset key filed value
2、如果锁存在则判断是否是自己的锁(hexists)
如果是自己的锁则重入:hincrby key field increment
如果不是自己的锁,则重试

// 当锁不存在 或者 持有锁的线程是当前线程时
if (redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1) 
then
    redis.call('hincrby', KEYS[1], ARGV[1], 1);
    redis.call('expire', KEYS[1], ARGV[2]);
    return 1;
else
	return 0;
end

2.6.2. 解锁脚本

解锁
1、判断自己的锁是否存在,不存在则返回nil
2、如果自己的锁存在,则减1(hincrby -1 ),判断减1后的值是否为0
为0则del key并且返回1,表示解锁成功
不为0,则返回0表示解锁失败

if(redis.call('hexists', KEYS[1], ARGV[1]) == 0) then 
    return nil; 
elseif(redis.call('hincrby', KEYS[1], ARGV[1], -1) > 0) then 
    return 0; 
else 
    redis.call('del', KEYS[1]); 
    return 1; 
end;

2、基于redis实现分布式锁,分布式锁,分布式锁

2.6.3. 代码实现

由于加解锁代码量相对较多,这里可以封装成一个工具类:
2、基于redis实现分布式锁,分布式锁,分布式锁

// 生成锁的工厂
@Component
public class DistributedLockClient {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private String uuid;

    public DistributedLockClient() {
        this.uuid = UUID.randomUUID().toString();
    }

    public DistributedRedisLock getRedisLock(String lockName){
        return new DistributedRedisLock(redisTemplate, lockName, uuid);
    }
}

// 锁具体实现
public class DistributedRedisLock implements Lock {

    private StringRedisTemplate redisTemplate;

    private String lockName;

    private String uuid;

    private long expire = 30;

    public DistributedRedisLock(StringRedisTemplate redisTemplate, String lockName, String uuid) {
        this.redisTemplate = redisTemplate;
        this.lockName = lockName;
        this.uuid = uuid;
    }

    @Override
    public void lock() {
        this.tryLock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        try {
            return this.tryLock(-1L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit timeUnit) throws InterruptedException {
        if (time != -1){
            this.expire = timeUnit.toSeconds(time);
        }
        String script = "if (redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1) " +
                "then" +
                "    redis.call('hincrby', KEYS[1], ARGV[1], 1);" +
                "    redis.call('expire', KEYS[1], ARGV[2]);" +
                "    return 1;" +
                "else" +
                "    return 0;" +
                "end";
        while (!this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), getId(), String.valueOf(expire))) {
            Thread.sleep(50);
        }
        return true;
    }

    @Override
    public void unlock() {
        String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 " +
                "then " +
                "   return nil " +
                "elseif redis.call('hincrby', KEYS[1], ARGV[1], -1) == 0 " +
                "then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        Long flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), getId());
        if (flag == null) {
            throw new IllegalMonitorStateException("this lock doesn't belong to you!");
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }


    /**
     * 给线程拼接唯一标识
     *
     * @return
     */
    String getId() {
        return uuid + ":" + Thread.currentThread().getId();
    }
}

2.6.4. 使用及测试

在业务代码中进行使用:

    public void deduct() {
        DistributedRedisLock lock = distributedLockClient.getRedisLock("lock");
        try {
            lock.lock();
            // 1. 查询库存信息
            String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
            // 2. 判断库存是否充足
            if (stockStr != null && stockStr.length() != 0) {
                Long stock = Long.parseLong(stockStr);
                if (stock > 0) {
                    redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                }
            }
        } finally {
            lock.unlock();
        }
    }

测试可重入性:

  public void deduct() {
        DistributedRedisLock lock = distributedLockClient.getRedisLock("lock");
        try {
            lock.lock();
            // 1. 查询库存信息
            String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
            // 2. 判断库存是否充足
            if (stockStr != null && stockStr.length() != 0) {
                Long stock = Long.parseLong(stockStr);
                if (stock > 0) {
                    redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                }
            }
            this.test();
        } finally {
            lock.unlock();
        }
    }

    private void test() {
        DistributedRedisLock lock = distributedLockClient.getRedisLock("lock");
        lock.lock();
        System.out.println("获取到可重入锁");
        System.out.println("执行其他业务代码......");
        lock.unlock();
    }

2、基于redis实现分布式锁,分布式锁,分布式锁

2.7. 自动续期

自动续期: 定时任务 (Time 定时器 ) + Lua脚本

判断自己的锁是否存在(hexists),如果存在则重置过期时间

if(redis.call('hexists', KEYS[1], ARGV[1]) == 1) then 
    redis.call('expire', KEYS[1], ARGV[2]); 
    return 1; 
else 
    return 0; 
end

DistributedRedisLock 实现

public class DistributedRedisLock implements Lock {

    private StringRedisTemplate redisTemplate;

    private String lockName;

    private String uuid;

    private long expire = 30;

    public DistributedRedisLock(StringRedisTemplate redisTemplate, String lockName, String uuid) {
        this.redisTemplate = redisTemplate;
        this.lockName = lockName;
        this.uuid = uuid + ":" + Thread.currentThread().getId();
    }

    @Override
    public void lock() {
        this.tryLock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        try {
            return this.tryLock(-1L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit timeUnit) throws InterruptedException {
        if (time != -1) {
            this.expire = timeUnit.toSeconds(time);
        }
        String script = "if (redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 1) " +
                "then" +
                "    redis.call('hincrby', KEYS[1], ARGV[1], 1);" +
                "    redis.call('expire', KEYS[1], ARGV[2]);" +
                "    return 1;" +
                "else" +
                "    return 0;" +
                "end";
        while (!this.redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, String.valueOf(expire))) {
            Thread.sleep(50);
        }
        // 加锁成功返回之前,开启自动续期
        this.renewExpire();
        return true;
    }

    @Override
    public void unlock() {
        String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 0 " +
                "then " +
                "   return nil " +
                "elseif redis.call('hincrby', KEYS[1], ARGV[1], -1) == 0 " +
                "then " +
                "   return redis.call('del', KEYS[1]) " +
                "else " +
                "   return 0 " +
                "end";
        Long flag = this.redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(lockName), uuid);
        if (flag == null) {
            throw new IllegalMonitorStateException("this lock doesn't belong to you!");
        }
    }

    @Override
    public Condition newCondition() {
        return null;
    }



    private void renewExpire() {
        String script = "if redis.call('hexists', KEYS[1], ARGV[1]) == 1 " +
                "then " +
                "   return redis.call('expire', KEYS[1], ARGV[2]) " +
                "else " +
                "   return 0 " +
                "end";
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                if (redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class), Arrays.asList(lockName), uuid, String.valueOf(expire))) {
                    // 当续期成功了,重新开启一个定时任务
                    renewExpire();
                }
            }
        }, this.expire * 1000 / 3);
    }
}

测试 ,改造stockService

    public void deduct() {
        DistributedRedisLock lock = distributedLockClient.getRedisLock("lock");
        try {
            lock.lock();
            // 1. 查询库存信息
            try {
                Thread.sleep(300000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");
            // 2. 判断库存是否充足
            if (stockStr != null && stockStr.length() != 0) {
                Long stock = Long.parseLong(stockStr);
                if (stock > 0) {
                    redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));
                }
            }
            this.test();
        } finally {
            lock.unlock();
        }
    }

可以看到加锁 10s后,锁过期时间重置为30s
2、基于redis实现分布式锁,分布式锁,分布式锁
【注意】
锁的自动续期和锁的过期释放是不是冲突的?

锁的自动续期和锁的过期释放并不冲突,它们是为了解决不同场景下的问题而设计的。

锁的自动续期:这个机制主要用于处理持有锁的客户端在执行任务时可能因为某些原因导致任务执行时间超过预设值。通过自动续期,可以确保在客户端仍在执行任务时,其他客户端无法获取到该锁。这样就避免了多个客户端同时操作共享资源造成数据混乱或者错误。

锁的过期释放:这个机制主要用于处理持有锁的客户端异常退出(比如崩溃、断电等)导致无法正常释放锁。通过设置一个合理的过期时间,在发生异常情况后一段时间内其他客户端依然可以获取到该锁,并且进行相应操作。

总之,两者都是为了确保分布式系统中对共享资源访问互斥性以及容错性而设计,并不存在冲突关系。文章来源地址https://www.toymoban.com/news/detail-617374.html

到了这里,关于2、基于redis实现分布式锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

    1.加锁的Lua脚本: lock.lua 2.解锁的Lua脚本: unLock.lua 3.将资源文件放在资源文件夹下 4.Java中调用lua脚本 1)获取文件方式 2)lua字符串方式 5.jedis调用Lua脚本实现分布式重试锁 1)引入jedis依赖 2)jedis调用lua

    2024年02月07日
    浏览(26)
  • 一种基于springboot、redis的分布式任务引擎的实现(一)

     总体思路是,主节点接收到任务请求,将根据任务情况拆分成多个任务块,将任务块标识的主键放入redis。发送redis消息,等待其他节点运行完毕,结束处理。接收到信息的节点注册本节点信息到redis、开启多线程、获取任务块、执行任务、结束处理。 1、主节点接收任务请求

    2024年02月11日
    浏览(17)
  • Zookeeper 和 Redis 哪种更好? 为什么使用分布式锁? 1. 利用 Redis 提供的 第二种,基于 ZK 实现分布式锁的落地方案 对于 redis 的分布式锁而言,它有以下缺点:

    关于这个问题,我们 可以从 3 个方面来说: 为什么使用分布式锁? 使用分布式锁的目的,是为了保证同一时间只有一个 JVM 进程可以对共享资源进行操作。 根据锁的用途可以细分为以下两类: 允许多个客户端操作共享资源,我们称为共享锁 这种锁的一般是对共享资源具有

    2024年01月16日
    浏览(29)
  • Redis实战案例14-分布式锁的基本原理、不同实现方法对比以及基于Redis进行实现思路

    基于数据库的分布式锁:这种方式使用数据库的特性来实现分布式锁。具体流程如下: 获取锁:当一个节点需要获得锁时,它尝试在数据库中插入一个特定的唯一键值(如唯一约束的主键),如果插入成功,则表示获得了锁。 释放锁:当节点完成任务后,通过删除该唯一键

    2024年02月13日
    浏览(30)
  • Asynq: 基于Redis实现的Go生态分布式任务队列和异步处理库

    Asynq [1] 是一个Go实现的分布式任务队列和异步处理库,基于redis,类似Ruby的 sidekiq [2] 和Python的 celery [3] 。Go生态类似的还有 machinery [4] 和goworker 同时提供一个WebUI asynqmon [5] ,可以源码形式安装或使用Docker image, 还可以和Prometheus集成 docker run --rm --name asynqmon -p 8080:8080 hibiken/as

    2024年02月14日
    浏览(25)
  • 在Spring中,可以使用不同的方式来实现分布式锁,例如基于数据库、Redis、ZooKeeper等

    在Spring中,可以使用不同的方式来实现分布式锁,例如基于数据库、Redis、ZooKeeper等。下面是两种常见的实现方式: 使用Redis实现分布式锁: 使用自定义注解实现本地锁: 以上是两种常见的在Spring中实现分布式锁的方式。第一种方式使用Redis作为分布式锁的存储介质,通过

    2024年03月17日
    浏览(27)
  • 基于Redis的分布式限流详解

    Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案。 用最简单的话来说: 外部请求是不可控的,而我们系统的负载是有限的,如果没有限流机制,一旦外部请求超过系统承载

    2024年02月04日
    浏览(31)
  • 分布式锁实现(mysql,以及redis)以及分布式的概念

    我旁边的一位老哥跟我说,你知道分布式是是用来干什么的嘛?一句话给我干懵了,我能隐含知道,大概是用来做分压处理的,并增加系统稳定性的。但是具体如何,我却道不出个1,2,3。现在就将这些做一个详细的总结。至少以后碰到面试官可以说上个123。 那么就正式进入

    2024年01月21日
    浏览(31)
  • 基于Redis的分布式锁到底安全吗(下)

    原文链接跳转 - 张铁蕾 今天,我们就继续探讨这个话题的后半部分。本文中,我们将从antirez反驳Martin Kleppmann的观点开始讲起,然后会涉及到Hacker News上出现的一些讨论内容,接下来我们还会讨论到基于Zookeeper和Chubby的分布式锁是怎样的,并和Redlock进行一些对比。最后,我们

    2024年04月10日
    浏览(22)
  • Redis——》实现分布式锁

    推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结——》【SpringBoot】     总结——》【MyBatis、MyBatis-Plus】     总结——》【Linux】     总结——》【MongoDB】    

    2024年02月10日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包