Redis 补充

Redis 为什么快?


参考

  • 内存操作
  • 优化的数据结构
  • 单线程事件循环 + IO 多路复用(命令执行永远单线程,所有原子性)

Redis 架构模式 * 4

  • standalone

  • master/replica 可以 写主读从

    master 会将 写操作同步 到 replica;每次添加 replica 可能会经历 全量复制 的步骤,后续再增量复制

  • sentinel

    解决问题:master 宕机后写失败,只能读 replica。需要需要人工介入重选 master 并重新配置所有 client 的 master IP。sentinel 自动执行这些步骤,实现 主从故障转移

    哨兵其实是一个运行在特殊模式下的 Redis 进程,所以它也是一个节点。哨兵节点主要负责三件事情:监控、选主、通知。

    至少需要三台机器。master * 1 + replica * 2 + sentinel * 3 共 6 台机器(或 3 台机器,一台一个哨兵+一个主/从)

    具体配置

  • cluster

    突破机器的内存限制(垂直扩容),实现水平扩容,每个节点承担部分数据的存储,而且负载均衡

Redis 消息队列 * 3

list, stream, Pub/Sub 三种模式

消息模型有两种

  • 点对点: Point-to-Point(P2P)
  • 发布订阅:Publish/Subscribe(Pub/Sub)

优缺:

  • list 仅支持一生产+一消费(类似 P2P)

    使用 lpush, rpop/brpop 指令

  • pub/sub 可以多个消费者(使用 PUBLISH, SUBSCRIBE)

  • list 没有 ack 机制,可能会丢消息

    解决:使用 B-RPOP-LPUSH <this-list> <back-list> 指令取内容(取之前先存一份备份,确定没问题了再 rpop back-list 里的数据)

  • pub/sub 不支持持久化,也没有 ack

  • Stream 比较完整地实现了消息队列的内容

  • 不是所有客户端都支持三种模式,例如:Jedis, lettuce, Redisson

参考:Redis 消息队列的三种方案(List、Streams、Pub/Sub) 最后的参考链接有好东西

Redis 分布式锁

set <key> <value> nx 返回值为 1 获得锁,而且最好在 value 中存入当前线程的信息,释放锁的时候只允许获得锁的线程释放

还需要考虑死锁的问题,最好设置 TTL,但是又要自动续期,可以使用 Redisson 的 RLock,自带 Watch Dog 监控

参考:分布式锁详解

缓存读取策略

  • cache-aside, Miss 的时候 app 直接去 DB,然后再新增 cache
  • read-through cache, app 直连 cache,cache miss 的时候自己去 DB,对 app 而言没有 DB 的概念

参考:Caching Strategies and How to Choose the Right One 有图,有优缺(类似

缓存一致性 cache consistency

缓存更新的策略

Cache invalidation, Cache 失效

DB 更新之后删除对应的 cache

  • 使用:简单场景,读多写少
  • 缺:cache 失效要查 DB
  • 先删 cache 再更新 db 行不行?不行,在未更新 db 之前,其他读请求重新设置一个 old cache,后面的查询就一直 get old value

Write-through caching

同时更新 cache 和 DB(先 cache)

  • 适用:读多写少
  • 缺:增加请求耗时(因为要同时操作 cache, db)

Write-behind caching

更新 cache,有空的时候异步更新 DB

  • 适用:写多
  • 缺:丢数据风险高

参考

缓存问题

参考

  • 雪崩:大量缓存同时过期 或 Redis 宕机

    • 均匀设置过期时间(加随机数)

    • 互斥锁

      当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁(带超时事件,避免释放失败),保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里),当缓存构建完成后,再释放锁。未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值

    • 双 key 策略

      我们对缓存数据可以使用两个 key,一个是主 key,会设置过期时间,一个是备 key,不会设置过期,它们只是 key 不一样,但是 value 值是一样的,相当于给缓存数据做了个副本。当业务线程访问不到「主 key 」的缓存数据时,就直接返回「备 key 」的缓存数据,然后在更新缓存的时候,同时更新「主 key 」和「备 key 」的数据

    • 后台更新缓存

      业务线程不再负责更新缓存,缓存也不设置有效期,而是让缓存“永久有效”,并将更新缓存的工作交由后台线程定时更新。还有些 问题 要考虑

    • 宕机解决

      • 限流
      • Redis HA
  • 击穿:热点缓存过期(缓存击穿是缓存雪崩的一个子集)

    • 互斥锁
    • 热点数据不设置过期,或即将过期通知后台重设时间
  • 穿透:不在缓存也不在数据库

    为什么发生?

    • 业务误操作,缓存中的数据和数据库中的数据都被误删除了
    • 黑客恶意攻击,故意大量访问某些读取不存在数据的业务

    解决:

    • 限制非法请求(API 入口检验请求参数)
    • 缓存空值或者默认值
    • 使用布隆过滤器快速判断数据是否存在

参考

其他