20210605 缓存一致性方案

Posted 陈如水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20210605 缓存一致性方案相关的知识,希望对你有一定的参考价值。

1,为什么要使用redis?

2,为什么redis速度快?

3,redis数据结构和使用场景?

4,redis的删除策略和内存淘汰策略

5,使用redis带来的问题?

1)数据一致性问题,2)缓存穿透问题,3)缓存雪崩问题,4)缓存并发竞争问题

 

1,为什么要使用redis?

性能和并发的角度:从缓存中读取,快速响应请求;使用缓存提高系统并发能力。

 

2,为什么redis速度快?

1)单线程操作redis,避免了频繁的上下文切换;

2)基于内存进行操作;

3)采用了非阻塞I/O多路复用机制;

 

3,redis数据类型和使用场景?

1)String 计数器,楼盘信息存储

2)Map 用户信息,用户id;

3)List集合 消息队列,先进先出特征;

4)Set集合功能:全局去重,通过redis做全局去重,集群模式部署,使用set集合,不太方便。利用交集、并集、差集等操作;

5)sorted set:权重,排行榜,可以按照评分进行排序。 对楼盘的访问频次进行统计,统计访问频次最高的哪些数据。

 

4,redis的删除策略和内存淘汰策略

Redis的过期删除策略(设置了过期时间的key)

1)惰性删除;访问删除。

2)定期删除;如果只采用定期删除策略,会导致很多key到时间没有删除。

过期策略的缺点:如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效,这样,Redis的内存会越来越高。那么就应该采用内存淘汰机制。

 

内存淘汰策略

如果没有设置 expire 的key,把这几种内存淘汰策略背下来。

1)noeviction:[ɪˈvɪkʃn] 当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

4)volatile-lru:[ˈvɒlətaɪl]当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐

 

5,使用redis带来的问题?

缓存不一致问题

产生原因

1)多线程中,写和读是并发的,没法保证顺序。

2)如果是redis集群,或者主从模式,写主读从,由于redis复制存在一定的时间延迟,也有可能导致数据不一致。

实际业务上,我们更多的还是以DB数据为准,这种读取到旧数据的业务影响可能比读取到为未更新到DB的数据影响要小。

更新DB和操作缓存两个动作之间原则上是非原子性的,一个是更新DB,一个是更新redis。但是,通常我们使用一个妥协的方案,类似于分布式事务最终一致性的实现,这里也可以使用消息队列实现最终一致性的消息保证。

如果对数据不一致容忍度比较低,那么建议是采用先更新DB,后淘汰缓存方案。

这里只是简单方案分析,如果复杂点的场景,还需要考虑DB读写分离时,主从数据同步延时导致的缓存不一致。

1)Redis引起的缓存不一致问题(数据一致性问题)。缓存一致性其实是使用Redis造成的问题,这个是数据一致性问题,redis 可以做到最终一致性,而不是强一致性。

Redis保证的是最终一致性,不是强一致性。有强一致性的数据不能放缓存。

保证数据一致性的方案:

(1)先删除缓存,在更新数据库;但是存在事务没有提交,读取到旧数据的问题,高并发情况下这种情况出现的概率很高。

(2)先更新数据库,在删除缓存,但是存在缓存失效的问题;可以为缓存设置过期时间,并且可以使用消息队列进行补偿,保证数据的最终一致性。

针对不用的场景,使用不同的策略保证缓存的最终一致性。

(1)读取数据的场景(读取操作) 先读缓存,缓存不存在从数据库读取,然后在写入缓存响应数据。

(2)更新数据的场景(更新操作)先更新数据库,再删缓存。另外,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。(删除失败,数据不一致,需要补偿机制)

为什么是删除而不是更新缓存?

为什么最后是把缓存的数据删掉,而不是把更新的数据写到缓存里。这么做引发的问题是,如果A,B两个线程同时做数据更新,A先更新了数据库,B后更新数据库,则此时数据库里存的是B的数据。而更新缓存的时候,是B先更新了缓存,而A后更新了缓存,则缓存里是A的数据。这样缓存和数据库的数据也不一致。

解决方案大概有以下几种: 删除失败了怎么处理? 如何保证数据的一致性?

1. 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。

2. 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。

3. 给所有的缓存一个失效期。这样最差的情况是在超时时间内,内存存在不一致。

第三种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。

 

缓存穿透问题

1)使用bloomfilter解决。每秒百万级别的请求,一定要考虑这个问题。请求所携带的Key是否合法有效。如果不合法,则直接返回。

 

缓存雪崩问题

即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

给缓存的失效时间,加上一个随机值,避免集体失效。

如何解决redis的并发竞争key问题?

消息队列,时间顺序

 

https://blog.csdn.net/hukaijun/article/details/81010279

以上是关于20210605 缓存一致性方案的主要内容,如果未能解决你的问题,请参考以下文章

浅析分布式缓存弹性扩容下的一致性哈希算法

redis

什么是惰性属性?

去哪儿网北京Java开发一二HR面全部通过

Hibernate缓存

Django QuerySet缓存和惰性机制