allkeys-random:从所有键值对中随机选择并删除数据。
allkeys-lru:使用LRU (Least Recently Used)算法在所有数据中进行筛选删除。
allkeys-lfu:使用LFU(Least Frequently Used)算法在所有数据中进行筛选删除。
</ol>c) 不处理:
- noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed whenused memory",此时Redis只响应读操作。
LRU (Least Recently Used)算法:在淘汰时应该优先选择最近最少使用的数据项。LRU算法维护一个使用顺序队列,最近访问的数据项被移动到队列的末尾(redis默认)。
LFU(Least Frequently Used)算法:在淘汰时应该优先选择最不经常使用的数据项。LFU算法维护一个使用频率计数器,记录每个数据项被访问的次数。当存在大量的热点缓存数据时,LFU可能更好。
<a name=" bXTF" rel="noopener">
删除Key的命令会阻塞Redis吗

会!
当你试图删除一个非常大的key时。例如,如果你有一个包含数百万个元素的列表或集合或者很大的String,并且你试图用一个DEL命令来删除它,那么这个操作可能会花费一些时间,并在此期间阻塞其他操作。
为什么当Key非常大的时候redis会阻塞?
Redis是单线程的,这意味着它一次只能执行一个操作。当你要求Redis删除一个大的key时,这个操作可能会花费一些时间。在这个时间内,Redis不能处理其他任何操作,因此会出现阻塞。
这是因为在Redis中,删除一个key需要遍历并释放key所关联的所有内存。如果key关联的数据结构非常大(例如,一个包含数百万个元素的列表或集合),那么遍历和释放内存的过程就会耗费更多的时间。在这个过程中,Redis不能处理其他操作,因此会出现阻塞。
为了避免这种阻塞,Redis 4.0引入了UNLINK命令。当你使用UNLINK命令删除一个key时,Redis会立即返回,并在后台异步删除key。这样,即使你正在删除一个大的key,其他的Redis操作也不会被阻塞。但是,UNLINK命令不能保证立即删除key,如果你需要立即删除key,你仍然需要使用DEL命令。
随机等待
</ol>设置失败时,会根据超时时间生成一个随机等待时间,等待后再次尝试获取锁。这样可以避免多个节点在同一时刻重复请求锁。
成功获取到锁后,会启动一个定时续期线程,周期性地续期锁的超时时间(添加子线程,每十秒确认线程是否在线,如果在线则重设过期时间),以避免锁被自动释放。
这是由于当业务处理时间大于锁的过期时间,需要延长锁的周期,防止在业务处理的过程中锁被释放。
释放锁时,会发送一条Lua脚本,这段脚本可以保证只有锁真正的占有者才能够释放锁。释放时需要判断是否是某个id的值,且占有锁的时间内才可以。(给锁加唯一的ID(UUID))
这是防止一个线程处理完业务,释放锁时释放的是别的线程持有的锁。
获取或释放锁失败时,会重试一定次数,以便在并发情况下能够重试成功。
如果一个节点在指定时间内一直失败,会通过让当前线程睡眠一段时间来避免死锁。
以上加锁、占有、释放、重试的流程,可以保证分布式环境下Redisson锁的安全性和可靠性。
 - 127.0.0.1:6379> SET tuling zhuge EX 120
- oK
- 127.0.0.1:6379> TTL tuling4 (integer)117
复制代码 这种方式有一个问题,就是当加锁是给一组主从节点加锁时,是直接存储在主节点的。由于redis的AP特性,只支持高性能,高可用,不支持高一致性,因此如果此时主节点宕机,从节点没来得及同步主节点的信息,那么会导致锁的加锁失败,因此就有了红锁(Redlock),redlock会将锁强制记录给所有主从节点。
<a name="lPbNw" rel="noopener">
Redlock 红锁
至少三个redis节点,setnx命令,给所有节点加锁,半数以上的节点加锁成功才会加锁成功。
缺点:
- 如果每个节点添加一个从节点,在加锁key的过程中其中一个节点挂了,换成其从节点顶替该节点,那么别的进程就会得不到这个节点的锁。如果要redlock高可用,那么就需要更多的单点服务器。
- redis每秒钟持久化一次,某个节点在持久化的1秒钟时间还没到的情况下,有可能还没加锁成功就挂了,运维重启这个节点,恢复的数据是没有加锁的,这样会导致并发问题。

如果 half 消息发送成功,MQ 收到这个 half 消息后,会返回一个 success 响应给服务 A。
服务 A 接收到 MQ 返回的 success 响应后,开始处理本地的业务逻辑。
提交/commit本地事务
</ol>
- 如果服务 A 本地事务提交成功,则会向 MQ 中发送 commit,表示将 half 消息提交,MQ 就会执行第 5 步操作;
- 如果服务 A 本地事务提交失败,则直接回滚本地事务,并向 MQ 中发送 rollback,表示将之前的 half 消息进行回滚,MQ 接收到 rollback 消息后,就会将 half 消息删除。
- MQ 如果 commit,则将 half 消息写入到磁盘。.
- 如果 MQ 长时间没有接收到 commit 或者 rollback 消息,例如:服务 A 在处理本地业务时宕机了,或者发送的 commit、rollback 因为在弱网环境,数据丢失了。那么 MQ 就会在一定时间后尝试调用服务 A 提供的一个接口,通过这个接口来判断 half 消息的状态。所以服务 A 提供的接口,需要实现的业务逻辑是:通过数据库中对应数据的状态来判断,之前的 half 消息对应的业务是否执行成功。如果 MQ 从这个接口中得知 half 消息执行成功了,那么 MQ 就会将 half 消息持久化到本地磁盘,如果得知没有执行成功,那么就会将 half 消息删除。
- 服务 B 从 MQ 中消费到对应的消息。
- 服务 B 处理本地业务逻辑,然后提交本地事务。
如何保证数据的最终一致性
实现流程说完了,可能你现在有各种各样的疑惑?
Q: half 消息是个啥?
A: 它和我们正常发送的普通消息是一样的,都是存储在 MQ 中,唯一不同的是 half 在 MQ 中不会立马被消费者消费到,除非这个 half 消息被 commit 了。(至于为什么未 commit 的 half 消息无法被消费者读取到,这是因为在 MQ 内部,对于事务消息而言,在 commit 之前,会先放在一个内部队列中,只有 commit 了,才会真正将消息放在消费者能读取到的 topic 队列中)
Q: 为什么要先发送 half 消息?
A: 前面已经解释过了,主要是为了保证服务 A 和 MQ 之间是否能正常通信,如果两者之间都不能正常通信,后面还玩个锤子,直接返回异常就可以了。
Q: 如果 MQ 接收到了 half 消息,但是在返回 success 响应的时候,因为网络原因,导致服务 A 没有接收到 success 响应,这个时候是什么现象?
A: 当服务 A 发送 half 消息后,它会等待 MQ 给自己返回 success 响应,如果没有接收到,那么服务 A 也会直接结束,返回异常,不再执行后续逻辑。不执行后续逻辑,这样服务 A 也就不会提交 commit 消息给 MQ,MQ 长时间没接收到 commit 消息,那么它就会主动回调服务 A 的一个接口,服务 A 通过接口,查询本地数据后,发现这条消息对应的业务并没有正常执行,那么就告诉 MQ,这个 half 消息不能 commit,需要 rollback,MQ 知道后,就将 half 消息进行删除。
Q: 如果服务 A 本地事务执行失败了,怎么办?
A: 服务 A 本地事务执行失败后,先对自己本地事务进行回滚,然后再向 MQ 发送 rollback 操作。
Q: 服务 A 本地事务提交成功或失败后,向 MQ 发送的 commit 或者 rollback 消息,因为网络问题丢失了,又该怎么处理?
A: 和上一个问题一样,MQ 长时间没有接收到 half 消息的 commit 或者 rollback 消息,MQ 会主动回调服务 A 的接口,通过这个接口来判断自己该对这个 half 消息如何处理。
Q: 前面说的全是事务消息的实现流程,这和事务消息如何保证数据的最终一致性有什么关系呢?
A: 有关系。首先,服务 A 执行本地事务并提交和向 MQ 中发送消息这是两个写操作,然后通过 RocketMQ 的事务消息,我们保证了这两个写操作要么都执行成功,要么都执行失败。然后让其他系统,如服务 B 通过消费 MQ 中的消息,然后再去执行自己本地的事务,这样到最后,服务 A 和服务 B 这两个系统的数据状态是不是达到了一致?这就是最终一致性的含义。
而RocketMQ作为一种消息队列,其本身特点是异步、解耦,无法保证服务A和服务B在同一时刻的数据强一致性。它只能保证最终一致性。
目前通过可靠消息来保证数据的最终一致性是很多大厂都采用的方案,基本都是通过 MQ 和补偿机制来保证数据的一致性。(所谓的可靠消息,就是消息不丢失,如何保证 MQ 的消息不丢失,下篇文章会写,这也是面试常考题)
Q: 服务 B 本地事务提交失败了,怎么办?
A: 如果服务 B 本地事务提交失败了,可以进行多次重试,直到成功。如果重试多次后,还是提交失败,例如此时服务 B 对应的 DB 宕机了,这个时候只要服务 B 不向 MQ 提交本次消息的 offset 即可。如果不提交 offset,那么 MQ 会在一定时间后,继续将这条消息推送给服务 B,服务 B 就可以继续执行本地事务并提交了,直到成功。这样,依旧是保证了服务 A 和服务 B 数据的最终一致性。
————————————————
版权声明:本文为CSDN博主「qq_34436819」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:<a href="https://blog.csdn.net/qq_34436819/article/details/114444204" target="_blank" rel="noopener">https://blog.csdn.net/qq_34436819/article/details/114444204
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |