Redis面试核心技术点和缓存相关问题

news2024/12/22 18:15:30

目录

Redis的数据结构和原理

Redis持久化:RDB和AOF

Redis的集群设计

缓存雪崩、击穿、穿透

高并发场景下缓存和数据库更新策略

Redis的大key和热key和大value

本地缓存


磁盘IO和网络开销 相比于 请求内存IO 要高上千倍,如果某个数据从数据库磁盘读出来需要 0.0045S,经过网络请求传输需要 0.0005S,那么每个请求完成最少需要 0.005S,该数据服务器每秒最多只能响应200 个请求。而如果该数据存于内存中,读出来只需要 100us,那么每秒能够响应 10000 个请求。这就是存储在硬盘的MySQL和存储在内存的Redis的差别。基于内存的缓存可以把系统响应能力提高 N 个数量级,远高于传统基于硬盘的关系型数据库。

Redis单线程有一个最大好处就是 节省线程切换的开销,更不用考虑并发读写带来的复杂操作场景,大大节省了线程间切换的时间。单线程模型避免了多线程的频繁上下文切换,这也避免了多线程可能产生的竞争问题。Reids 核心是基于非阻塞的 IO 多路复用机制。

Redis的数据结构和原理

Redis常用的5种数据结构如下,更多命令和使用场景请查看:Redis命令合集和设计场景_浮尘笔记的博客-CSDN博客

  • 字符串 String: 基础的数据存储类型,可存储文本、Json、图片数据等任何二进制文件。主要命令:set/get。
  • 列表 List: 按照插入顺序排序的字符串链表,在插入时如果key不存在会创建一个新的链表。主要命令:lpush/rpush/lpop/rpop。
  • 哈希 Hash: 适合存储键值对信息,比如一个用户的昵称、性别、年龄等属性可以存储为一个key。主要命令:hset/hget/hgetall/hmset/hdel。
  • 集合 Set: 无序集合,值不能重复。可以原来存储唯一IP、唯一用户ID 等信息。主要命令:sadd/smembers/srem。
  • 有序集合 Sorted Set: 支持从小到大排序的集合,适用于排行榜结构的数据存储。主要命令:zadd/zscore/zrange[withscores]。

Redis底层原理:Redis 使用C语言编写,其中String类型使用简单动态字符串(simple dynamic string,简称 SDS)实现,有一个专门用于保存字符串长度的变量,可以通过len属性的值获取字符串长度,从一定程度上提高了读取效率。Redis中字符串的定义如下:

struct sdshdr {
    int len; //记录 buf 中已经使用的空间长度
    int free; //记录 buf 中还空余的空间
    char buf[]; //记录字符串存储的内容
}

有序集合 Sorted Set 的内部使用 哈希表(HashMap) 和跳跃表 (SkipList)  来保证数据的存储和有序,HashMap 里放的是成员到 score 的映射,跳表里存放的是所有的成员,排序依据是HashMap 里存的 score,使用跳表的结构可以获得比较高的查找效率,并且在实现上比较简单。

【问】为什么 Redis 的使用跳表结构而不用红黑树?
【答】红黑树的查找效率很高,但是在进行重新平衡时会涉及到大量节点的变化,因此实现和操作起来都比较复杂。而跳表通过简单的多层索引结构,实现简单,且能达到近似于红黑树的查找效率,插入多层节点不需要像红黑树那样有额外操作;而且跳表还能实现范围查找及输出,而红黑树只支持单个元素查找,对于范围查找效率低。

关于Redis底层的更多细节请查看:Redis底层数据结构和原理_浮尘笔记的博客-CSDN博客

缓存的淘汰策略:缓存相比于磁盘更加昂贵,因此不能把所有数据都放入缓存,只能把最重要的或者要求查询速度最快的数据缓存起来,因此需要有一定的策略来让过期的缓存失效。常见的缓存淘汰策略有下面几个:

  • FIFO(First In First Out): 先进先出算法,先放入缓存的先被移除。
  • LRU(Least Recently Used): 最近最少使用算法,使用时间距离现在最久的那个被移除。
  • LFU(Least Frequently Used): 最不常用算法,一定时间段内使用次数(频率)最少的那个被移除。

Redis持久化:RDB和AOF

RDB 是 Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中,会在指定目录下生成一个 dump.rdb 文件。Redis 如果重启了,会通过加载 dump.rdb 文件恢复数据。可以使用 SAVE 和 BGSAVE 两个命令来生成 RDB 文件,SAVE是阻塞的,BGSAVE 是后台 fork 子进程进行,不会阻塞主进程处理命令请求。载入 RDB 文件不需要手工运行,而是 server 端自动进行。只要启动时检测到 RDB 文件存在,server 端便会载入 RDB 文件重建数据集。如果同时存在 AOF 的话会优先使用 AOF 重建数据集,因为其保存的数据更完整。

如果业务对数据完整性和一致性要求不高,RDB 的启动速度更快; RDB 的性能很好,需要持久化时,主进程会 fork 一个子进程出来,然后把持久化的工作交给子进程,自己不会有相关的 I/O 操作。缺点就是可能丢失间隔时间内的数据,而且RDB备份时会创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍),最后再将临时文件替换之前的备份文件。

AOF 默认不开启,它采用日志的形式记录每个写操作,生成一个 appendonly.aof 文件,并将日志追加到文件末尾。Redis 重启的时候会根据日志文件的内容,将写指令从前到后执行一次以完成数据的恢复工作,有点类似 MySQL 的 binlog。优点就是能最大限度地保证数据不丢失,数据的完整性和一致性更高。缺点就是AOF备份产生的 appendonly.aof 文件较大,数据恢复的时候也会比较慢,Redis 针对 AOF 文件大的问题,提供了重写的瘦身机制:可以通过bgrewiteaof手动触发,或者配置相关选项自动触发重写。

关于Redis持久化的更多内容请查看:redis笔记06-持久化rdb和aof

Redis的集群设计

稍微优点规模的项目,不管是用 MySQL还是 Redis,单机基本上扛不住,差不多都得使用集群了。Reids 官方给出了 Redis-cluster 方案,无中心架构,可线性扩展到 1000 个节点。

Redis Cluster 是官方在 Redis 3.0 版本正式推出的高可用以及分布式的解决方案,内置数据自动分片机制,由多个 Redis 实例组成的整体,数据按照槽(slot) 存储分布在多个 Redis 实例上,集群内部将所有的 key 映射到 16384 个 Slot 中。

Redis Cluster 实现的功能:

  • 将数据分片到多个实例 (按照 slot 存储);
  • 集群节点宕掉会自动 failover;
  • 提供相对平滑扩容 (缩容) 节点。 

Redis Cluster 的优点:

  • 无中心架构:三机房部署,其中一主一从构成一个分片,之间通过异步复制同步数据,异步复制存在数据不一致的时间窗口,保证高性能的同时牺牲了部分一致性(CAP定理)。一旦某个机房掉线,则分片上位于另一个机房的 slave 会被提升为 master 从而可以继续提供服务。
  • 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除。
  • 降低运维成本,提高系统的扩展性和可用性。
  • Redis Cluste 可以线性扩展至 1000 个节点。Redis Cluster 本身就能自动进行 master 选举和故障转移。

  • 三机房部署,每个机房有一主一从,即一个 Master 对应一个 Slave。机房 1 中的 Master 1 连接的 Slave 在机房 2,机房 2 中的 Master 2 连接的 Slave 在机房 3,机房 3 中的 Master 3连接的 Slave 在机房 1,这样构成了一个环。
  • 其中一主一从构成一个分片,之间通过异步复制同步数据,一旦某个机房掉线,则分片上位于另一个机房的 slave 会被提升为 master,从而可以继续提供服务;每个 master 负责一部分slot,数目尽量均摊;客户端对于某个 Key 操作先通过公式计算出所映射到的slot,然后直连某个分片,写请求一律走 master,读请求根据路由规则选择连接的分片节点。
  • Master 负责写,Master 会自动同步到 Slave,假设机房 1 的机器全部断电了,机房1的Master 写服务宕机,此时 Slave 读服务会被提升为 Master ,也就是说机房 1 的数据在机房 2 的 Slava2 上还有备份,数据还在,在宕机的 master 没有恢复前 Slave 要同时承担读写服务,虽然累一点,但是系统仍然能提供服务。
  • 如果单个机房距离很远, Master 1 的数据同步到 Slave2 上是跨机房同步,肯定不如同机房快,这样一来 Slave2 负责的读就会有延迟,Master1 要更新的数据还没有同步到他在另一个机房的备份前,读操作就是不一致的,这样设计牺牲掉一致性(C)。

缓存雪崩、击穿、穿透

很多人在处理缓存和数据库同步问题的思路是这样的:先查缓存,如果缓存没有,则去查数据库,然后更新缓存,如下图所示:

这样设计在数据量小的时候基本没啥问题,如果数据量大,遇到缓存中热点key集中失效,就很可能引发雪崩或者穿透;

雪崩 就是缓存中大批量热点数据过期后,系统的大量请求犹如雪崩一般涌入,引起数据库压力,造成数据库查询堵塞甚至宕机。解决办法:

  • 缓存失效时间分散开,比如每个 key 的过期时间是随机,防止同一时间大量数据过期现象发生,这样不会出现同一时间全部请求都落在数据库层。如果缓存数据库是分布式部署,将热点数据尽可能均匀分布在不同 Redis 和数据库中,有效分担压力,别让一台机器扛着压力。
  • 简单粗暴,让 Redis 数据永不过期,人为手动控制删除已过期的数据。

穿透 是调用者发起的请求参数(key)在缓存和数据库中都不存在,绕过 Reids,通过不存在的 key成功穿透到数据库层或者更底层,大规模不断发起不存在的 key 的请求,会导致系统压力过大最后故障。解决办法:

  • 分布式布隆过滤器:Redis 自身支持布隆(BloomFilter),参考:Redis(十) 布隆过滤器
  • 返回空值:遇到数据库和 Redis 都查询不到的值,在 Redis 里设置一个缺省值,过期时间很短,目的在于同一个 key 再次请求时直接返回 null,避免穿透。

击穿 和穿透类似,是指一个 key 是热点 key,某个瞬间会有成千上万次请求(比如微博热点排行榜,key 是小时时间戳,value 是个 list 的榜单)。每个小时产生一个 key,这个key 会有百万 QPS,如果这个 key 失效了,就像保险丝熔断,百万 QPS 直接压垮数据库。解决办法:

  • 对于访问特别频繁的热点数据就不设置过期时间了,让key永久有效,然后手动控制删除key。

关于缓存雪崩、穿透、击穿的更多内容请查看:缓存雪崩、缓存击穿、穿透穿透具体指哪些问题?_浮尘笔记的博客-CSDN博客 

高并发场景下缓存和数据库更新策略

如果更新一个数据,先更新据库再更新缓存,可能出现高并发场景下的缓存和数据库中数据不一致问题。如果有多个线程同时更新数据库,然后更新缓存,可能出现下面的情况:

针对这个问题,有一个办法,就是先更新数据库,再删除缓存,等待下次读取数据的时候再从数据库读取最新值,然后写入新的缓存:

怎么解决?可以使用 延迟双删,具体二次删除缓存延迟多久,基本上要大于一次更新操作所花费的时间。如果第二次删除失败,可以放到队列中循环删除。

总结:优先查询缓存,如果缓存未命中则查询数据库,将结果写入缓存;数据更新时先更新数据库,再删除缓存,然后一段时间后再延迟删缓存(防止并发场景下操作出现问题)

Redis的大key和热key和大value

正常情况下,Redis 集群中数据都是均匀分配到每个节点,请求也会均匀的分布到每个分片上,但在一些特殊场景中(比如外部爬虫、攻击、热点商品、热搜话题等),这种短时间内某些 key 访问量过于大,对于这种相同的 key 会请求到同一台数据分片上,导致该分片负载较高成为瓶颈,导致雪崩等一系列问题。

热点数据最大的问题会造成 Reids 集群负载不均衡(也就是数据倾斜)导致的故障,这些问题对于 Redis 集群都是致命打击。造成 Reids 集群负载不均衡故障的主要原因:

  • 高访问量的 Key,也就是热 key,如果一个 key 访问的 QPS 超过 1000 就要特别注意了,比如热门商品,热门话题等。
  • 大 Value,有些 key 访问 QPS 虽然不高,但是由于 value 很大,造成网卡负载较大,网卡流量被打满,单台机器可能出现千兆/秒,IO 故障。
  • 热点 Key + 大 Value 同时存在,服务器杀手。

热点 key 或大 Value 会造成哪些故障:

  • 数据倾斜问题:大 Value 会导致集群不同节点数据分布不均匀,造成数据倾斜问题,大量读写比例非常高的请求都会落到同一个 redis server 上,该 redis 的负载就会严重升高,容易打挂。
  • QPS 倾斜:分片上的 QPS 不均。
  • 大 Value 会导致 Redis 服务器缓冲区不足,造成 get 超时。
  • Redis 缓存失效导致数据库层被击穿的连锁反应。

如何准确定位热点数据?

  • 根据业务情况,人工预估统计可能会成为热点的数据,比如 促销活动商品,热门话题等。
  • 在调用端统计某些key的请求次数,但是无法预知具体是哪些key,代码侵入性强。
  • Redis 集群代理层统计:像 Twemproxy、codis 这些基于代理的 Redis 分布式架构,统一的入口,可以在 Proxy 层做收集上报,但是缺点很明显,并非所有的 Redis 集群架构都有 proxy。
  • Redis 服务端收集:监控 Redis 单个分片的 QPS,发现 QPS 倾斜到一定程度的节点进行 monitor,获取热点 key, Redis 提供了 monitor 命令,可以统计出一段时间内的某 Redis 节点上的所有命令,分析热点 key,在高并发条件下,会存在内存暴涨和 Redis 性能的隐患,所以此种方法适合在短时间内使用;同样只能统计一个Redis 节点的热点 key,对于集群需要汇总统计,实现起来比较麻烦。

如何解决热点数据问题:主要从两个方面考虑,第一是数据分片,让压力均摊到集群的多个分片上,防止单个机器打挂;第二是迁移隔离。

  • key 拆分:如果当前 key 的类型是一个二级数据结构,例如哈希类型。如果该哈希元素个数较多,可以考虑将当前 hash 进行拆分,这样该热点 key 可以拆分为若干个新的 key 分布到不同 Redis 节点上,从而减轻压力。
  • 迁移热点 key:以 Redis Cluster 为例,可以将热点 key 所在的 slot 单独迁移到一个新的 Redis 节点上,这样这个热点 key 即使 QPS 很高,也不会影响到整个集群的其他业务,还可以定制化开发,热点 key 自动迁移到独立节点上,这种方案也叫做:多副本。
  • 热点 key 限流:对于读命令 可以通过迁移热点 key 然后添加从节点来解决,对于写命令 可以通过单独针对这个热点 key 来限流。 
  • 增加本地缓存:对于数据一致性不是很高的业务,可以将热点 key 缓存到业务机器的本地缓存中,因为是业务端的本地内存中,省去了一次远程的 IO 调用。但是当数据更新时,可能会造成业务和 Redis 数据不一致。

Redis存储的大 Value 如何优化?

  • 由于 Redis 是单线程运行的,如果一次操作的value 很大会对整个 redis 的响应时间造成影响,因为 Redis 是 Key-Value 结构数据库,大 value 就是单个 value 占用内存较大,对 Redis 集群造成最直接的影响就是数据倾斜。
  • 一般如果 string 类型的 value > 10KB,set、list、hash、zset 等集合数据类型中的元素个数 > 1000个就算是大value;如果string 类型 value > 100KB,set、list、hash、zset 等集合数据类型中的元素个数 > 10000个就算是超大value。
  • 一个较大的 key-value 可以拆分成几个 key-value ,将操作压力平摊到多个 redis 实例中,降低对单个 redis 的 IO 影响;可以将拆分后的几个 key-value 存储在一个 hash 中,每个 field 代表一个具体的属性,使用 hget,hmget 来获取部分的 value,使用 hset,hmset 来更新部分属性。

更多关于Redis的优化方案请查看:Redis 常见的性能问题和优化方案

本地缓存

同样是内存里读写数据,为啥使用本地缓存就会更快?

因为本地缓存的请求是当前服务器上的,相比于请求 Redis或者MySQL都是要转发一次到对应的数据库服务器,而当前代码和Redis服务器可能都不在同一个机房(跨机房,甚至是异地),Redis 相比本地缓存,多一次网络 IO,那肯定不如在本地获取数据快。

常用的本地缓存框架:Google Guava、Ehcache、Spring Cache、Java Map。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/611934.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第17章:存储引擎

一、查看存储引擎 1.什么是存储引擎 MySQL的存储引擎就是表的类型 2.查看mysql存储引擎 show engines; 二、设置系统默认的存储引擎 1.查看默认的存储引擎 select default_storage_engine; 2.修改默认存储引擎 set default_storage_engineMyISAM; 三、设置表的存储引擎 …

微信小程序开发之云函数本地调试

环境 微信开发者工具 Stable 1.06.2303220云开发控制台 v1.5.47 简介 微信云开发提供了云函数本地调试功能,在本地提供了一套与线上一致的 Node.js 云函数运行环境,让开发者可以在本地对云函数调试: 在本地调试时,可以设置断点…

【MD5知识详解】【面试知识】

MD5概述: MD5消息摘要算法,属Hash算法一类。MD5算法对输入任意长度的消息进行运行,产生一个128位的消息摘要(32位的数字字母混合码)。 MD5主要特点: 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样 (一个MD5理…

微信小程序nodejs+vue个人身体健康管理系统56b65

开发语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具:Navicat 开发软件:VScode 。配置文件 (自动编号、配置参数值、配置参数名称); 论坛交流 (自动编号、用户id、状态、父节点id、帖子标题、用户名…

ndoejs基于Vue.js二手书交易网站系统x3oh4

开发语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具:Navicat 开发软件:VScode 本毕业设计的内容是设计并且实现一个基于vue框架的二手书交易系统。它是在Windows下,以MYSQL为数据库开发平台&#xf…

chatgpt赋能python:Python取消warning指南:如何避免和处理警告

Python取消warning指南:如何避免和处理警告 如果您已经在使用Python编程,那么您一定会遇到过警告(warning)这个问题。虽然警告有时可能很有用,但在特定情况下,它们可能会引起程序错误或产生意想不到的行为…

Depcheck 检查前端项目中未使用的依赖包

前言 随着前端项目的迭代,项目中一部分的依赖包可能没被项目所使用的,手动查找这些依赖包耗时又繁琐,有没有根据能够快速的帮助我们识别和清理项目中未使用的依赖包呢? Depcheck 简介 Depcheck 是一款用于分析项目中依赖关系的…

windows下的SVN客户端访问ubuntu下的SVN服务器

目录 第一部分 windows创建本地版本库、连接ubuntu的SVN服务器 步骤0: 步骤一:创建windows本地版本库 步骤二:checkout检测 步骤三:输入之前配置的用户名和密码 第二部分 windows上传文件至SVN服务器 步骤一:添加…

Python数据攻略-DataFrame的创建与基础特性

大家好,我是Mr数据杨,今天我带来的是一本既实用又有趣的Python教程笔记,主角是Pandas DataFrame。它就像《三国演义》中的诸葛亮,机智、实用,可以帮助我们轻松处理各种数据问题。 我们来看如何创建诸葛亮——这个Data…

实现UDP通信(socket接口函数扩展)

一、write/read到send/recv 函数原型: ussize_t send(int sockfd, const void *buf, size_t len, int flags);//发送 ussize_t recv(int sockfd, void *buf, size_t len, int flags);//接收 前三个参数同read/write一样; ussize_t read(int fd, voi…

2023CCPC河南省赛 VP记录

感觉现在的xcpc,风格越来越像CF,不是很喜欢,还是更喜欢多点算法题的比赛 VP银了,VP银也是银 感觉省赛都是思维题,几乎没有算法题,感觉像打了场大型的CF B题很简单没开出来,一直搞到最后&…

Android crash 流程详解(二):NE

源码基于:Android R 接上一篇博文:《Android crash 流程详解(一):JE》 0. 前言 在上一篇博文剖析了java layer exception 的处理流程,本文将继续剖析 Android crash 的另一部分,即 native 端的 crash,又称…

【JavaEE】网络编程之UDP套接字

目录 1、网络编程基础 2、UDP数据报套接字编程 2.1.DatagramSocket API(方法) 2.2、DatagramPacket API(方法) 2.3、InetSocketAddress API 3、基于UDP socket写一个回显服务器 3.1、服务器端 3.2 、客户端 3.3、完…

机器学习常识 21: 卷积神经网络

摘要: 卷积操作保留体现了空间相关性. 1. 卷积操作 图 1. 卷积操作 Valid 方式. 图 1 下平面标定的 3 3 3 \times 3 33 区域, 对应于个 3 3 3 \times 3 33 卷积, 这 9 9 9 个数对应着相乘, 然后相加, 获得了上平面标定的 1 1 1 个小区域的值. 注意这里不是矩阵的乘法. 卷…

《大数据技术与应用》课程实验报告|week12|实验8|Pig——高级编程环境 验证评估函数

目录 一、实验内容 二、实验目的 三、实验设备 四、实验步骤 步骤一 步骤二 步骤三 步骤四 步骤五 步骤六 步骤七 步骤八 步骤九 步骤十 步骤十一 步骤十二 步骤十三 步骤十四 步骤十五 步骤十六 五、实验结果 六、实验小结 一、实验内容 验证19.5节中的…

Apache网页的日志分割与优化

Apache网页的日志分割与优化 一、日志分割的作用二、rotatelogs 分割1.修改apache服务的主配置文件2.创建分割日志保存目录3.浏览器访问 三、AWStats 分析系统1.将安装AWStats 所需软件包传到/opt目录下2.安装 AWStats 软件包3.为要统计的站点建立配置文件4.修改自动生成的 aws…

电表的698通信协议

原文连接:https://blog.csdn.net/ss86655/article/details/109997891 该协议规定了用电信息的数据交换过程,一般用于主站与电能表之间、终端与电能表之间的数据交换,主站与终端一般用不同的客户机地址来区分。1、通信架构 有两种方向的数据…

linux系统中代码突然无法执行,没有权限访问文件,但是可以在文件管理器中查看文件

前言 这段时间遇到了一个非常离谱的问题,我的只要设计移动硬盘中数据的所有代码突然无法运行,我折腾了很久一直觉得是移动硬盘坏了,但拿到其他电脑上去是可以运动的。今天终于偶然发现了问题。 直接说结论: 移动硬盘的挂载点变了…

SQL-DML、DQL查询数据

SQL-DML、DQL查询数据 1 DML DML主要是对数据进行增(insert)删(delete)改(update)操作。 1.1 添加数据 给指定列添加数据 INSERT INTO 表名(列名1,列名2,…) VALUES(值1,值2,…);给全部列添加数据 INSERT…

MySQL数据库性能优化技巧介绍

MySQL是目前最流行和广泛使用的开源关系型数据库之一,随着数据量的增长和访问负载的提高,优化数据库性能变得至关重要,以确保系统能够高效地处理大量的并发请求。本文将记录一些MySQL数据库性能优化的技巧,提高数据库的运行效率&a…