[八股速成]redis篇

前言

我之前整理过redis超详细八股笔记:https://www.nowcoder.com/discuss/583767072316858368?sourceSSR=search,但是说实话因为这份这份八股资料过于详细,内容过于充实,给背记带来了很大的挑战,所以我准备再出一系列帖子,内容就是我根据自己的面试经历和网上的面经,去筛选八股里面哪些是最常被问到的问题把它们整理出来,这样也能省去大家自己整理和筛选的时间大家可以在面试前一两个小时快速把这一系列最常问八股的帖子拿出来看看,临时抱佛脚的效果应该很好。后面这系列帖子我会放入专栏https://www.nowcoder.com/creation/manager/columnDetail/0ybvLm,欢迎大家订阅。最后我想说,速成虽好,但是还是建议有时间就去看看我详细的八股笔记帖子。

1.redis常用数据类型

Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:

  • 字符串(string):普通字符串,Redis中最简单的数据类型,string的内部结构实现上类似Java的ArrayList
  • 哈希(hash):也叫散列,类似于Java中的HashMap结构
  • 列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList,底层是双向链表
  • 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
  • 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素

2.zset的底层原理

Redis的有序集合(Zset)底层采用两种数据结构,分别是压缩列表(ziplist)和跳跃表(skiplist)

  • 当Zset的元素个数小于128个且每个元素的长度小于64字节时,采用ziplist编码。在ziplist中,所有的key和value都按顺序存储,构成一个有序列表。这种实现方式主要是为了节省内存,因为压缩列表是一块连续的内存区域,通过紧凑的存储可以有效地利用空间。虽然压缩列表可以有效减少内存占用,但在需要修改数据时,可能需要对整个列表进行重写,性能较低。
  • 跳表是一种多层次的链表结构,通过多级索引提升查找效率。在不满足使用压缩列表的条件下,Redis会采用跳表作为Zset的底层数据结构。跳表能够提供平均O(logN)的时间复杂度进行元素查找,最坏情况下为O(N)。跳表中的每一层都是一个有序链表,并且层级越高,链表中的节点数就越少,从而允许在高层快速跳过一些元素,达到快速定位的目的。

综上所述,Redis的Zset通过灵活地使用压缩列表和跳跃表作为底层数据结构,在不同的场景下平衡了内存使用效率和数据操作性能。这两种数据结构各有优劣,压缩列表适用于数据量小、内存受限的场景,而跳跃表适合于数据量大、需要高效操作的环境。

什么是跳表?

跳表(Skip List)是一种基于有序链表的数据结构,通过多级索引的方式实现高效的查找、插入和删除操作

跳表以空间换时间的方式优化了传统单链表的效率。在单链表中,即使数据是有序的,查找一个元素也需要从头到尾遍历整个链表,时间复杂度为O(n)。而在跳表中,通过建立多层索引来实现快速查找。顶层索引链表的节点数量远少于底层原始链表,并且层级越高,节点越少。

跳表中的每一层都是一个有序链表,并且每个节点都包含指向同层级下一个节点的指针以及指向下一层对应节点的down指针。例如,当查找一个元素时,首先在顶层索引进行查找,如果当前节点的值大于要查找的值,则继续在同一层级向右移动;如果小于要查找的值,则通过down指针下沉到下一层继续查找。每下降一层,搜索范围就缩小一半,最终在底层链表中找到目标元素或者确认元素不存在。

跳表的插入和删除操作同样高效,其时间复杂度也是O(logn)。向跳表中插入新元素时,首先要找到合适的插入位置,保持链表的有序性。然后通过随机函数决定新节点应该出现在哪些层级的索引中:随机结果高于某个固定概率p,就在该层级插入新节点。删除操作类似,先找到要删除的节点,然后在所有包含该节点的层级中移除它。

3.hash的底层原理

Redis的Hash数据结构底层原理主要基于两种数据结构:ziplist和hashtable

具体来说,这两种数据结构的应用如下:

  • ziplist:当满足特定条件时(键和值的字符串长度都小于64字节,且键值对数量少于512),Hash数据结构会采用ziplist作为其底层实现。在ziplist中,所有的key和value都按顺序存储,构成一个有序列表。这种实现方式主要是为了节省内存,因为压缩列表是一块连续的内存区域,通过紧凑的存储可以有效地利用空间。
  • hashtable:当不满足ziplist的条件时,Hash数据结构会使用hashtable作为底层实现。在hashtable中,每个键值对都以字典的形式保存,其中字典的键为字符串对象,保存了原键值对的键;字典的值为另一个字符串对象,保存了原键值对的值。这样的结构允许快速的查找、插入和删除操作。

此外,在Hash数据结构中,如果ziplist编码所需的两个条件中的任意一个不再满足时,会发生编码转换,即原本保存在ziplist中的所有键值对会被转移到字典中,对象的编码也会从ziplist变为hashtable。这通常发生在键的长度过大、值的长度过大或者键值对的数量过多的情况下。

综上所述,Redis的Hash数据结构根据数据的规模和访问模式灵活地在ziplist和hashtable之间切换,以达到既节省内存又保证访问效率的目的。

4.3种redis特殊数据类型

Bitmap (位图)

Bitmap 存储的是连续的二进制数字(0 和 1),本来int数字占4字节32位,但通过 Bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态(比如:01表示1,001表示2) 。,所以 Bitmap 本身会极大的节省储存空间。

# 将名为myBitmap的bitmap的第5位设置为1
SETBIT myBitmap 5 1  //SETBIT key offset value

你可以将 Bitmap 看作是一个存储二进制数字(0 和 1)的数组,数组中每个元素的下标叫做 offset(偏移量)。

HyperLogLog(基数统计)

HyperLogLog 是一种有名的基数计数概率算法 ,基于 LogLog Counting(LLC)优化改进得来,并不是 Redis 特有的,Redis 只是实现了这个算法并提供了一些开箱即用的 API。

Redis 提供的 HyperLogLog 占用空间非常非常小,只需要 12k 的空间就能存储接近2^64个不同元素。这是真的厉害,这就是数学的魅力么!并且,Redis 对 HyperLogLog 的存储结构做了优化,采用两种方式计数:

  • 稀疏矩阵:计数较少的时候,占用空间很小。
  • 稠密矩阵:计数达到某个阈值的时候,占用 12k 的空间

Geospatial (地理位置)

Geospatial index(地理空间索引,简称 GEO) 主要用于存储地理位置信息,基于 Sorted Set 实现。

通过 GEO 我们可以轻松实现两个位置距离的计算、获取指定位置附近的元素等功能。

数值范围0-40亿的数如何排序(bitmap)

使用Bitmap进行排序是一种特殊的方法,适用于处理大量数据的排序问题,尤其是在内存有限的情况下。以下是使用Bitmap排序的步骤:

  1. 初始化Bitmap:根据数值范围创建一个足够大的Bitmap。由于数值范围是0-40亿,Bitmap的大小需要能够覆盖这个范围,即至少需要40亿位。
  2. 标记数值:遍历待排序的数值列表,将每个数值在Bitmap中对应的位置标记为1。例如,如果数值是5,则在Bitmap的第6位(从0开始计数)标记为1。
  3. 按位输出:按照Bitmap的顺序,输出所有标记为1的位置对应的数值,即可得到排序后的结果。

5.使用 Redis 实现一个排行榜怎么做?

Redis 中有一个叫做 Sorted Set 的数据类型经常被用在各种排行榜的场景,比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。

相关的一些 Redis 命令: ZRANGE (从小到大排序)、 ZREVRANGE (从大到小排序)、ZREVRANK (指定元素排名)。

6.redis为什么这么快?

1、完全基于内存的,C语言编写,没有磁盘IO上的开销。数据存在内存中,读写速度快。

2、采用单线程,避免不必要的上下文切换以及锁等同步机制的开销

3、使用多路I/O复用模型,基于select/epoll等I/O多路复用技术实现高吞吐量网络I/O

  • IO多路复用是一种操作系统中的一种技术,允许一个线程或进程同时处理多个输入输出(IO)操作。Redis通过使用IO多路复用技术,以单线程的方式高效地处理了多个客户端的请求,避免了为每个连接创建新线程的开销,并保持高性能。

能解释一下I/O多路复用模型?

IO多路复用模型的思路就是:系统提供了select、poll、epoll函数可以同时监控多个fd(文件描述符)的操作,有了这个函数后,应用线程通过调用select函数就可以同时监控多个fd,一旦某个描述符就绪(一般是读就绪或者写就绪),select函数就会返回可读/可写状态,这时询问线程再去通知想请求IO操作的线程,对应线程此时再发起IO请求去读/写数据

文件描述符是一个非负整数,用于标识被进程打开的文件,是操作系统为了高效管理这些文件所创建的索引

7.Redis遇到哈希冲突怎么办?

当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时, 我们称这些键发生了冲突(collision)。

Redis 的哈希表使用链地址法(separate chaining)来解决键冲突: 每个哈希表节点都有一个 next 指针, 多个哈希表节点可以用 next 指针构成一个单向链表, 被分配到同一个索引上的多个节点可以用这个单向链表连接起来, 这就解决了键冲突的问题。

原理跟 Java 的 HashMap 类似,都是数组+链表的结构。当发生 hash 碰撞时将会把元素追加到链表上。

8. Cache Aside Pattern(旁路缓存模式)

Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。

Cache Aside Pattern 中服务端需要同时维系 db 和 cache,并且是以 db 的结果为准。

  • 先更新 db
  • 然后直接删除 cache 。

:

  • 从 cache 中读取数据,读取到就直接返回
  • cache 中读取不到的话,就从 db 中读取数据返回
  • 再把数据放到 cache 中。

1.在写数据的过程中,可以先删除 cache ,后更新 db 么?

不行,因为这样可能会造成 数据库(db)和缓存(Cache)数据不一致的问题。举例:请求 1 先写数据 A,请求 2 随后读数据 A 的话,就很有可能产生数据不一致性的问题。

过程如下:请求 1 先把 cache 中的 A 数据删除 -> 请求

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

本专栏价格永远为19.9元! 不想当架构师的后端开发工程师不是好码农! 此专栏一方面用于存放我的架构设计学习笔记, 另外我会在本专栏加入一系列最常问八股问题帖子,内容就是我根据自己的面试经历和网上的面经,去筛选八股里面哪些是最常被问到的问题把它们整理出来,大家可以在面试前一两个小时快速把这一系列最常问八股的帖子拿出来看看,临时抱佛脚的效果应该很好

全部评论
看看😃
点赞 回复 分享
发布于 2024-12-09 12:01 湖北

相关推荐

11-25 19:53
湖南大学 Java
字节剪映一面1. 你做的项目是实际有社会上的用户在使用,还是个人兴趣去研究的?2. 你大概能实习多久?3. 实习地点在广州或者深圳,你有了解吗?4. 请整体介绍一下鹿山美食探店平台的整体架构,你是怎么设计的?5. 你都是去云上找的服务器吗?是买的还是其他方式?6. 整个系统分成了几大块?它们的分层架构是怎么样的?7. 这些功能都是你一个人做的吗?8. 你的秒杀功能是怎么设计的?9. 你是怎么得出高并发下乐观锁实现秒杀失败率高的结论?做了压测吗?10. 压测了多少 KPS?11. 1000 个并发下的失败率是多少?12. 你是用 MySQL 去判断库存是否大于 0 吗?13. 改完判断库存的方式后,秒杀成功率有明显提升吗?14. 你用 Redis 减库存时,减到 0 怎么处理?如何防止减出负数?15. 改为 Redis 缓存库存 + 异步下单后,有再进行压测吗?16. 异步下单后,如何让用户实时感知到秒杀成功与否?17. 如果想要提高秒杀的并发量,你还有什么优化措施?18. 库存分段具体怎么分段?19. 针对线上工业级的量,排行榜的更新和查询有什么优化措施?20. 设计全局热榜(更新频繁、查询量大),从更新和查询两方面该怎么设计?21. 千万用户量级下,用户频繁点赞导致 Redis 频繁写,这种情况合理吗?有考虑过相关场景吗?22. 全局热榜查询时,有什么应对高查询量的措施?23. 你在项目中的哪些场景分别解决了缓存穿透、雪崩和击穿的问题?24. 请分别讲解缓存穿透、雪崩和击穿是什么?25. 如何应对缓存穿透?26. 布隆过滤器会有误判吗?27. 缓存雪崩的第一种情况(缓存统一过期)怎么解决?28. 如何解决缓存击穿?29. 热门 key 非常热,全网都来查询,即使有 Redis 缓存也可能爆掉,这种情况怎么处理?30. 多级缓存该如何分布?31. 如何提高一个热门 key 的并发量?32. Java 中的两个等号和 equals 有什么区别?33. 如果 equals 没有实现,默认比较的是什么?34. 用双引号声明的字符串 "ABC" 和 new String("ABC") 用两个等号判断是否相等?35. Java 中的 Volatile 关键字有什么作用?36. Volatile 能保证原子性吗?37. 实际中你平常会用到 Volatile 关键字吗?38. 交替打印是怎么样的实现?多个线程修改变量时需要加锁吗?39. 计算机存储层次从快到慢依次是哪些?40. 二维数组按行和按列遍历,性能会有差别吗?41. TCP 中 TIMEWAIT 状态有什么作用?42. 你对 TCP 的哪些知识还有印象?43. TCP 的全双工能解释一下吗?44. TCP 和 UDP 主要有哪些区别?45. 两条 SQL 语句的性能怎么样?如果不行该怎么优化?46. 模糊匹配时除了把字段反过来存,还有其他更高效的办法吗?47. 深度分页问题该怎么处理?48. 请分别举例出行锁和表锁的触发场景?49. 更新操作一定是行锁吗?有没有什么条件会变成表锁?50. Redis 中的过期删除策略是怎么样的?51. 由 N-1 个正整数组成的未排序数组,元素是 1 到 N 不重复的整数,如何找到缺失的那个数?52. 给定一个先序和中序序列,如何输出后续序列?53. 你对本次面试的项目组主要业务流程有什么想要咨询的吗?54. 你对面试流程(日常实习生)有什么想要咨询的吗?55. 你对简历有什么想要咨询的建议吗?
点赞 评论 收藏
分享
评论
11
51
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务