目录
- 1. 简单介绍一下 Redis 呗!
- 2. 分布式缓存常见的技术选型方案有哪些?
- 3. 说一下 Redis ,本地缓存和 Memcached 的区别和共同点
- 3. 1 共同点
- 3. 2 区别 :
- 4. 缓存数据的处理流程是怎样的?
- 5. 为什么要用 Redis/为什么要用缓存?
- 6. Redis 除了做缓存,还能做什么?
- 7. Redis 常见数据结构以及使用场景分析
- 7. 1:String(simple dynamic string,SDS)
- 7. 2:list(双向链表)
- 7. 3 hash表
- 7. 4 set
- 7. 5 sorted set
- 7. 6 bitmap
- 8. Redis 单线程模型详解
- 8. 1. 单线程的原因:
- 8.2. 既然是单线程,那怎么监听大量的客户端连接呢?
- 8.3. Redis需要处理的事件?
- 8.4. 文件事件处理器(file event handler)4 个部分:
- 8.5. Redis 没有使用多线程?为什么不使用多线程?
- 8.6. Redis6.0 之前 为什么不使用多线程?
- 8.7. Redis6.0 之后为何引入了多线程?
- 9. Redis 给缓存数据设置过期时间有啥用?
- 10. Redis 是如何判断数据是否过期的呢?
- 11 .过期的数据的删除策略了解么?
- 12 . Redis 内存淘汰机制(8种机制)
- 13 . Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
- 13.1 快照持久化(RDB)(Reids默认采用)
- 13.2 AOF(append-only file)持久化
- 13.3 AOF与RDB区别:
- 13.4 现实使用的方法
- 13.5 Redis 4.0 加强方法
- 14. Redis bigkey
- 14. 1 什么是bigkey?
- 14. 2 bigkey的危害?
- 14. 3 如何发现 bigkey?
- 15 Redis 事务?
- 15.1 Redis 事务的四个命令?
- 15.2 Redis 事务的特点?
- 15.2 Redis 为什么没有回滚?
- 16. Redis 可以做消息队列么?
- 17. 缓存穿透?
- 17.1 什么是缓存穿透?
- 17.2 如何解决缓存穿透问题?
- 17.3 布隆过滤器原理
- 18. 缓存雪崩?
- 18.1 什么是缓存雪崩?
- 18.2 解决办法?
- 18. 缓存雪崩,缓存击穿和缓存穿透区别
- 19. 如何保证缓存和数据库数据的双写一致性
- 19.1 一致性
- 19.2 双写一致性策略
- 20. 3种常用的缓存使用策略(读写策略)
- 20.1 Cache Aside Pattern(旁路缓存模式)
- 20.1.1 写数据
- 20.1.2 读数据
- 20.1.3 写数据存在的问题?
- 20.1.4 Cache Aside Pattern 的缺陷
- 20.2 Read/Write Through Pattern(读写穿透)
- 20.2 .1 写
- 20.2 .2 读
- 20.3 Write Behind Pattern(异步缓存写入)
- 21. Redis内存碎片
- 21.1 为什么会有 Redis 内存碎片?
- 21.3 如何查看 Redis 内存碎片的信息?
- 21.4 如何清理 Redis 内存碎片?
1. 简单介绍一下 Redis 呗!
- 定义:就是一个C语言开发的数据库。特点是数据存储在内存当中,即内存数据库。
- 特点:由于在内存当中,故 读写速度非常快,被广泛应用于缓存当中。
- 应用:缓存,分布式锁,消息队列。
- 功能:提供多种数据类型支持不同的业务场景。Redis还支持事务,持久化,Lua脚本,多种集群方案。
2. 分布式缓存常见的技术选型方案有哪些?
- 问题:分布式缓存需要解决的问题(针对与本地缓存)?
1)单机缓存的容量限制,无法保存通用信息。
2)只在当前服务有效,如果部署了两个相同的服务,两者之间的缓存不能够进行交互。 - 解决工具:使用比较多是: Memcached 和 Redis,目前几乎都是在用后者。
3. 说一下 Redis ,本地缓存和 Memcached 的区别和共同点
3. 1 共同点
- 基于内存的数据库
- 都有过期策略
- 两者的性能都非常高
3. 2 区别 :
- Redis支持丰富的数据类型:支持更加复杂的应用场景。
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中。支持重启加载再使用。
- Redis有灾难恢复机制。
- Redis 有原生的集群模式。
- IO模型:
(1):Redis 使用单线程的多路 IO 复用模型,后面也引入了多线程IO模型。
(2):Memcached :多线程,非阻塞IO复用模型。 - 过期数据的删除策略:
(1):Redis:惰性删除与定期删除
(2):Memcached :惰性删除
4. 缓存数据的处理流程是怎样的?
- 首先查找缓存中是否存在数据(内存中搜索)
- 再查找磁盘中是否存在数据(在磁盘中搜索)
- 如果在上述两个地方都没找到,返回空数据。
5. 为什么要用 Redis/为什么要用缓存?
- 使用缓存主要是为了提升用户体验(高性能)以及应对更多的用户(高并发)。
6. Redis 除了做缓存,还能做什么?
- 分布式锁 :通过 Redis 来做分布式锁是一种比较常见的方式
- 限流:一般通过Redis+Lua脚本的方式来实现限流。
- 消息队列:Redis自带的list数据结构可以作为一个简单的消息队列来进行使用。Redis5.0增加了数据结构Streanm类型,更加适合作为消息队列。
- 复杂业务场景:使用提供的多种数据结构。
7. Redis 常见数据结构以及使用场景分析
7. 1:String(simple dynamic string,SDS)
- 介绍:简单的 key-value 类型;不光可以保存文本数据还可以保存二进制数据;Redis 的 SDS API 是安全的;会造成缓冲区溢出。
- 常用命令:set,get,strlen,exists,decr,incr,setex 等等。
- 应用场景:需要计数的场景:用户访问的次数和热点文章的点赞转发数量。String在redis是可变的,当雨挡增1减1的操作时,会变为int类型操作。
7. 2:list(双向链表)
- 介绍:双向链表,即可以支持反向查找和遍历,是一个按照插入顺序排序的列表。
- 常用命令:rpush,lpop,lpush,rpop,lrange,llen。
- 应用场景:发布与订阅,或者说是消息队列和慢查询。实现队列,实现栈,实现范围查找。朋友圈的点赞列表、评论列表、排行榜。
7. 3 hash表
- 介绍:hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)
- 常用命令:hset,hmset,hexists,hget,hgetall,hkeys,hvals
- 应用场景:系统中对象数据的存储。(参考文章三)
1)购物车:hset [key] [field] [value] 命令, 可以实现以用户Id,商品Id为field,商品数量为value,恰好构成了购物车的3个要素
2)存储对象:hash类型的(key, field, value)的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。
7. 4 set
- 介绍:set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序不重复集合
- 常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
- 应用场景:需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景。好友、关注、粉丝、感兴趣的人集合。存储某活动中中奖的用户ID(不会重复的特点)
- sinter命令可以获得A和B两个用户的共同好友;
- sismember命令可以判断A是否是B的好友;
- scard命令可以获取好友数量;
- 关注时,smove命令可以将B从A的粉丝集合转移到A的好友集合
- Redis 多次set同一个key,怎么保证只在第一次设置成功,其他时候都设置失败?
1)分布式锁:加上NX(只有键不存在的时候才进行操作)
2)额外:可以加上设置过期时间EX - Redis并发修改一个值,如何进行解决
1)分布式锁:准备一个分布式锁,大家去抢锁,抢到锁就做set操作。Redis(setnx命令);Zookeeper(临时节点)
2)消息队列:把Redis.set操作放在队列中使其串行化,必须的一个一个执行。 - 客户端 1 加锁的锁 key 默认生存时间才 30 秒,如果超过了 30 秒,客户端 1 还想一直持有这把锁,怎么办呢?(续期机制)
1)续期机制:Watch Dog 机制其实就是一个后台定时任务线程,获取锁成功之后,会将持有锁的线程放入到一个MAP里面,然后每隔 10 秒 检查一下,如果客户端 还持有锁 key,就会延长 key 的时间。
2)缺陷:watch dog 机制启动,且代码中没有释放锁操作时,watch dog 会不断的给锁续期;
7. 5 sorted set
- 介绍:sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
- zadd,zcard,zscore,zrange,zrevrange,zrem 等。
- 对数据根据某个权重进行排序的场景;例如:直播系统中,在线用户列表,各种礼物排行榜,弹幕消息。
1)zadd [key] [score] [value] 向指定key的集合中增加元素 - Sorted set的底层数据结构?
1)ziplist:小规模,每个集合元素使用两个紧挨在一起的压缩列表结点来保存。
2)跳表skiplist:大规模(类似于链表的二分查找)
(1)跳跃表是一种基于有序链表的扩展,简称跳表.跳表会维护多个索引链表和原链表.;不断得提升新的关键节点形成新的有序链表,通过空间换时间. - Sorted set底层为什么用跳跃表而不是平衡树,红黑树这些?
1)在增删改方面,红黑树和调表不相上下,但是在按照区间查找方面,调表效率更好。
2)跳表的代码简单;并且增加新节点的策略可以改变。
3)跳表和红黑树相比二叉查找树的有点是速度快;
4)跳表需要维持有序,并且空间复杂度更高,二叉树不需要。
3)跳表的时间复杂度:插入和删除的空间复杂度:O(logN);空间复杂度O(n);典型的以空间换时间。
7. 6 bitmap
- bitmap 存储的是连续的二进制数字(0 和 1)
- 常用命令: setbit 、getbit 、bitcount、bitop
- 应用场景:适合需要保存状态信息,方便后续的使用。
(1):用户行为分析 很多网站为了分析你的喜好,需要研究你点赞过的内容。
(2):统计活跃用户 很多网站为了分析你的喜好,需要研究你点赞过的内容。
(3):用户在线状态
思考一下为什么这样很是方便进行查找。
8. Redis 单线程模型详解
8. 1. 单线程的原因:
- Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型 ;
- 这套事件处理模型对应的是 Redis 中的文件事件处理器;
- 文件事件处理器(file event handler)是单线程方式运行的
- 故Redis是单线程的。
8.2. 既然是单线程,那怎么监听大量的客户端连接呢?
- 方法:IO 多路复用程序
- 好处:不需要额外创建多余的线程来监听客户端的大量,进而节约资源的消耗。
8.3. Redis需要处理的事件?
Redis服务器是一个事件驱动程序。
- 文件事件(最多):客户端进行读取写入等操作,涉及一系列网络通信。
- 时间事件:较少
8.4. 文件事件处理器(file event handler)4 个部分:
- 多个 socket(客户端连接)
- IO 多路复用程序(支持多个客户端连接的关键)
- 文件事件分派器(将 socket 关联到相应的事件处理器)
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
8.5. Redis 没有使用多线程?为什么不使用多线程?
- 有多线程:Redis 在 4.0 之后的版本中就已经加入了对多线程的支持;主要是针对一些大键值对的删除操作的命令;Redis 6.0 之前主要还是单线程处理。
8.6. Redis6.0 之前 为什么不使用多线程?
- 单线程编程容易并且更容易维护;
- Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
- 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
8.7. Redis6.0 之后为何引入了多线程?
- 主要是为了提高网络 IO 读写性能,瓶颈主要受限于内存和网络
- 读写使用多线程,但是执行还是单线程,故在运行时不存在线程安全问题。
9. Redis 给缓存数据设置过期时间有啥用?
- 原因:
(1): 内存是有限的,如果一直保留,非常容易满了。
(2): 短信验证码可能只在 1 分钟内有效,用户登录的 token 可能只在 1 天内有效。自动过期。不用单独设置功能。 - 设置过期时间方法:
(1):setex:字符串类型有自己独有设置过期时间
(2):expire:其他方法设置的过期时间‘
(3):persist:移除过期时间。
10. Redis 是如何判断数据是否过期的呢?
- 通过过期字典(hash表):
(1):key:Redis 数据库中的某个 key(键) ;
(2):value:数据库键的过期时间
11 .过期的数据的删除策略了解么?
常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):
- 惰性删除 :调用了Key值才进行判断是否过期;对CPU更加友好,但是导致很多没删掉
- 定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。通过删除操作执行的时间和频率进行限制。对内存更加友好,但是会对CPU产生影响。
- 混合制: Redis 采用的是 定期删除+惰性/懒汉式删除 。
- Redis 内存淘汰机制: 针对上述不完整的地方操作
12 . Redis 内存淘汰机制(8种机制)
>相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?【以下图片和文字参考文章二】
1)volatile为前缀的策略都是从已过期的数据集中进行淘汰。
2)allkeys为前缀的策略都是面向所有key进行淘汰。
3)LRU(least recently used)最近最少用到的。,笔试常考
4)LFU(Least Frequently Used)最不常用的。
5)它们的触发条件都是Redis使用的内存达到阈值时。
13 . Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
13.1 快照持久化(RDB)(Reids默认采用)
- 定义:Redis可以通过快照来获取存储在内存里面的数据在某个时间上的副本。
- 快照时间:在多少时间内,如果至少有K个key发生变化,那么就会自动触发快照,创建新的副本。
13.2 AOF(append-only file)持久化
- 定义:设定一个时间,把Redis中的数据自动同步到磁盘中去。
- 触发时机:每次有数据修改发生时都会写入;每1秒钟执行一次;让操作系统决定何时进行同步
- AOF重写:在进程运行的时候,维护一个AOF重写缓冲区,在进行结束的时候,用新的AOF文件取代旧的AOF文件。
13.3 AOF与RDB区别:
- RDB持久化间隔时间为五分钟,这五分钟之内数据可能会丢失;AOP按秒进行执行写入,数据丢失的少。
- RDB回复数据速度快;AOP存的是指令,还需要推演,花费时间。
- 原理:RDB对 redis 中的数据执行周期性的持久化;AOF 机制对每条写入命令作为日志,之后重新构建数据集。
13.4 现实使用的方法
综合上述两种方法,在现实过程中,使用AOF的频次更加高,每秒钟同步一次效果很好,Redis的性能不会受太大影响。
13.5 Redis 4.0 加强方法
RDB 和 AOF 的混合持久化
- AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。结合两者的有点,快速加载内容,但是由于把两种内容进行拼接,可读性较差。
14. Redis bigkey
14. 1 什么是bigkey?
- 如果一个key对应的value所占内存比较大,那么这个key就可以看做是bigkey了。
14. 2 bigkey的危害?
- 消耗更多的内存空间,对性能的影响也很大。
14. 3 如何发现 bigkey?
- 使用 Redis 自带的 --bigkeys 参数来查找。
(1):会扫描Redis中的所有的key值,会对Redis性能有一点影响,但是只能找出每种数据结构的最大bigkey。 - 分析 RDB 文件
通过分析RDB文件找出bigkey,前提是使用的是RDB持久化方法。
15 Redis 事务?
15.1 Redis 事务的四个命令?
- 指令描述:multi ;exec ;discard ;watch等命令
- 执行的流程
//1)事务开启
multi
//2)命令入队,使用先进先出的顺序进行。
....
....
//3)执行命令
exec
- discard命令:取消一个事务,清空事务队列中的所有指令。
- watch命令:用于监听指定的键,如果执行事务的时候,一个被watch监听的键发生了修改,那么整个事务都执行,直接返回失败。
15.2 Redis 事务的特点?
- 持久性,隔离性,一致性,没有原子性,因为没有回滚操作。
- Redis事务可以理解为,命令首先进行打包,按照顺序执行,中间不能够中断。
15.2 Redis 为什么没有回滚?
- 没有回滚,能够使得Redis更加简单和高效
- 开发者认为,命令执行错误应该在开发过程中就被发现,而不是生产过程中。
16. Redis 可以做消息队列么?
- Redis增加了一种数据结构Stream可以来做消息队列,Stream支持:
(1):发布/订阅模式
(3):发布/订阅模式
(3):数据持久化(RDB和AOP) - 还是不建议用Redis来作为消息队列,可以使用市面上成熟的消息队列:RocketMQ、Kafka。
17. 缓存穿透?
17.1 什么是缓存穿透?
- 定义:大量请求的key不在缓存中,导致数据请求直接落在了数据库上,没有经过缓存步骤。使得查找的性能大大降低。(容易被黑客攻击使用,造成系统的瘫痪)
17.2 如何解决缓存穿透问题?
- 缓存无效 key:即可以查找到缓存,然后写一个到redis中设置过期时间。但是无效的key会很占内存。
- 布隆过滤器:非常神奇的一种数据结构,能够很方便的判断一个数据是否在海量数据中,如果不存在,那么就是无效数据,不经过缓存和数据库。
(1):布隆过滤器缺点:会存在误判情况,因为布隆过滤器的原理就是哈希表,哈希表就会存在哈希冲突的问题,如果数据太大,没处理好,就可能会导致误判。
17.3 布隆过滤器原理
1.与哈希表很像,哈希表是一个哈希函数,布隆过滤器是多个哈希函数;所以存在误判率。
2. 添加与查询元素步骤
1)添加元素
(1)将要添加的元素给 k 个哈希函数
(2)得到对应于位数组上的 k 个位置
(3)将这k个位置设为 1
2)查询元素
(1)将要查询的元素给k个哈希函数
(2)得到对应于位数组上的k个位置
(3)如果k个位置有一个为 0,则肯定不在集合中
(4)如果k个位置全部为 1,则可能在集合中
18. 缓存雪崩?
18.1 什么是缓存雪崩?
- 场景一:缓存在同一时间大面积失效,使得查找都落到了数据库中,给数据库造成很大的压力,数据库支撑不住,非常容易宕机。
- 场景二:有一些被大量访问的数据**(热点缓存**)在某一个时刻大面积失效,使得查找都落到了数据库中。
18.2 解决办法?
- 针对场景一,Redis服务不可用情况:
(1):采用Redis集群,避免单机出现问题导致整体不能用。
(2):限流:避免同时处理大量的请求。 - 针对场景二:热点缓存失效
(1):设置热点缓存永不失效
(2):设置不同的失效时间,比如随机过期时间。
18. 缓存雪崩,缓存击穿和缓存穿透区别
(1):缓存穿透:原本就没有的数据,请求如入无人之境,直奔数据库(增加过滤器合法校验,布隆过滤器;对空结果进行缓存,设置短有效期)
(2):缓存击穿:缓存击穿是指数据库原本有得数据,但是缓存中没有,一般是缓存突然失效了,这时候如果有大量用户请求该数据(热点数据不过期;数据为空,加锁,允许一个请求访问数据库)
(3)缓存雪崩:缓存击穿的放大版,多个缓存同时失效(热点数据永不失效;设置随机过期时间;双缓存)
19. 如何保证缓存和数据库数据的双写一致性
19.1 一致性
- 强一致性:承诺立即实现一致,性能要求太高,难以实现
- 弱一致性:不承诺立即实现,也不承诺什么时候实现,反正总能实现。
3 最终一致性(最常使用):弱一致性的特例,保证在一定时间内能够实现。
19.2 双写一致性策略
- 先更新数据库,再删除缓存(有一定的间隙会读到脏数据)
- 先删除缓存,再更新数据库,很大概率会吧脏数据作为缓存一直保留(不好)
- 缓存延时双删(改进方法二)
1)先删除缓存,再更新数据库,然后再删除缓存(时隔1s) - 删除缓存重试机制(针对删除缓存失败)
1)先更新数据库;再删除缓存
2)如果缓存删除失败:先把删除失败的key存入消息队列,然后取出消息队列,再进行删除操作(删除失败就多删除几次)
缓存读写:旁路缓存策略(读多写少),读写穿透策略
20. 3种常用的缓存使用策略(读写策略)
这3 种缓存读写策略各有优劣,不存在最佳,需要我们根据具体的业务场景选择更适合的。旁路缓存模式;读写穿透;异步缓存写入
20.1 Cache Aside Pattern(旁路缓存模式)
旁路缓存模式是一种平时使用比较多的缓存读写模式,比较适合读请求比较多的场景。
服务端需要同时维护DB和Cache,并且是以DB结果为准。
20.1.1 写数据
- 首先更新DB中的数据
- 直接删除cache中的该数据。
20.1.2 读数据
- 首先从Cache中读取指定数据,读取到就返回
- 如果在Cache中未读到,则从DB中读取数据并返回
- 把DB中读取到的数据存入Cache中
20.1.3 写数据存在的问题?
- 在写数据的过程中,可以先删除Cache,再更新DB吗?
(1):不可以。如果先删除,再更新,会产生大量的脏数据,并且会一直存在。
(2):例子:并发两个操作:更新和查询。(首先进行redis删除,然后更新DB;由于更新操作比较慢,故在查询过程中,会读取DB中的旧数据放入redis中,并且后续一直存在)
2. 在写数据的过程中,先更新DB,然后删除Cache,会存在什么问题?
(1):使用这种方法也还是可能会存在DB和Cache数据不一致的问题,但是只会出现在最开始。(付出的代价较小)
(2):例子说明:并发两个操作:更新和查询。(先对DB中的数据进行更新,在这个同时查询操作能够在缓存中找到旧数据,返回,会达到不一致;但是在后续的查询中,由于缓存会被删除,故可以得到数据库中的最新数据)
20.1.4 Cache Aside Pattern 的缺陷
- 缺陷一:首次请求的数据一定不在Cache中
(1):将热点数据提前存放进 - 缺陷二:写操作比较频繁的话,会导致cache被频繁的删除,降低缓存命中率。
(1):数据库和缓存数据库强一致:更新DB的同时也更新Cache.
(2):数据库和缓存数据库弱一致:更新DB的同时也更新Cache,但是给缓存一个比较短的过期时间。
20.2 Read/Write Through Pattern(读写穿透)
- 概念:服务器把Cache作为主要的数据存储,从中读数据和写数据,之后再把数据更新到DB中去,降低应用程序的职责。
- 在Redis中用的少:第一是性能问题;第二是Redis没有提供cache将数据写入DB的功能。
20.2 .1 写
- 先检查Cache中是否该数据,如果没有,直接更新DB中的数据
- 如果Cache中有该数据,则先更新Cache中的数据,然后Cache服务自己再更新DB(同步更新Cache和DB)
20.2 .2 读
- 首先查询Cache是否有数据,如果有,则直接返回
- 如果没有,会先从DB中加载,然后把数据写入到Cache中,再进行返回。
20.3 Write Behind Pattern(异步缓存写入)
- 定义:和读写穿透非常相似,都是Cache充当数据存储的主体,但是以下几点区别:
(1):读写穿透:首先读写穿透是同步更新Cache和DB的
(2):异步缓存写入:只是更新缓存,不及时更新DB,改为异步批量更新DB的方式。 - 异步缓存写入的优缺点:
(1):缺点:如果cache的数据没有及时更新到DB中,数据就会发生丢失
(2):优点:写性能非常高,非常适合数据经常变化,但是数据一致性要求没有那么高的场合(比如浏览量和点赞量)
21. Redis内存碎片
21.1 为什么会有 Redis 内存碎片?
- Redis存储数据的时候,向操作系统申请的内存空间可能会大于数据实际所需的存储空间。
- 频繁修改Redis中的数据也会产生内部碎片。
21.3 如何查看 Redis 内存碎片的信息?
使用 info memory 命令即可查看 Redis 内存相关的信息。
21.4 如何清理 Redis 内存碎片?
直接通过 config set 命令将 activedefrag 配置项设置为 yes 即可。
config set activedefrag yes