【Java八股-第二十九期】Redis - 中间件

提纲:

🔥Redis

  • 概念

  • 数据类型与数据结构

  • 数据清除策略

  • 持久化

  • Redis 集群

  • 事务

  • 双写一致性策略

  • 缓存雪崩&击穿&穿透

一、Redis

1. 概念

  • Redis 是一款基于内存的以 K-V 形式进行存储的非关系型数据库

  • Redis 为什么快

    • 1、Redis 是基于内存存储

    • 2、Redis 底层采用的数据结构,通常都在读写耗时和内存空间利用上进行了优化

    • 3、Redis 采用单工作线程,避免上下文切换的开销,提高效率

    • 4、Redis 基于 I/O 多路复用监听多个 Socket

2.数据类型与数据结构

  • Redis 是以 K-V 的方式存储数据,Key 采用的是 string 字符串类型,K-V 映射采用的是 dict 数据结构,而 Value 使用的是RedisObject 类型,在 RedisObject 类型中,包含的成员主要有:

    • 1)type -- 数据的类型,即 string,hash,list 等;

    • 2)encoding -- 数据类型的底层实现,如 dict,ziplist 等;

    • 3)LRU用于计算 LRU 时间;

    • 4)ptr 数据存储的真实地址指针

  • String 字符串

    • INT: 整型(其实是long),用来存储纯数字字符串,节省存储空间

    • SDS:动态字符串,由 header 和存储的字符数组组成,header 中记录了数组长度与 SDS 容量,可以动态扩展

  • Hash 键值对映射

    • ZipList:压缩链表,在内存中连续存储双向链表,相当于一个数组,减少内存碎片,增加内存利用率,但增删操作会导致扩容和 realloc 的额外开销

    • Dict:当 K-V 元素数量大于512个时使用 Dict,Redis 中的 Dict 使用分离链表法解决散列时的数组下标冲突问题,Redis 为了提高内存利用率,默认的扩容因子是1,且还设置了缩容因子为0.1,并且 Redis不会在一次 CRUD 操作中完成扩容,而是将扩容均摊到多次操作中完成,避免单次操作时间因扩容大大增加

  • List 链表

    • LinkedList:双向链表,按元素插入的先后顺序排序,在内存中分散存储

    • QuickList:是双向链表与压缩链表的结合,本质上是一个节点为 ZipList 的 LinkedList,可以一定程度克服 LinkedList内存碎片的开销,以及 ZipList 增删的开销,可以设置单个 ZipList 的最大大小。

  • Set 无序集合,元素不可重复

    • IntSet:用来存储整型,可以使用二分查找快速去重

    • Dict

  • Sorted Set 有序不可重复集合,可以通过设置 score 权重排序

    • ZSet

      • Dict:在 ZSet 中,Dict 用来建立权重 score 到元素的映射

      • SkipList:跳表,在 ZSet 中,跳表用来根据 score 快速查找,跳表就是一个多层的链表,可以通过每一层比其上一层元素数减半从而实现链表的二分查找,但在增删时维护多层链表之间的元素数量与位置的开销会增大,Redis 中,不要求每一层链表间有严格的数量关系,而是随机为每个节点分配一个层数,层数上限即跳表的头节点层数,层数越高分配的概率越低

3.数据清除策略

  • 数据过期清除策略

    • 1、定期清除:Redis 每隔 100ms,随机选取设置了过期时间并已经过期的数据进行清除

    • 2、惰性清除:对于已经过期的数据,当 client 对其访问时,会触发 Redis 对其进行清除

  • 缓存淘汰策略

    • 1、不进行缓存淘汰,一旦缓存写满,Redis 就不再提供服务

    • 对过期数据的缓存淘汰策略

      • 2、ttl:过期数据淘汰策略特有,清除所有设置了过期时间的且即将过期的数据

      • 3、random:随机选取设置了过期时间的数据清除

      • 4、LRU:最近最少使用,经典 LRU 算法通过在链表中移动元素,让最近被访问的元素始终位于头节点,最久未访问的元素位于尾节点,但这需要维护一个链表,数据量大时会造成大量额外开销; # Redis 的使用:在 RedisObject 中记录了 LRU 访问时间戳,进行缓存淘汰时,一次性随机选取 N 个数据看作一个淘汰集合,并按照 LRU 时间戳排序,淘汰 LRU 最小的数据,当需要再次淘汰时,随机选取新数据加入,直到淘汰集合中数据达到 N 个,用这种局部淘汰的方式,避免对所有数据维护一个链表

      • 5、LFU:最近最少使用频率,如果一个数据 A 只使用了一次,但这一次刚好就在缓存淘汰之前,而数据 B 是一个多次使用的热点数据,但最后一次使用时间早于 A,使用 LRU 算法就会将 B 进行删除,显然不合理;

        • # LFU 实现主要要考虑的问题:

          • 1)与 LRU 同样需要使用链表维护,并且需要维护访问次数记录,热点数据的访问次数内存开销大;

          • 2)热点数据随时间推移可能发生变化,即访问模式发生改变,已经失效的热点数据无法被清除

      • # Redis 的解决:

        • 1)Redis 使用一个 24 位的数字来记录 LFU 信息,其中 8 位用来表示访问次数,16 位用来表示上一次递减时间,即访问次数上限是 255,并且访问次数越大,再增长的速度就越慢

        • 2)可以在 redis 配置文件中设置实际访问次数增长与访问计数的比值,递减时间用来表示上一次进行递减操作的时间戳后 16 位,递减时,随机采样 N 个元素,并查看其递减时间与当前时间差是否大于一个策略时间 M,若大于,则进行减半或递减,时间 M 可以在配置文件中配置

4.持久化

  • RDB

    • 触发 RDB 存储时,Redis 会抓取当前数据库的数据快照,并以 rdb 文件的形式替换上一个 rdb 持久化文件进行存储

    • 使用方式:

      • save 指令:save 指令会阻塞当前工作进程,存储完成后才恢复,会造成 Redis 服务短时间内不可用,一般不采用

      • bgsave 指令:Redis 通过 fork 出一个子进程进行 RDB 存储

      • 日志配置:通过配置 操作次数/时间 阈值,来让 Redis 自行调用 bgsave 进行持久化

    • 优点:

      • 1、存储的是当前数据库的快照,rdb 文件较小

      • 2、对于海量数据,进行数据恢复时效率高

    • 缺点:

      • 1、在 RDB 过程中,对 Redis 的修改无法存储,Redis 挂掉的话,这一部分数据就成为脏数据

      • 2、操作系统的 fork 采用的是写时拷贝(copy - on -write),即子进程会共享主进程的虚拟内存页,在子进程进行 RDB 备份时,主进程对数据修改会拷贝一份页进行修改,在 RDB 结束后再同步,最坏情况可能会使消耗的内存翻倍

  • AOF

    • AOF 进行的是增量备份,即每进行一次修改操作,就将这条指令追加至 aof 备份文件

    • Rewrite:由于进行增量备份且单个 aof

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

代码鹿のJAVA八股面试题总结 文章被收录于专栏

【📫专栏目录在最底部📫】 - 本专栏适合于JAVA已经入门的学生或人士,有一定的编程基础。 - 本专栏特点: 本专刊囊括了JAVA、Spring、计算机网路、操作系统、计算机网络、MySQL、算法与数据结构、中间件等一系列知识点,总结出了高频面试考点(附有答案),事半功倍,为大家春秋招助力。 - 本专栏内容分为五章

全部评论

相关推荐

评论
点赞
2
分享

创作者周榜

更多
牛客网
牛客企业服务