• Redis梳理


    1. Redis

    简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以存写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。

    2. Redis应用场景

    1、热点数据的缓存: 由于redis访问速度块、支持的数据类型比较丰富,所以redis很适合用来存储热点数据。
    2、分布式锁:多并发情况下,使用 setnx和expire(原子性操作下)加锁,以及当线程执行到一定时间还未结束使用守护线程,用来给快要过期的锁“续航”。
    3、做计数器: redis由于incrby命令可以实现原子性的递增,所以可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等。

    3. Redis 支持的数据类型

    String(字符串)、List(列表)、Set(集合)、Zset(Sorted Set:有序集合)、以及Hash(哈希)。
    
    • 1

    4. Redis 持久化之RDB和AOF

    1、Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。
    2、RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差(镜像全量持久化)。
    3、Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中(增量持久化)。
    4、AOF 的数据完整性比RDB高,但记录内容多了,会影响数据恢复的效率。
    5、Redis 针对 AOF文件大的问题,提供重写的瘦身机制。
    6、若只打算用Redis 做缓存,可以关闭持久化。
    7、若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    AOF的重写机制:
       AOF的工作原理是将写操作追加到文件中,文件的冗余内容会越来越多。所以聪明的 Redis 新增了重写机制。当AOF文件的大小超过所设定的阈值时,Redis就会对AOF文件的内容压缩。
      重写的原理:Redis 会fork出一条新进程,读取内存中的数据,并重新写到一个临时文件中,最后替换旧的aof文件。

    5. Redis 持久化的作用

    用于快速恢复数据。Redis为了内部数据的安全考虑,会把本身的数据以文件的形式保存在硬盘中一份,在重启之后会自动把硬盘的数据恢复到内存(redis)里面。
    
    • 1

    6. Redis跳跃表

    跳跃表:将有序链表改造为可以支持链表式“二分查找”算法,可以快速的进行插入、删除、查找。
    
    • 1

    跳跃表查找节点的过程(以插入元素为例,删除、查找的过程是一样的)
      1、从head开始,根据forward指针(箭头)向前查找,如果前一个元素大于待查找的元素或者遇到tail指针(红色线情况),下移层次继续查找(红色线头节点为起始点);如果下一个元素不大于待查找的元素,forward向前推进一个节点,继续比较。
      2、重复1步骤,直到遇到的前一个节点的值大于待查找的值
      最终总是能找到比待查找节点的值大的前一个位置,在这个位置插入元素。
      跳跃表插入数据时,层级为投硬币的方式随机生成level高度,然后找到前置节点进行插入(比如22,1层)。
      其基本原理就是从最高层的链表节点开始,如果比当前节点要大和比当前层的下一个节点要小,那么则往下找,也就是和当前层的下一层的节点的下一个节点进行比较,以此类推,一直找到最底层的最后一个节点,如果找到则返回,反之则返回空。
    在这里插入图片描述

    7. Redis内存满了如何清理

    1、通过配置文件配置
      通过在Redis安装目录下面的redis.conf配置文件中添加以下配置设置内存大小。

    maxmemory 100mb
    
    • 1

    2、内存淘汰策略
      内存淘汰策略相当于清除掉那些占用内存并且使用不太频繁的数据,淘汰掉这些不活跃数据来清理内存。
    1)LRU 剔除最近最少使用

    volatile-lru:从全数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
    allkeys-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
    
    • 1
    • 2

    2)LFU剔除最近使用频率最低

    volatile-lfu:从全数据集(server.db[i].dict)中挑选最近使用频率最低的数据淘汰
    allkeys-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最近使用频率最低的数据淘汰
    
    • 1
    • 2

    3)Random随机剔除

    volatile-lfu:从全数据集(server.db[i].dict)中随机数据淘汰
    
    allkeys-lfu:从已设置过期时间的数据集(server.db[i].expires)中随机数据淘汰
    
    • 1
    • 2
    • 3

    4)其他

    noeviction:默认策略,不驱逐,不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。
    volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
    
    • 1
    • 2

    8. Redis6.0之后支持多线程,开启多线程后,是否会存在线程并发安全问题?

    不会,Redis 的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。
    
    • 1

    9. Redis使用的I/O多路复用epoll

    I/O多路复用作用:

    1、阻塞 I/O 只能阻塞一个 I/O 操作,而 I/O 复用模型能够阻塞多个 I/O 操作,所以才叫做多路复用。
    2、可以同时监听多个网络连接 IO, 相对于多进程、多线程切换的开销问题,IO 多路复用可以极大的提升系统效率。

    I/O多路复用: 单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流 。select, poll,
    epoll 都是I/O多路复用的具体的实现。

    select, poll, epoll三者区别

    1、底层数据结构
      select:数组,poll:链表,epoll:红黑树。
    2、支持一个进程所能打开的最大连接数  
      select 单个进程所能打开的最大连接数有1024(x86)或2048(x64)。poll无上限。epoll 虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。
    3、FD剧增后带来的IO效率问题
      Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
    4、消息传递方式
      select/poll 内核需要将消息传递到用户空间,都需要内核拷贝动作。epoll通过内核和用户空间共享一块内存来实现的。

    10.如果Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,生产环境下,如何将它们全部找出来?

    redis的命令是单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了。但是整体所花费的时间会比直接用keys指令长。

    11. 什么是缓存穿透,缓存雪崩,缓存击穿?如何解决或预防?

    (一)缓存穿透

    访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上.此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被“穿透”了一样,起不到任何作用。
    解决预防方案
      1、接口校验: 在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法性校验等,例如商品查询中,商品的ID是正整数,则可以直接对非正整数直接过滤等等。
      2、缓存空值: 当访问缓存和DB都没有查询到值时,可以将空值写进缓存,但是设置较短的过期时间,该时间需要根据产品业务特性来设置。
      3、布隆过滤器
       ① 应用场景
       · 在爬虫时,对爬虫网址进行过滤,已经存在布隆中的网址,不在爬取。
       · 垃圾邮件过滤,对每个发送邮件的地址进行判断是否在布隆的黑名单中,若是在就判断为垃圾邮件。
       · 缓存穿透。
       ② 特性
       布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
       ③ 原理
       在一个足够大的足够大的 bitmap中,插入时与多个不同的hash函数生成多个哈希值,将对应位置置1。查找时如果发现所有hash函数对应位都是1说明存在,只要有一个为0则认为这个元素不存在(hash冲突会导致一定的误识别率)。

    (二)缓存雪崩
    	指在某一个时间段,缓存集中过期失效。Redis宕机。
    
    • 1

    解决预防方案
      1、优化缓存过期时间: 设计缓存时,让过期时间尽量均匀,避免大量的 key 在同一时刻同时失效,造成缓存雪崩。
      2、集群: 可以把缓存层设计成高可用的,即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务。利用sentinel或cluster实现。
      3、构建多级缓存架构: nginx缓存 + redis缓存 +其他缓存(ehcache等)。

    (三)缓存击穿

    指一个热点key被大并发量请求,热点key突然失效时,大批量请求涌向数据库,导致数据库的压力瞬间过大。
    解决预防方案
      1、预先设置热门数据: 在Redis高峰访问之前,把一些热门数据提前存入Redis中,并且加大这些热门数据key的时长。
      2、互斥锁: 用分布式锁控制访问的线程,其他线程等待,等到抢到锁的线程从数据库中加载数据完毕。
      3、本地缓存: 把缓存数据取出时直接加载到本地缓存(Ehcache、Guava Cache),这样访问热key时就可以直接访问自己缓存了。

    12. Redis的三种集群

    1、主从模式: 可以一主多从,主从数据同步保证数据完整性,且可以实现写主、读从,性能有所提升,但主节点故障后写受影响,没有故障选举功能,且无监控各主从运行状态功能。
    2、哨兵模式: 哨兵(sentinel)监控各节点运行状态,主节点发生故障后长(经历主观下线和客观下线),可以通过再次选举产生主节点,实现故障恢复,但若从节点挂了不能实现从节点的故障转移;  
      ① 选举机制:
      如果主节点被判定为客观下线之后,就要选取一个哨兵节点来完成后面的故障转移工作,选举出一个leader的流程如下:
      a)每个在线的哨兵节点都可以成为领导者,当它确认(比如哨兵3)主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
      b)当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;
      c)如果哨兵3发现自己在选举的票数大于等于num(sentinels)/2+1以及quorum数时,将成为领导者,如果没有超过,继续选举。  
      ② 哨兵sentinel工作原理  
      · Sentinel状态持久化
    snetinel的状态会被持久化地写入sentinel的配置文件中。每次当收到一个新的配置时,或者新创建一个配置时,配置会被持久化到硬盘中,并带上配置的版本戳。这意味着,可以安全的停止和重启sentinel进程。
      · Sentinel作用:
      1)Master状态检测
      2)如果Master异常,则会进行Master-Slave切换,将其中一个Slave作为Master,将之前的Master作为Slave。
      3)Master-Slave切换后,主redis配置文件6379.conf、备redis配置文件6379.conf和主备redis的sentinel.conf的内容都会发生改变,即主redis的6379.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换。
      ③ Sentinel工作方式(每个Sentinel实例都执行的定时任务)
      1)每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个PING命令。
      2)如果一个实例(instance)距离最后一次有效回复PING命令的时间超过 own-after-milliseconds 选项所指定的值,则这个实例会被Sentinel标记为主观下线。
      3)如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
      4)当有足够数量的Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态,则Master会被标记为客观下线。
      5)在一般情况下,每个Sentinel 会以每10秒一次的频率向它已知的所有Master,Slave发送 INFO 命令。
      6)当Master被Sentinel标记为客观下线时,Sentinel 向下线的 Master 的所有Slave发送 INFO命令的频率会从10秒一次改为每秒一次。
      7)若没有足够数量的Sentinel同意Master已经下线,Master的客观下线状态就会被移除。 若 Master重新向Sentinel 的PING命令返回有效回复,Master的主观下线状态就会被移除。
      ④ 三个定时任务
      1)每10秒每个sentinel会对master和slave执行info命令,这个任务达到两个目的:
          a)发现slave节点;
          b)确认主从关系。
      2)每2秒每个sentinel通过master节点的channel交换信息(pub/sub)。master节点上有一个发布订阅的频道(sentinel:hello)。sentinel节点通过__sentinel__:hello频道进行信息交换(对节点的"看法"和自身的信息),达成共识。
      3)每1秒每个sentinel对其他sentinel和redis节点执行ping操作(相互监控),这个其实是一个心跳检测,是失败判定的依据。
      ⑤ Sentinel配置文件
      至少包含一个监控配置选项,用于指定被监控Master的相关信息:
      Sentinel monitor ,例如 :

    修改/etc/redis/sentinel.conf,添加以下配置
    sentinel monitor mymaster 127.0.0.1 6379 2		##配置监听的master信息,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,172.25.76.1代表监控的主服务器,6379代表端口,后面的2表示投票机制,至少有2个节点认为master挂了,才会切换
    
    • 1
    • 2

    · 用于监控mymaster主服务器,服务器ip和端口,将这个主服务器判断为下线失效至少需要2个Sentinel同意,如果多数Sentinel同意才会执行故障转移。
    · Sentinel会根据Master的配置自动发现Master的Slaves
    · Sentinel默认端口号为26379

    注意启动的顺序。首先是主机(server1)的Redis服务进程,然后启动从机的服务进程,最后启动3个哨兵的服务进程。

    3、cluster集群: Redis Cluster 是一种服务器 Sharding 技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个 Redis 主机和多个从机组成,片区和片区之间是相互平行的。Redis Cluster 集群采用了 P2P 的模式,完全去中心化。
      工作原理:
      · 集群完全去中心化,采用多主多从;所有的 redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输速度和带宽。
      · 客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
      · 每一个 master 节点负责维护一部分槽,以及槽所映射的键值数据;集群中每个节点都有全量的槽信息,通过槽每个 node 都知道具体数据存储到哪个 node 上。根据公式HASH_SLOT=CRC16(key) mod 16384,计算出映射到哪个分片上,然后Redis会去相应的节点进行存取操作。
      ·为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。

    13.如何保证 Redis 缓存与数据库双写一致性

    1、加分布式读写锁(适合读多写少场景): 通过加锁保证并发读写,写的时候按顺序排好队,读不影响。(加读写锁可能会导致系统变得沉重,系统变慢)
    2、为缓存设置超时时间(适合允许一段时间DB和缓存不一致的场景): 每隔一段时间自动刷新缓存。
    3、使用Canal解决缓存一致性问题: Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。(用Canal需要额外增加Canal中间件,加重系统复杂度)。

  • 相关阅读:
    算法设计与分析 SCAU19180 集合划分问题
    【算法集训 | 暑期刷题营】7.27题---并查集
    Codeforces Round 916 (Div. 3)(A~E题解)
    深度学习之二(前馈神经网络--Feedforward Neural Network)
    unity save load系统 快速搭建
    【0119】PostgreSQL FMS(Free Space Map)
    eslint与prettier
    2022年品牌发布的的立冬海报合集来了!
    java基于SpringBoot+vu的疫情网课授课作业管理系统 elementui
    GitLab在YugabyteDB和TiDB For PostgreSQL上的兼容性对比
  • 原文地址:https://blog.csdn.net/Leslie_qlh/article/details/126719492