#yyds干货盘点#--redis-2

知识的价值不在于占有,而在于使用。这篇文章主要讲述#yyds干货盘点#--redis-2相关的知识,希望能为你提供帮助。
之前本来准备写篇关于redis的文章,后来种种原因只写了简单的概念介绍(redis-1),这次有时间就接着写完redis的一些东西。基础概念就不再写了,直接翻前面文章。首先大家思考一个问题,为什么我们要使用redis?
我总结了下自己的看法,以下几点供参考:

  • 提升系统整体性能: 一个用户访问数据库第一次的某些数据有些慢,再次访问时可以存放在redis中,众所周知redis操作内存,所以访问会很快。
  • 缓解后端库的压力: 多并发全部直接查询后端库,对库的风险很大,通过先访问redis来查询,直接访问redis的承受压力远高于库,可以缓解后端库的压力。存在问题是数据强一致性,后面再讲吧。
  • 业务处理速度提升: 一个业务系统总是有后台逻辑处理的时间的,很多又是有返回等待需求的,redis可以极大的缓解长时间请求,结果却不变的这种业务类型。可以快速的完成响应,大幅提升业务处理。
我们接着来说redis使用中可能存在的问题:
  • 缓存雪崩问题
  • 缓存穿透问题
  • 缓存和数据库双写一致性问题
  • 缓存的并发竞争问题
缓存雪崩:缓存因部分原因失效,导致大量数据请求疯狂涌入后端数据库,导致数据库响应异常。怎么解决缓存雪崩呢?提供几个思路:
  • 缓存高可用部署,集群模式尽量保证可用性
  • Hystrix是一款开源的“防雪崩工具”,它通过 熔断、降级、限流三个手段来降低雪崩发生后的损失
  • 使用互斥锁,缺点吞吐量明显下降
缓存穿透:缓存本身存在失效时间,过期后大量请求访问不存在数据,导致所有请求直达数据库,响应异常。解决方案:
  • 只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据,互斥锁。
  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
  • 缓存失效为永不过期,缺点当重构缓存时可能存在数据不一致情况,不推荐这种方式,主要看应用容忍情况。
缓存和数据库双写一致性问题:
  • 一致性问题是分布式常见问题,分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。因此,有强一致性要求的数据,不能放缓存。
  • 采取正确更新策略,先更新数据库,再删缓存。
  • 因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。
缓存的并发竞争问题:
简单讲就是同时有多个子系统去set一个key,例如本身key值是1,顺序更新为2,3,4,5,但是由于多并发最后顺序变成5-4-3-2,结果更新结束值变为了2.怎么解决呢?
分布式锁:
  • 分布式锁可以使用redis自身的分布式锁,也可以使用zookeeper,但是一般使用后者,上面举的例子,要求key的操作需要顺序执行,所以需要保存一个时间戳判断set顺序。
系统A key 1 ValueA 7:00
系统B key 1 ValueB 7:05

  • 假设系统B先抢到锁,将key1设置为ValueB 7:05。接下来系统A抢到锁,发现自己的key1的时间戳早于缓存中的时间戳(7:00< 7:05),那就不做set操作了;这种方案的前提是要保证各系统的时间是一样的,如果不能保证可以采用分布式锁+版本号的方式。
【#yyds干货盘点#--redis-2】消息队列:
  • 通过消息中间件进行处理把并行读写进行串行化。把Redis.set操作放在队列中使其串行化,必须的一个一个执行。这种方式在一些高并发的场景中算是一种通用的解决方案。

    推荐阅读