redis关于数据一致性问题的总结

为了解决redis缓存一致性的问题,redis供了多种缓存策略,主要包括内存淘汰,过期删除,以及主动更新机制

内存淘汰

     淘汰策略的执行过程:客户端发送一个命令给Redis,比如要写入一个新的键值对或者是其他可能会增大内存使用的命令;Redis收到命令后,首先检查当前已使用的内存量是否超过了预先配置的maxmemory阈值;如果内存使用量超出限制,将按照配置的内存淘汰策略来决定要从内存中删除哪些键值对来释放空间。

策略如下:

 no-eviction

默认策略。如果新的写入操作会导致Redis内存超出设定的最大限制,则该写入请求会被拒绝。只读请求可以正常执行。

allkeys-lru

当写入新数据后的内存超过限定值时,Redis会基于LRU(最近最少使用)从所有key中选择   并淘汰最近最少使用过的key

volatile-lru

与allkeys-lru类似,但在内存紧张时仅针对设置了过期时间的key应用LRU淘汰规则。

allkeys-random

当写入新数据后的内存超过限定值时,从所有key中随机淘汰key,不考虑key的访问频次或过期时间

volatile-ttl

优先淘汰即将过期的key,按照key的剩余生存时间(Time to Live, TTL)决定淘汰顺序,TTL越短的key越可能被淘汰。

allkeys-lfu

当写入新数据后的内存超过限定值时,Redis基于LFU(最少频率使用)从所有key中淘汰使用频率最低的key

volatile-lfu

类似allkeys-lfu。筛选淘汰对象时,仅关注设置了过期时间的key,并依据使用频率进行淘汰。

LRU(Least Recently Used 最近最少使用):在 Redis 4.0 之前,LRU 算法的工作原理是基于一些采样机制来估算哪些键可能是最近最少使用的。当配置了 LRU 淘汰策略后,Redis 在内存不足时会尝试从所有数据集中找出最近最少访问的键,并将其从内存中移除,释放空间给新的数据。


LFU(Least Frequently Used 最不经常使用):Redis 4.0 引入了真正的 LFU 算法支持,LFU 更加关注键的访问频率,而非单纯依赖访问时间。LFU 使用了一个计数器来跟踪每个键的访问频率,当需要淘汰数据时,会选择访问次数最少的键进行淘汰。随着时间推移,LFU 还会逐渐降低访问次数较少的键的权重,防止长期未访问但早期访问频繁的键一直占用内存。Redis 实现的 LFU 算法同样进行了优化,它不是一个纯正的 LFU 实现,而是混合了访问时间和访问频率两个因素,避免了纯粹 LFU 可能存在的“缓存污染”问题。

过期删除


Redis作为一种缓存工具,被广泛应用于存储具有有效期的数据,比如Session管理、限时活动数据等。在这些场景下,数据有一个明确的生命周期,到达一定时间后应当自动失效,不再被访问或使用。**过期删除策略是专门针对这类数据设计的,用于在键值对达到预设的过期时间后自动将其从数据库中移除,无需等待内存达到上限时再由内存淘汰策略处理。**内存淘汰策略主要解决内存资源受限时如何合理释放内存空间,通常是在内存使用达到配置的最大限制时才会起作用,其目的是为了避免Redis因内存耗尽而崩溃,而非根据数据的实际有效期来管理数据。

 定时删除

    key设置了过期时间,一旦过期就立即删除。优点:对内存比较有好  缺点:定时器消耗cpu资源,用时间换空间

惰性删除

性删除
   过期的key不会立即被删除,而是继续保存在内存中,当客户端访问到某个key时,redis会检查key是否过期,如果key过期了,redis不会返回key对应的值,而是直接在访问删除key,并返回nil,表示key不存在。
     优点:资源节省,只有客户端访问key时,才会去检查过期的key,不会因为频繁的检查所有的key的过期时间而占用额外的cou资源。
     缺点:如果有大量的key过期后未被访问,这些过期的key会继续占用内存资源,会导致内存资源的浪费。对于不活跃的key的过期的key,惰性删除无法主动及时的清理,可能会影响到新的数据的存储。

定期删除

 Redis 会定期进行过期键的扫描(默认每秒进行十次),并在扫描过程中随机检查并删除已过期的键。定期删除不是一次性检查所有键,而是通过采样方式来避免一次性扫描整个数据库造成较大的CPU负担。Redis 会动态调整定期删除策略的执行频率,以保持在内存紧张时有足够的驱逐力度,而在内存充足时减少不必要的计算资源消耗。

优点:
会主动扫描并清理过期key,避免过期key长时间占用内存资源,使得Redis可以在资源使用和内存回收之间找到平衡点。
缺点:
需要额外的CPU和时间资源执行扫描和清理工作,如果扫描过于频繁或扫描范围过大,可能会影响Redis服务器的性能。

定时删除和定期删除的异同

Redis 中针对键值对的过期处理机制设计了两种策略来自动删除过期的键:定时删除(Timed deletion)和定期删除(Periodic deletion)。这两种策略各有特点,适用于不同的场景,下面详细说明它们的异同点:

相同点

1. **目的相同**:无论是定时删除还是定期删除,其主要目的都是为了自动清理Redis数据库中已过期的键值对,以释放内存空间,保持数据库的健康运行。
2. **配合使用**:在实际应用中,Redis 会根据情况动态调整策略,可能同时使用这两种机制来达到最佳的过期键处理效果。

 不同点

1. **执行时机**:
   - **定时删除**:在为键设置过期时间的那一刻起,Redis 会在后台监控这个键,一旦键过期,立即将其删除。这是一种即时响应过期事件的方式。
   - **定期删除**:Redis 周期性地检查数据库中的键,判断它们是否已过期,并删除那些已过期的键。这种策略是周期性触发检查和删除操作,而非每个键过期时立即执行。

2. **资源消耗**:
   - **定时删除**:如果大量键在同一时间或短时间内集中过期,可能会引发高频率的删除操作,从而增加CPU负担,影响Redis的响应速度和处理能力。
   - **定期删除**:相对而言,定期删除能更均匀地分散删除操作带来的资源消耗,避免瞬时高负载,但可能会导致已过期的键在一段时间内未被删除,占用额外的内存空间。

3. **实现机制**:
   - **定时删除**依赖于Redis内部的事件驱动机制,通过在键上设置过期时间后,等待时间事件触发删除操作。
   - **定期删除**则是通过后台线程周期性地执行过期键检查任务来实现,比如使用 `activeExpireCycle` 函数按批次检查并删除过期键。

总结

- 定时删除策略提供了较低的延迟,能够快速回收内存,但可能在高并发过期场景下引起性能问题。
- 定期删除策略较为温和,对系统性能的影响较小,但在过期键的处理上可能存在一定的延迟,导致内存占用较高。

在实际应用中,Redis通常结合两者,使用惰性删除(Lazy deletion,即访问时检查是否过期并删除)作为补充,以平衡性能与内存管理的需求。

主动更新


     当应用Redis作为缓存时,数据源通常是持久化的数据库,而数据库中的数据可能因为用户的写操作而发生变化。如果不采取措施,Redis缓存中的数据就会与数据库中的数据产生不一致,进而导致读取缓存时获取到的是过期或无效的数据。**主动更新旨在解决这一问题,确保当数据库的数据发生变化时,Redis缓存中的对应数据也能及时更新,保持与数据库的一致性。**主动更新大致分为:Cache Aside Pattern(旁路缓存模式)、Read/Write Through Pattern(直写式/读写穿透)、Write Behind Caching Pattern(异步缓存写入)

Cache Aside Pattern

最经典常用的缓存策略,适用于读多写少的场景。该策略中,缓存和数据库独立运作,应用程序负责维护二者之间的数据同步。
写操作时,当应用程序需要更新数据时,先更新数据库,然后删除相应的Redis缓存。
读取时,先读缓存,缓存存在直接返回;缓存不存在则从数据库中重新加载最新数据并将其存在缓存中。
优点:
结构简单,易于实现和理解;
对数据库压力小,只在缓存失效时才读取数据库;
缓存和数据库解耦,适合复杂的分布式系统。
缺点:
数据更新后,缓存的更新是异步的,存在短暂不一致窗口期;
需要额外代码来管理缓存的更新和失效;
高并发写入场景下,可能会频繁引发缓存失效,导致大量的缓存重建请求。

Read/Write Through Pattern

读写穿透。在该策略中,读写操作均通过缓存层完成,缓存层负责与数据库的交互,保证缓存和数据库的数据一致性。
写操作时:当应用程序发起请求,先查找Redis缓存,如果缓存中有相应数据,先更新缓存,然后同步更新到数据库。如果缓存中没有数据,直接更新数据库,并将更新后的数据放入缓存中。
读操作时:应用程序发起读请求,缓存命中直接返回缓存数据,缓存未命中时,从数据库读取数据,并将数据存入缓存后再返回给客户端。
优点:
写操作同时更新缓存和数据库,保持二者同步;
减轻了应用程序处理缓存更新的复杂度。
缺点:
每次写操作都需要同时操作缓存和数据库,增加了写操作的延迟和网络负载;
如果缓存层和数据库层同时出现问题,可能会影响到整个系统的可用性;
对缓存服务要求搞,需要缓存服务能够很好地与数据库服务协同工作。

Write Behind Caching Pattern

异步缓存写入,该策略主要减少对数据库的直接写操作,提高写性能。
写操作时,应用程序先将缓存更新写入Redis缓存中,缓存系统将这些更新暂存,并在一个合适的时间(例如,达到一定数量的更新或者一定时间间隔后)批量将写入数据库中;
读操作时,先读缓存,缓存存在直接返回;缓存不存在则读数据库,然后把读取的数据库数据存入缓存。
优点:
性能最好,通过批处理方式异步写入数据库,减少数据库操作次数和网络交互;
减少了对数据库的压力,适合高并发写入场景。
缺点:
数据库可能存在短暂不一致,即缓存中的数据可能比数据库的新;
如果在数据异步会写到数据库前缓存挂掉,可能会丢失这部分更新;
如果系统故障恢复期间,需要处理缓存和数据库间的不一致状态。

缓存更新策略的实现方式

先更新缓存,再更新DB

并发写场景下,如果所有线程都是先更新缓存再更新DB,线程A在更新缓存后还未完成对DB的更新时,线程B可能开始更新相同的缓存和DB。这时,如果线程B的更新由于线程A完成,那么即使线程A最终完成了数据库的更新,也会导致缓存种存储的数据与DB种的数据不一致。
先更新DB,再更新缓存
并发写情况下,所有线程都是先更新DB后更新缓存。如果线程A在更新DB后,线程B可能在同一时间段内完成DB更新并开始更新缓存。此时,线程A在更新缓存时,可能会覆盖线程B刚刚更新的缓存内容,从而导致缓存与DB的不一致。
先删除缓存,再更新DB
并发写场景下,所有线程都是先删除缓存再更新DB。这种方法可以确保在更新数据库前缓存已经被清空,因此不会出现缓存和DB的数据不一致。

并发读写场景,可能存在这样的情况:线程A在删除缓存后,线程B在读取不到缓存的情况下查询DB并将旧数据写入缓存,随后线程A才更新DB,此时缓存中的数据就会落后于DB。

先更新DB,再删除缓存


并发写场景下,所有线程都是先更新DB再删除缓存,无论哪个线程先更新DB再删除缓存,缓存都会被删除,不会导致缓存和DB不一致。例如:假设线程A先更新DB,紧接着线程B也更新DB,然后线程B删除缓存,线程A删除缓存
并发读写场景下,线程A在更新DB后,线程B可能在缓存被删除前读取到旧缓存数据并以此为基础更新缓存,导致缓存中存储的是旧数据。


延迟双删


为应对并发读写场景下的数据不一致问题,提出了延迟双删策略。延迟双删是基于先删除缓存再更新DB的基础上的改进:在更新DB后延迟一定时间,再次删除

缓存。在该策略中,受限删除缓存,然后更新DB,并在完成DB更新后经过一段延迟再次删除缓存。这段延迟是为了确保在第二次删除缓存时,所有读取请求都能够查询到已更新的数据库内容,从而在重新加载缓存时能够保持数据一致性。

并发读写场景下,假设线程A执行写操作,线程B执行读操作。线程A先删除缓存,线程B查询,未命中,线程B再查询DB,线程B根据查询的DB结果更新缓存,线程A更新DB,线程A延时删除缓存。线程A第一次删除缓存后,线程B根据查询的DB结果更新缓存,此时查询得到的结果是旧数据,线程A延迟第二次删除缓存后,后续查询DB,重新写入缓存,不会导致缓存和DB数据不一致

虽然该方法不能提供强一致保证,但能够在很大程度上减少缓存与DB不一致的情况。

异步删除缓存


异步更新缓存是基于先更新DB再删除缓存的基础上的改进,通过引入消息队列,更新DB后异步地执行缓存删除操作。
并发写场景下,多个线程地删除操作会被添加到消息队列中依次执行。线程A先更新DB,线程B再更新DB,线程B把删除缓存放入消息队列中,线程A紧接着把删除缓存放入消息队列中,最后消息队列删除缓存
并发读写场景下,线程A更新DB,线程B查询缓存,命中返回,线程A把删除缓存放入消息队列,最后消息队列删除缓存。其实,异步删除缓存期间,读线程获取的缓存是旧数据,短暂出现数据不一致,异步删除缓存后最终会一致。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis是一个内存数据库,它以键值对的形式存储数据。由于数据存储在内存中,Redis具有快速读写的特性,但也带来了一些数据一致性问题Redis数据一致性主要涉及两个方面:持久性和复制。 1. 持久性:Redis提供了两种持久化数据的方式,分别是RDB(Redis Database)和AOF(Append Only File)。 - RDB是一种快照的方式,将Redis在某个时间点的数据保存到磁盘上,可以通过加载RDB文件来恢复数据。RDB具有较高的性能,但可能会丢失最后一次持久化之后的数据。 - AOF是一种日志追加的方式,将Redis的操作以追加的形式保存到日志文件中,可以通过重新执行AOF文件中的操作来恢复数据。AOF具有较好的数据一致性,但对于写入操作来说,相对于RDB会有更高的性能开销。 通过配置合适的持久化策略,可以根据业务需求来平衡数据一致性和性能。 2. 复制:Redis支持主从复制,在主节点上进行写操作,然后将写操作同步到从节点上。复制可以提高系统的可用性和读取性能,但也会引入数据一致性问题Redis的复制是异步的,主节点将写操作发送给从节点之后,不会等待从节点的确认,而是继续处理后续的操作。这意味着主从节点之间可能存在数据不一致的情况,从节点可能会落后于主节点一段时间。 为了提高数据一致性,可以使用Redis提供的复制延迟监控机制来监控主从节点之间的延迟,并采取相应的措施来保证数据一致性总结起来,Redis数据一致性可以通过合适的持久化策略和监控机制来保证,根据业务需求和性能要求选择适合的方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值