• redis复习总结


    本文主要记录在换工作期间对redis的复习和总结

    参考资料

    • Redis在公司项目实践
    • 《Redis实践》

    常见问题

    1. redis的数据结构
    2. redis为什么这么快
    3. redis用什么命令进行排序
    4. redis事务模型
    5. redis持久化方式 优缺点 [AFO可以设置的选项有那些]
    6. redis默认持久化方式是什么
    7. redis如何实现分布式锁?
    8. redis如何实现消息队列 有何缺点
    9. redis的发布订阅模型
    10. 简单讲下redis主从复制启动过程
    11. redis如何清除过期key[redis过期策略/内存淘汰机制]
    12. redis单机和集群模式下的差异
    13. 简单讲下redis的批处理

    项目实践

    1. 项目中redis如何部署的
    2. redis设置的持久化方式
    3. 如何保障数据库和缓存的一致性
    4. 项目中会用到批处理吗
    5. 说下你们redis的架构有什么优缺点
    6. 你知道哪几种redis项目架构
    7. 你们用到了多数据库机制吗
    8. 缓存穿透和缓存雪崩如何处理
    9. 和memcached 的差异

    答案

    redis的数据结构:

    string list hash set zset

    redis为什么这么快

    单线程多路IO复用模型 纯内存交互无线程切换开销

    redis事务模型

    使用命令watch & multi & execRedis 参考了 CAS(Compare And Swap)乐观锁.

    可以在 multi 命令之前使用 watch 命令监控某些键值对,然后使用 multi 命令开启事务,数据操作命令就会进入队列。redis收到exec 会检测watch监控的是键值对,无变化,执行,提交事务;如果发生变化,那么它不会执行任何事务中的命令,事务回滚。无论事务是否回滚,Redis 都会去取消执行事务前的 watch 命令.

    redis 在接受到multi 只是收到事务信号,不会开始执行命令,当收到exec时候才会一次性执行被multi和exec包裹的命令。

    redis持久化方式 优缺点

    持久化方式有RDB & AFO

    • RDB的提供2种持久化命令 BGSAVESAVE 都会调用rdbSave.

    SAVE会阻塞主进程,在快照保存完成前无法处理客户端的任何请求
    BGSAVE 会fork出来一个子进程来调用rdbSave,不会干扰主进程。缺点是子进程处理较慢,随着redis的内存占升高,创建子进程耗时会增加可能会导致redis的长时间不可用。

    解决办法: 关闭自动保存,在合适的时间自己调用SAVE速率较高,但是会有小段时间的数据丢失。

    • AOF append only fileaof持久化会把被执行写命令写道aof文件的末尾可以记录所有数据变化。恢复数据时候只需要执行一次aof文件即可。

    可以设置的参数: always[每条同步一次]/everysec/[每秒一次]/no[系统决定何时写入 类似Java write]

    缺点是aof文件可能会过大,需要设置定期重写BG write AOF[距离上次重写增大的% 或者距离上次重写的时间]

    redis实现分布式锁

    使用SETNX命令[set if not exsit 不存在就设置] ,同时使用expire来设置超时时间。为了防止在设置超时时间时候客户端崩溃导致设置失败,每个客户端在获取锁时候,需要检测下锁是否设置了过期时间,给未设置过期时间的锁设置时间,避免无法释放锁。

    简单讲下redis主从复制启动过程

    置主从使用配置 SLAVE of host port

    1. 从服务器连接主服务器发送SYNC命令
    2. 主服务器开始执行BGSAVE命令,并使用缓冲区记录快照生成期间的命令
    3. BGSAVE执行完成主服务器通过SCP向从服务器发送快照文件
    4. 从服务器丢弃老数据,开始执行快照的写入
    5. 从服务器完成对快照的解释,开始接受新得请求,同时接收主服务器缓冲区发来的写命令。
    6. 主服务缓冲区命令同步完成开始正常执行接受写命令,没接收一次给从服务器同步一次。

    redis如何清除过期key

    redis默认过期策略 惰性删除 + 定期删除

    支持的过期策略有

    • 定时删除:给每个设置了过期时间的都配置上定时器
    • 定期删除:key的定期删除会在Redis的周期性执行任务(serverCron,默认每100ms执行一次)中进行,而且是发生Redis的master节点,因为slave节点会通过主节点的DEL命令同步过来达到删除key的目的。依次遍历每个db(默认配置数是16),针对每个db,每次循环随机选择20个(ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)key判断是否过期,如果一轮所选的key少于25%过期,则终止迭次,此外在迭代过程中如果超过了一定的时间限制则终止过期删除这一过程。
    • 惰性删除:查询这个数据的时候再判断是否过期需要删除。

    内存淘汰策略通过maxmemory-policy volatile-lru配置

    redis 内存淘汰机制(内存淘汰策略)有以下几个:

    • noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
    • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
    • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
    • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
    • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
    • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

    如果redis中不存在设置了过期时间的key 那么配置volatile-lru-random-ttl 等效于noeviction 。新加入的数据会报错。

    redis的批处理

    mset和mget 因为我们的项目使用的 Redis Cluster的集群,不同的key会被
    CRC16(key) Mod 16384 到不同的slot上去,直接使mset和mget是不太好的,所以我们一般不使用批处理。

    你知道那些redis的高可用方案

    redis哨兵模式 Replication+Sentinel

    使用sentinel 对主从进行监控,和自动故障转移,并且提供一个vip给client进行访问,当出现故障的时候,自动踢掉Mster节点,并在slave中选择新。

    Sentinel的作用有三个:

    监控:Sentinel 会不断的检查主服务器和从服务器是否正常运行。
    通知:当被监控的某个redis服务器出现问题,Sentinel通过API脚本向管理员或者其他的应用程序发送通知。
    自动故障转移:当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系 的其中一个从节点升级为新的主节点,并且将其他的从节点指向新的主节点。

    缺点: 主从切换会有部分数据丢失且 不支持扩容,单机内存限制集群上线。

    redis哨兵模式+proxy

    单纯哨兵模式的升级版 在vip后加入了proxy层 均衡到后端多个master节点。可以支持水平扩容,但运维困难。

    redis Cluster

    当前我们项目就使用的这种 关键算法 crc16(Key) mod 16384

    项目的持久化方式

    master节点关闭持久化
    slave开启 AOF 也可以开启AOF&RDB

    缓存击穿/缓存雪崩

    击穿

    1. 使用锁,当缓存失效时候,先取锁,拿到锁访问数据库,更新缓存。没获取锁返回空。
    2. 在访问缓存前先校验key是否有效,例如使用布隆过滤器,将所有可能的查询key 先映射到布隆过滤器中,查询时先判断key是否有效,有效继续向下执行,无效直接返回。

    雪崩

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

    Redis和 memcached 的区别

    1. redis可以持久化 memcached数据全在内存中断电就是失效。
    2. redis的支持的数据类型更多,memcached只支持k-V
    3. redis的单个value大于memcached

    以下是面试以来被问到过的题,当然也包括一些没被问的题,有备无患。

    被问到过的

    Redis跳表

    链表加上多级索引的结构,称为跳表,应用在Redis ZSet数据结构当中。ZSet要求数据唯一有序,且支持插入删除。

    实现

    跳表包含zskiplistNode和skiplist两个结构定义。

    1. zskiplistNode表示跳表节点。
    2. zskiplist保存跳表节点的信息。
      • 节点的数量,
      • 表头节点和表尾节点的指针等等。

    参考文章

    1. Redis-跳跃表实现
    2. MySQL索引和跳表

    缓存和数据库一致性

    常见的四种更新策略

    1. 先更新数据库再更新缓存

      • 线程安全角度:2线程同时更新统一条数据,然后再更新缓存的时候无法保障缓存被更新的时间和数据库更新的时间一致。
    2. 先更新数据库再删缓存 对比操作1上增加了删除操作,可能会删除非脏值,但是不影响一致性。

      • 步骤
        1. 查询:缓存空,查数据库放入缓存。/非空 直接查询缓存拿到数据。
        2. 更新:更新数据库,删除缓存。
      • 极端情况:A读B写 针对统一条数据 此时缓存失效。
        1. A缓存读不到 去数据库拿到旧值。
        2. B写数据库。
        3. B删除缓存。
        4. A将数据库的旧值写入缓存。
      • 极端情况解决:
        1. 要求B写操作比A读操作更快,不现实。
        2. 可在每一次写操作后再加上一次异步删除 双删保平安。
    3. 先删除缓存再更新数据库

      • 若A写数据B读数据,A删除缓存时候B读数据 此时B会读数据库更新缓存。出现脏数据。
      • 解决办法:加上休眠时间。A写完数据库后1S再次删除缓存,但是在休眠时间内依然会出现脏数据。为避免吞吐量降低可以使用异步删除,队列或者异步线程均可。
    4. 先更新缓存再更新数据库

      • AB更新同一条数据 无法保证更新数据库的顺序和更新缓存的顺序一致,且更新缓存成功更新数据库失败无法容错。

    Redis cluser 集群原理

    集群扩容 & 新增节点

    扩容对应着集群新增节点,步骤如下:

    1. 启动redis节点 加入集群设置为Master节点
    2. 为当前增加的master节点增加slave节点[master节点没分配slot前不会实际进行工作]
    3. 准备slot的数据迁移。可以选择 all所有数据源 nodeId指定数据源。也就是reShared操作。
    4. 迁入数据slot状态设置为: IMPROTING
    5. 迁出数据slot状态设置为:MIGRATING
    6. 数据迁移完成,广播更新slot和nodeId的映射信息,扩容完成。

    以上4&5顺序不能乱。
    假设我们有两个 Redis 主节点,分别称为 A 和 B。我们想将哈希槽 8 从 A 移动到 B,因此我们发出如下命令:

    • 迁入节点B: CLUSTER SETSLOT 8 IMPORTING[from] A 在目标节点设置 迁入slot 从源节点。
    • 迁出节点A:CLUSTER SETSLOT 8 MIGRATING [to] B 在迁出的节点设置 移动slot到目标。

    若顺序乱且迁出设置迁入还未设置: 客户端访问A对应slot 重定向到B B对应slot找不到数据,且未设置 IMPORTING。会返回moved A & slot.形成死。

    正在扩容的节点如何提供数据访问

    迁出的节点

    1. 迁出的slot状态为:MIGRATING
      若待查询数据存在,正常返回,若查询数据正在migrate状态当中,会阻塞等命令结束继续处理客户端请求,若待查询数据不存在,返回ASK以及目标节点的信息。此时客户端会带上ASKING信息去查询目标节点。所有不存在的查询都会重定向到MIGRATING的slot上。

    2. 目标slot状态为:IMPORTING
      节点会接受当前slot所有携带ASKING的查询。若查询是客户端发起,正常返回,若查询非客户端发起,则会返回MOVED&对该次查询绑定真实slot的目标节点。

    MOVED & ASK 差异

    1. 如果收到 ASK 重定向,则只发送被重定向到指定节点的查询,但继续向旧节点发送后续查询。
    2. 使用 ASKING 命令启动重定向查询。ASKING 命令在客户端上设置一个一次性标志,强制节点为有关 IMPORTING 插槽的查询提供服务。
    3. 不要更新本地客户端表以将 slot 映射到新的节点上。
         
      从A->B 迁移到B
      一旦哈希槽 8 迁移完成,A 将发送一条 MOVED 消息,客户端可以将哈希槽 8 永久映射到新的 IP 和端口对。请注意,如果有问题的客户端更早地执行映射,这不是问题,因为它不会在发出查询之前发送 ASKING 命令,因此 B 将使用 MOVED 重定向错误将客户端重定向到 A。

    客户端更新slot和节点的映射关系

    两种情况下,客户端通常需要获取完整的插槽列表和映射的节点地址:

    1. 在启动时填充初始插槽配置。
    2. 当MOVED收到重定向时。

    客户端可以通过MOVED仅更新当前slot来处理重定向,但这通常效率不高,因为通常会同时修改多个slot映射.

    集群容错 心跳和gossip消息

    心跳机制是指redis cluster节点之间不停的交换ping pong信息,节点发送 ping 数据包,这将触发接收者回复 pong 数据包,节点也可以只发送pong不触发回复,ping 和 pong 数据包的总和称为心跳数据包。

    故障检测

    1. 前提
      集群节点都确保不超过一半NODE_TIMEOUT时间内和其他所有节点ping/pong一次,NODE_TIMEOUT过去之前,节点还尝试重新连接与另一个节点的 TCP 链接,以确保不会因为当前 TCP 连接存在问题而认为节点不可达。
    2. PFAIL状态
      若当前节点ping某个节点在NODE_TIMEOUT时间结束都没有收到回复,则会被计为PFAILpossible fail 可能失败
    3. PFAIL升级为FAIL
      a. 某个节点,我们称之为 A,将节点 B 标记为PFAIL。
      b. 从集群中大多数主节点的角度来看,节点 A 通过gossip消息收集了有关 B 状态的信息。
      c. 大多数主人及时发出信号PFAIL或FAIL条件NODE_TIMEOUT * FAIL_REPORT_VALIDITY_MULT。(在当前实现中,有效性因子设置为 2,所以这只是两倍的NODE_TIMEOUT时间)。
      d. a/b/c满足的情况下 B被A标记为FAIL 向所有可达阶节点传递FAIL消息,且所有可达节点对B的的状态被强制标记为FAIL.

    FAIL状态清除

    1. 该节点已经可达并且是一个从节点。在这种情况下,FAIL可以清除标志,因为从站没有故障转移。
    2. 该节点已经可以访问并且是一个不为任何slot提供服务的主节点。在这种情况下,FAIL可以清除标志,因为没有slot的主节点并没有真正参与集群,并且正在等待配置以加入集群。
    3. 该节点已经可以访问并且是一个主节点,但是很长一段时间(N 倍NODE_TIMEOUT)过去了,没有任何可检测的从节点提升。在这种情况下,最好重新加入集群并继续。

    节点重新加入集群

    1. 当失效节点重新加入集群,且向其他节点发送心跳广播自己对slot的映射关系时,其他节点看到相同slot有当前更新的从属关系时候,UPDATE slot的新配置向 A发送消息,A更新配置。
    2. 当前master 会成为他FAIL前所管理的最后一个slot,现在对应的master节点的slave.
    3. 重新成为slave前会将映射的slot信息逐渐清空。

    选举机制

    选举条件:

    1. master节点处于FAIL状态。
    2. master节点对非零数量的slot提供服务。
    3. slave和master最近一次通讯时间不超过设定时间。

    选举过程:

    1. 一旦 master 处于FAIL状态,slave 会在尝试被选举之前等待一小段时间,避免其他master还没收到当前master的FAIL状态而拒绝投票。
    2. slave 增加计数器currentEpoch,广播FAILOVER_AUTH_REQUEST信息,在2倍NODE_TIMEOUT等待返回。
    3. 在时间内其余master只能给同一master下的其中一个slave回复FAILOVER_AUTH_ACK。
    4. 收到FAILOVER_AUTH_ACK 超过多数的slave将成为新的master.若当前没有任何一个slave得到超过一半选票,那么尝试新的选举,且选举时间为上一次的2倍。

    副本迁移算法

    若某一段slot对应的master失效,slave成为新的master,且当前无新的slave. 集群将尝试从slave最多的master节点当中,将较低节点 ID 的非故障slave迁移为当前master的slave,以保证集群副本的动态平衡。

    参考文档

    1. 官方文档:Redis Cluster Specification
    2. 官方文档:Redis cluster tutorial
    3. 社区沙龙:腾讯会议用户暴涨 Redis集群的无缝扩容
    4. 公司实践:前东家软测项目 Redis实践。
  • 相关阅读:
    linux下用shell删除三天前或者三天内的文件
    linux在shell中获取时间
    mongodb_修改器($inc/$set/$unset/$push/$pop/upsert......)
    java List<Map> 排序问题
    linux crontab定时执行shell脚本
    MongoDB 基础命令——数据库表的增删改查——遍历操作表中的记录
    Windows7下安装MongoDB
    JavaWeb学习总结(十二)——Session
    javaweb学习总结(十一)——使用Cookie进行会话管理
    iOS10 SiriKit QQ适配详解
  • 原文地址:https://www.cnblogs.com/threecha/p/13828157.html
Copyright © 2020-2023  润新知