您当前的位置:首页 > 电脑百科 > 数据库 > Redis

Redisson锁机制源码分析

时间:2023-08-28 14:26:42  来源:今日头条  作者:IT动力

1、了解分布式锁的特性

  • 1、锁的互斥性
    • 也就是说,在任意时刻,只能有一个客户端能获取到锁,不能同时有两个或多个客户端获取到锁。
    • 简单来说,就比如上厕所,一个厕所只有一个坑位,只能一个人上,不能同时两个人或多个人上。
  • 2、锁的同一性
    • 也就是说,锁只能被持有该锁的客户端进行删除(释放锁),不能由其他客户端删除。
    • 简单俩说,就是谁加的锁,就只能谁来解锁。也就是解铃还须系铃人。
  • 3、锁的可重入性
    • 也就是说,持有某个锁的客户端,可以继续对该锁进行加锁,实现锁的续租。
    • 简单来说,就是你上厕所的按时间收费的,时间快到了会按照时间给你续租,而会给你价钱。
    • redisson则会增大的你的续租次数,也就是可重入次数。但绝不收费,因为Redis是开源的嘛。
  • 4、锁的容错性
    • 锁超过了最大续租时间后,会自动释放锁,其他客户端会继续获得该锁,从而防止死锁的发生。
    • 简单来说,比如你上个厕所上了五小时,厕管员觉得不对劲,就来测试,发现你悄悄逃票了,此时测试会自动变成解锁状态,其他人就可以去上了,只是厕管员血亏5块大洋。

2、带着几个特性去看Redisson源码

先回顾一下Redisson加解锁代码如何写的

public TestEntity getById2(Long id){
    RLock lock = redissonClient.getLock("demo2_lock");
    lock.lock(20, TimeUnit.SECONDS);
    index++;
    log.info("current index is : {}", index);
    TestEntity testEntity = new TestEntity(new Random().nextLong(), UUID.randomUUID().toString(), new Random().nextInt(20) + 10);
    log.info("模拟查询数据库:{}", testEntity);
    lock.unlock();
    return testEntity;
}

2.1、关注Redisson.getLock()方法

@Override
public RLock getLock(String name) {
    return new RedissonLock(commandExecutor, name);
}

其实就是创建一个RedissonLock对象, 所以加锁的逻辑就在RedissonLock.lock()中,解锁的逻辑就在RedissonLock.unlock()。

2.2、关注RedissonLock.lock()方法

// RedissonLock.lock()的方法体
public void lock(long leaseTime, TimeUnit unit) {
    try {
        // 调用了lock的重载方法
        lock(leaseTime, unit, false);
    } catch (InterruptedException e) {
        throw new IllegalStateException();
    }
}

关注lock的重载方法

// leaseTime表示最大续时间,unit表示续约时间单位,interruptibly表示是否可以中断
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
    // 获取当前线程的线程ID
    long threadId = Thread.currentThread().getId();
    // 尝试获取锁,结果为null表示此时没有客户端占用锁,绝不矫情,直接拿到锁就返回。
    // 结果ttl>0的话,表示此时已经有了其他不识好歹的客户端暂用了锁,那么就只能绝望的等待了    
    Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
    // lock acquired
    if (ttl == null) {
        return;
    }

    // 等待时订阅一个渠道,如果锁被其他客户端释放了,会通过发布订阅模式在publish上发一个消息,表示锁已经释放了
    CompletableFuture<RedissonLockEntry> future = subscribe(threadId);
    pubSub.timeout(future);
    RedissonLockEntry entry;
    if (interruptibly) {
        entry = commandExecutor.getInterrupted(future);
    } else {
        entry = commandExecutor.get(future);
    }

    try {
        // 我干等这不是办法,我还是要不断去尝试看能不能获取锁
        while (true) {
            ttl = tryAcquire(-1, leaseTime, unit, threadId);
            // 如果TTL为空了,表示获取到了锁,那还等什么,长驱直入就是。
            if (ttl == null) {
                // 结束循环等待
                break;
            }

            // 如果ttl还是大于0的,表示其他客户端真的是过于不识好歹,还不肯释放锁。但好歹还是说了它还要持有错多久。
            if (ttl >= 0) {
                try {
                    // 既然如此,那么我就等待你的时间到达吧,除非我突然有啥事被中断了,否则我就等到你过期
                    entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    // 如果传入了中断标识,直接抛出异常,中断了,干别的事情去
                    if (interruptibly) {
                        throw e;
                    }
                    // 否则还是老老实实的继续等待时间到来
                    entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                }
            }
            // 锁过期时间小于0, 表示那个杀千刀的客户端居然没有设置超时时间,它包场了,这可咋整。
            else {
                // 如果不被中断,那么我也只有无期限的等待下去了,我不希望这个期限是一万年
                if (interruptibly) {
                    entry.getLatch().acquire();
                } else {
                    entry.getLatch().acquireUninterruptibly();
                }
            }
        }
    } finally {
        // 最后,不管如何,我无论如何都要去取消订阅这个publish的消息,因为这会浪费我的精力,这已经是我最后的坚持了。
        // 其实是释放资源
        unsubscribe(entry, threadId);
    }
//        get(lockAsync(leaseTime, unit));
}

关注tryAcquire加锁方法

private Long tryAcquire(long wAItTime, long leaseTime, TimeUnit unit, long threadId) {
    return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}

该方法调用了tryAcquireAsync来实现的,所以我们关注tryAcquireAsync方法,继续跟进。

关注tryAcquireAsync加锁方法

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {
    RFuture<Long> ttlRemainingFuture;
    // 首先判断租约时间是否大于0
    if (leaseTime > 0) {
        // 大于零,调用tryLockInnerAsync获取锁
        ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
    } else {
        // 否则,使用默认的租约时间 追溯下去发现private long lockWatchdogTimeout = 30 * 1000;  也就是30s的租约时间
        ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,
                TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
    }
    // 
    CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {
        // lock acquired
        // 结果为空,如果leaseTime大于哦,更新internalLockLeaseTime为指定的超时时间,并且不会启动看门狗(watch dog)
        if (ttlRemaining == null) {
            if (leaseTime > 0) {
                internalLockLeaseTime = unit.toMillis(leaseTime);
            } else {
                // 使用定时任务,自动续约(使用看门狗(watch dog))
                scheduleExpirationRenewal(threadId);
            }
        }
        return ttlRemaining;
    });
    return new CompletableFutureWrapper<>(f);
}

可以看到,加锁最终会调用tryLockInnerAsync进行加锁,而续约会使用scheduleExpirationRenewal进行续约。

关注tryLockInnerAsync实现真正的加锁逻辑

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
            "if (redis.call('exists', KEYS[1]) == 0) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return nil; " +
                    "end; " +
                    "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                    "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return nil; " +
                    "end; " +
                    "return redis.call('pttl', KEYS[1]);",
            Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}

这里执行了一段lua脚本(整个lua脚本保障原子性),我们将脚本内容复制出来,详细解释一下。

-- KEYS[1] 加锁的对象(也就是我们传入的的锁名称)
-- ARGV[1] 表示锁的过期时间
-- ARGV[2]:UUID+当前线程id
-- 如果锁不存在。 == 0表示不存在 == 1表示存在
if (redis.call('exists', KEYS[1]) == 0) then
    -- 对我自己的锁执行一个incrby(自增,表示锁的可重入次数)操作
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 对key设置一个过期时间(过期时间就是保证锁的容错性)
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回nil, 相当于null, 表示获取锁成功
    return nil;
end ;
-- 继续判断锁名成+UUID+当前线程id是否存在,其实就是判断我自己有没有已经拿到锁(保证锁的可重入性)
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 自己已经持有锁,执行一个incrby(自增,表示锁的可重入次数)操作
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    -- 重新设置过期时间
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回nil, 相当于null, 表示获取锁成功
    return nil;
end ;
-- 都不是,表示已经有其他客户端获取到了锁,此时返回key的过期时间,也就是别人释放锁的时间(但其他客户端可能出现续约,存在会等待更久的可能)
return redis.call('pttl', KEYS[1]);

整个lua脚本保障原子性,从而只会有一个客户端能够获取到锁,这样就保证了锁的互斥性。

打一个断点看获取到的锁信息

 

hash表中的第一个值表示UUID+线程ID,这二个值表示锁的重入次数,如果锁被多次获取,那么这个值就是大于1。

关注scheduleExpirationRenewal实现自动续约

protected void scheduleExpirationRenewal(long threadId) {
    ExpirationEntry entry = new ExpirationEntry();
    ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);
    // 不为空表示已经开启了续约操作    
    if (oldEntry != null) {
        oldEntry.addThreadId(threadId);
    } else {
        // 如果没有开启续约操作
        entry.addThreadId(threadId);
        try {
            // 自动续约
            renewExpiration();
        } finally {
            if (Thread.currentThread().isInterrupted()) {
                cancelExpirationRenewal(threadId);
            }
        }
    }
}

关注renewExpiration()方法

private void renewExpiration() {
    ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
    if (ee == null) {
        return;
    }
    // 创建一个定时任务去实现自动续约
    Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
        @Override
        public void run(Timeout timeout) throws Exception {
            // 获取当前锁的ExpirationEntry 对象。
            ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
            if (ent == null) {
                return;
            }
            // 获取第一个线程ID
            Long threadId = ent.getFirstThreadId();
            if (threadId == null) {
                return;
            }
            // 锁续期
            CompletionStage<Boolean> future = renewExpirationAsync(threadId);
            future.whenComplete((res, e) -> {
                if (e != null) {
                    log.error("Can't update lock " + getRawName() + " expiration", e);
                    EXPIRATION_RENEWAL_MAP.remove(getEntryName());
                    return;
                }
                // 续约成功,递归自己无限续约下去
                if (res) {
                    // reschedule itself
                    renewExpiration();
                } else {
                    // 续约失败,表示锁已释放,取消续约任务
                    cancelExpirationRenewal(null);
                }
            });
        }
    }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); // internalLockLeaseTime / 3表示每隔锁时间的三分之一,去续约一次
    
    ee.setTimeout(task);
}

关注renewExpirationAsync方法

 protected CompletionStage<Boolean> renewExpirationAsync(long threadId) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                    "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                    "return 1; " +
                    "end; " +
                    "return 0;",
            Collections.singletonList(getRawName()),
            internalLockLeaseTime, getLockName(threadId));
}

我们发现,又是一段lua脚本,还是复制出来,格式化后详细解释下代码。

-- KEYS[1] 加锁的对象(也就是我们传入的的锁名称)
-- ARGV[1] 表示锁的过期时间
-- ARGV[2]:UUID+当前线程id
-- 使用hexists判断锁是不是自己持有的, == 1表示是自己持有,== 0 表示被其他客户端持有
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
    -- 重新设置过期时间
    redis.call('pexpire', KEYS[1], ARGV[1]);
    -- 返回1 表示续约成功
    return 1;
end ;
-- 返回0 表示续约失败,也意味着锁已经被释放或者被其他客户端获取了
return 0;

所以续约的逻辑就是,启动一个定时任务,每隔续约时间的三分之一次就执行一次。尝试去续约,续约成功则会一直递归续约下去。续约失败表示锁已被释放,则停止续约任务。

而续约的操作就是,判断是否是自己持有锁,是的话就重新设置过期时间,并且返回1表示续约成功,否则返回0表示续约失败。

2.3、关注RedissonLock.unlock()方法

@Override
public void unlock() {
    try {
        // 其实就是调用了unlockAsync进行解锁
        get(unlockAsync(Thread.currentThread().getId()));
    } catch (RedisException e) {
        if (e.getCause() instanceof IllegalMonitorStateException) {
            throw (IllegalMonitorStateException) e.getCause();
        } else {
            throw e;
        }
    }
    
//        Future<Void> future = unlockAsync();
//        future.awaitUninterruptibly();
//        if (future.isSuccess()) {
//            return;
//        }
//        if (future.cause() instanceof IllegalMonitorStateException) {
//            throw (IllegalMonitorStateException)future.cause();
//        }
//        throw commandExecutor.convertException(future);
}

我们可以看到,会使用unlockAsync方法进行解锁,并且在这里传入了当前的线程ID。

关注unlockAsync方法

@Override
public RFuture<Void> unlockAsync(long threadId) {
    // 调用unlockInnerAsync实现异步解锁
    RFuture<Boolean> future = unlockInnerAsync(threadId);

    // 释放之后再处理一些事情
    CompletionStage<Void> f = future.handle((opStatus, e) -> {
        // 取消(停止)续约任务,这里也会停止watch dog
        cancelExpirationRenewal(threadId);
        
        if (e != null) {
            throw new CompletionException(e);
        }
        if (opStatus == null) {
            IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
                    + id + " thread-id: " + threadId);
            throw new CompletionException(cause);
        }

        return null;
    });

    return new CompletableFutureWrapper<>(f);
}

关注解锁的核心逻辑unlockInnerAsync方法

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +
                    "return nil;" +
                    "end; " +
                    "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
                    "if (counter > 0) then " +
                    "redis.call('pexpire', KEYS[1], ARGV[2]); " +
                    "return 0; " +
                    "else " +
                    "redis.call('del', KEYS[1]); " +
                    "redis.call('publish', KEYS[2], ARGV[1]); " +
                    "return 1; " +
                    "end; " +
                    "return nil;",
            Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}

可以看到,其实又是一段lua脚本,继续复制出来分析一下。

-- KEYS[1] 加锁的对象(也就是我们传入的的锁名称)
-- KEYS[2] 监听该锁的频道 也就是publish要发送锁被释放的频道,用于在锁释放时通知其他客户端可以重新获取锁了
-- ARGV[1]:解锁消息
-- ARGV[2] 表示锁的过期时间
-- ARGV[3]:UUID+当前线程id
-- 先判断自己的锁是不是已经释放了 ==0 表示key不存在了,也就是锁被释放了
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    -- 返回nil,也就是null, 表示释放锁成功
    return nil;
end ;
-- 对锁的重入次数减一  因为重入一次counter会+1,所以释放时每次也只能-1,跟重入次数匹配
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 如果重入次数仍然大于0,续约过期时间
if (counter > 0) then
    redis.call('pexpire', KEYS[1], ARGV[2]);
    -- 返回解说失败
    return 0;
else
    -- 表示重入次数已经为0了,删除锁的key
    redis.call('del', KEYS[1]);
    -- 使用publish发布一个消息,其他订阅了的客户端收到消息,就说明解锁成功了饿、然后可以重新获取锁了
    redis.call('publish', KEYS[2], ARGV[1]);
    -- 返回1 表示解锁成功
    return 1;
end ;
return nil;

其实就是在解锁的时候,已经解锁了直接返回成功,可重入次数没有到0,将会解锁失败,直到可重入次数重新减到0后,开始删除锁的key.

并且此时会使用publish发送一个消息在渠道上,订阅者们订阅到了,就说明锁已经被释放了,然后可以从重新获取锁了。

3、小结

Redisson实现分布式锁,就是使用lua脚本保证原子性和互斥性的。每次都判断是不是自己持有锁,才进行操作,这就保证了同一性。

在加锁时使用incrby对key对应的value值进行自增,减锁时自减实现锁的可重入性。

使用redis的超时自动过期来保证锁的容错性,不会一直锁死下去。所以锁的最大续约时间是防止思索的一个有效的方法。



Tags:Redisson   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
Redisson杂谈,你学到了什么?
一.Redisson 简介Redisson 是一个基于 Netty 通信框架的高性能 Redis 客户端, 实现了分布式和可扩展的 Java 数据结构,提供很多分布式相关操作服务以及大量便利的工具方法,让...【详细内容】
2023-10-16  Search: Redisson  点击:(297)  评论:(0)  加入收藏
Redisson锁机制源码分析
1、了解分布式锁的特性 1、锁的互斥性 也就是说,在任意时刻,只能有一个客户端能获取到锁,不能同时有两个或多个客户端获取到锁。 简单来说,就比如上厕所,一个厕所只有一个坑位...【详细内容】
2023-08-28  Search: Redisson  点击:(259)  评论:(0)  加入收藏
Springboot+Redisson封装分布式锁Starter
我们将分布式锁基于缓存扩展了一版,也就是说本starter即有分布式缓存功能,又有分布式锁功能。而注解版的分布式锁能够解决大多数场景的并核问题,小粒度的Lock锁方式补全其他场...【详细内容】
2023-08-28  Search: Redisson  点击:(254)  评论:(0)  加入收藏
Redisson看门狗机制你了解多少
前言谈到Redisson就不得不说Redis了,一想到Redis就不得不想到并发编程锁机制,一想到锁机制那么就不能不考虑一个很头疼的问题,如何保证原子性的问题,高QPS请求量的系统对每次执...【详细内容】
2023-08-22  Search: Redisson  点击:(195)  评论:(0)  加入收藏
Jedis 与 Redisson 对比有什么优缺点?
Jedis 和 Redisson 都是 Java 开发者广泛使用的 Redis Java 客户端之一。它们能够很好地与 Redis 数据库进行交互,提供丰富的功能和灵活的配置选项。虽然它们都可以用于在 Ja...【详细内容】
2023-05-18  Search: Redisson  点击:(438)  评论:(0)  加入收藏
一文看懂Redisson分布式锁的Watchdog机制源码实现
一、 分布式锁简介分布式锁是一种常见的协调分布式系统的机制,在分布式环境下保证数据的一致性和可用性。分布式锁的实现有很多种方式,其中较为常见的方式是利用Redis实现分布...【详细内容】
2023-04-12  Search: Redisson  点击:(247)  评论:(0)  加入收藏
使用redisson实现分布式秒杀功能
redisson相比原生的jredis具有排队的功能,不一致秒杀时,一时获取锁失败就返回失败。秒杀的原理就是使用redis的分布式锁的功能,保证每次抢购不会出现超卖的情况 1 引入pom...【详细内容】
2022-09-01  Search: Redisson  点击:(466)  评论:(0)  加入收藏
厉害了,原来 Redisson 这么好用
Redis 是最流行的 NoSQL 数据库是解决方案之一,而 Java 是世界上最流行(注意,我没有说“最好”)的编程语言之一。虽然两者看起来很自然地在一起“工作”,但是要知道,Redis 其实并...【详细内容】
2022-06-17  Search: Redisson  点击:(740)  评论:(0)  加入收藏
聊一聊Redis官方置顶推荐的Java客户端Redisson
写这篇的时候,相信有很多朋友还在用Jedis作为Redis的客户端,我不禁有很多问号,Jedis还香吗?如果你早些年说它香我信,但是都2020年了,它真的不那么香了。那为什么还继续使用它呢?大...【详细内容】
2020-10-12  Search: Redisson  点击:(412)  评论:(0)  加入收藏
Redis的三个框架:Jedis,Redisson,Lettuce
Jedis api 在线网址:http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.htmlredisson 官网地址:https://redisson.org/redisson git项目地址:https://githu...【详细内容】
2019-11-11  Search: Redisson  点击:(1118)  评论:(0)  加入收藏
▌简易百科推荐
Redis 不再 “开源”,未来采用 SSPLv1 和 RSALv2 许可证
Redis 官方于21日宣布修改开源协议 &mdash;&mdash; 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause...【详细内容】
2024-03-27  dbaplus社群    Tags:Redis   点击:(11)  评论:(0)  加入收藏
Redis“叛逃”开源,得罪了几乎所有人
内存数据库供应商Redis近日在开源界砸下了一块“巨石”。Redis即将转向双许可模式,并实施更为严格的许可条款。官方对此次变更的公告直截了当:从Redis 7.4版本开始,Redis将在Re...【详细内容】
2024-03-25    51CTO  Tags:Redis   点击:(9)  评论:(0)  加入收藏
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  后端Q  微信公众号  Tags:Redis   点击:(17)  评论:(0)  加入收藏
Redis不再 “开源”
Redis 官方今日宣布修改开源协议 &mdash;&mdash; 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause 开...【详细内容】
2024-03-21  OSC开源社区    Tags:Redis   点击:(8)  评论:(0)  加入收藏
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  编程技术汇    Tags:Redis   点击:(47)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  楼仔  微信公众号  Tags:Redis   点击:(39)  评论:(0)  加入收藏
工作中Redis有哪些好用的运维工具
工作中使用 Redis 时,如果大家公司没有专业运维,可能开发人员就会面临这些运维的工作,包括 Redis 的运行状态监控,数据迁移,主从集群、切片集群的部署和运维等等。本文我就从这三...【详细内容】
2024-02-06  waynaqua    Tags:Redis   点击:(55)  评论:(0)  加入收藏
批量执行Redis命令的四种方式!
前言在我们的印象中Redis命令好像都是一个个单条进行执行的,如果有人问你如何批量执行Redis命令,你能回答的上吗,或者说能答出几种方式呢?最容易想到的是Redis的一些批量命令,例...【详细内容】
2024-01-17  小许code  微信公众号  Tags:Redis命令   点击:(58)  评论:(0)  加入收藏
Redis 实现多规则限流的思考与实践
市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的...【详细内容】
2024-01-03  架构精进之路  微信公众号  Tags:Redis   点击:(109)  评论:(0)  加入收藏
一站式Redis解决方案
Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有模糊条件查询,在面对一些需...【详细内容】
2024-01-01  大雷家吃饭    Tags:Redis   点击:(66)  评论:(0)  加入收藏
站内最新
站内热门
站内头条