- 简单介绍⼀下 Redis 呗!
- 分布式缓存常⻅的技术选型⽅案有哪些
- 说⼀下 Redis 和 Memcached 的区别和共同点
- Redis 支持的数据类型有哪些?
- Redis 单线程模型详解
- Redis 没有使⽤多线程?为什么不使⽤多线程?
- Redis6.0 之后为何引⼊了多线程?
- Redis的过期键的删除策略
- 我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
- Redis 内存淘汰机制了解么?
- Hash 冲突怎么办?
- Redis的内存用完了会发生什么?
- Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进⾏恢复)
- Redis 事务
- 缓存穿透
- 缓存雪崩
- 如何基于Redis实现分布式锁?
- Redis 与 数据库的双写一致性?
- 如何保证redis中存放的都是热点数据?
- Redis 常见性能问题和解决方案?
- Redis事务及其相关面试题
简单介绍⼀下 Redis 呗!
简单来说 Redis 就是⼀个使⽤ C 语⾔开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度⾮常快,因此 Redis 被⼴泛应⽤于缓存⽅向。
另外, Redis 除了做缓存之外, Redis 也经常⽤来做分布式锁,甚⾄是消息队列。
Redis 提供了多种数据类型来⽀持不同的业务场景。 Redis 还⽀持事务 、持久化、 Lua 脚本、多种集群⽅案。
分布式缓存常⻅的技术选型⽅案有哪些
分布式缓存的话,使⽤的⽐较多的主要是 Memcached 和 Redis。不过,现在基本没有看过还有项⽬使⽤ Memcached 来做缓存,都是直接⽤ Redis。
分布式缓存主要解决的是单机缓存的容量受服务器限制并且⽆法保存通⽤的信息。因为,本地缓存只在当前服务⾥有效,⽐如如果你部署了两个相同的服务,他们两者之间的缓存数据是⽆法共同的。
说⼀下 Redis 和 Memcached 的区别和共同点
共同点:
- 都是基于内存的数据库,⼀般都⽤来当做缓存使⽤。
- 都有过期策略。
- 两者的性能都⾮常⾼。
区别:
- Redis ⽀持更丰富的数据类型(⽀持更复杂的应⽤场景)。 Redis 不仅仅⽀持简单的 k/v 类型的数据,同时还提供 list, set, zset, hash 等数据结构的存储。 Memcached 只⽀持最简单的 k/v 数据类型
- Redis ⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使⽤,⽽ Memecache 把数据全部存在内存之中。
- Memcached 是多线程,⾮阻塞 IO 复⽤的⽹络模型; Redis 使⽤单线程的多路 IO 复⽤模型。 (Redis 6.0 引⼊了多线程 IO )
- Memcached过期数据的删除策略只⽤了惰性删除,⽽ Redis 同时使⽤了惰性删除与定期删除。
Redis 支持的数据类型有哪些?
string 字符串
字符串类型是 Redis 最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的。字符串类型实际上可以是字符串:简单的字符串、XML、JSON;数字:整数、浮点数;二进制:图片、音频、视频。
Hash(哈希)
在 Redis中哈希类型是指键本身是一种键值对结构,如 value={{field1,value1},……{fieldN,valueN}}
使用场景:哈希结构相对于字符串序列化缓存信息更加直观,并且在更新操作上更加便捷。所以常常用于用户信息等管理,但是哈希类型和关系型数据库有所不同,哈希类型是稀疏的,而关系型数据库是完全结构化的,关系型数据库可以做复杂的关系查询,而 Redis 去模拟关系型复杂查询开发困难且维护成本高。
List(列表)
列表类型是用来储存多个有序的字符串,列表中的每个字符串成为元素,一个列表最多可以储存 2 ^ 32 - 1 个元素,在 Redis 中,可以队列表两端插入和弹出,还可以获取指定范围的元素列表、获取指定索引下的元素等,列表是一种比较灵活的数据结构,它可以充当栈和队列的角色。
Set(集合)
集合类型也是用来保存多个字符串的元素,但和列表不同的是集合中不允许有重复的元素,并且集合中的元素是无序的,不能通过索引下标获取元素,Redis 除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。合理的使用好集合类型,能在实际开发中解决很多实际问题。
使用场景:如:一个用户对娱乐、体育比较感兴趣,另一个可能对新闻感兴趣,这些兴趣就是标签,有了这些数据就可以得到同一标签的人,以及用户的共同爱好的标签,这些数据对于用户体验以及曾强用户粘度比较重要。
zset(sorted set:有序集合)
有序集合和集合有着必然的联系,它保留了集合不能有重复成员的特性,但不同得是,有序集合中的元素是可以排序的,但是它和列表的使用索引下标作为排序依据不同的是:它给每个元素设置一个分数,作为排序的依据。
使用场景:排行榜是有序集合经典的使用场景。例如:视频网站需要对用户上传的文件做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
Redis 单线程模型详解
Redis 基于 Reactor 模式来设计开发了⾃⼰的⼀套⾼效的事件处理模型,这套事件处理模型对应的是 Redis中的⽂件事件处理器。由于⽂件事件处理器(file event handler)是单线程⽅式运⾏的,所以我们⼀般都说 Redis 是单线程模型。
既然是单线程,那怎么监听⼤量的客户端连接呢?** **
Redis 通过IO 多路复⽤程序 来监听来⾃客户端的⼤量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发⽣。
这样的好处⾮常明显: I/O 多路复⽤技术的使⽤让 Redis 不需要额外创建多余的线程来监听客户端的⼤量连接,降低了资源的消耗(和 NIO 中的 Selector 组件很像)。
Redis 没有使⽤多线程?为什么不使⽤多线程?
⼤体上来说, Redis 6.0 之前主要还是单线程处理。
那, Redis6.0 之前 为什么不使⽤多线程?
我觉得主要原因有下⾯ 3 个:
- 单线程编程容易并且更容易维护;
- Redis 的性能瓶颈不再 CPU ,主要在内存和⽹络;
- 多线程就会存在死锁、线程上下⽂切换等问题,甚⾄会影响性能。
Redis6.0 之后为何引⼊了多线程?
Redis6.0 引⼊多线程主要是为了提⾼⽹络 IO 读写性能,因为这个算是 Redis 中的⼀个性能瓶颈
(Redis 的瓶颈主要受限于内存和⽹络)。
虽然, Redis6.0 引⼊了多线程,但是 Redis 的多线程只是在⽹络数据的读写这类耗时操作上使⽤
了, 执⾏命令仍然是单线程顺序执⾏。因此,你也不需要担⼼线程安全问题。
Redis的过期键的删除策略
常⽤的过期数据的删除策略就两个 :
- 惰性删除:只会在取出key的时候才对数据进⾏过期检查。这样对CPU最友好,但是可能会
造成太多过期 key 没有被删除 - 定期删除:每隔⼀段时间抽取⼀批 key 执⾏删除过期key操作。并且, Redis 底层会通过限
制删除操作执⾏的时⻓和频率来减少删除操作对CPU时间的影响
我们知道通过expire来设置key 的过期时间,那么对过期的数据怎么处理呢?
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:
- 定时去清理过期的缓存;
- 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
Redis 内存淘汰机制了解么?
Redis 提供 6 种数据淘汰策略:
- volatile-lru(least recently used) :从已设置过期时间的数据集(server.db[i].expires)
中挑选最近最少使⽤的数据淘汰 - volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
- allkeys-lru(least recently used) :当内存不⾜以容纳新写⼊数据时,在键空间中,移除
最近最少使⽤的 key(这个是最常⽤的) - allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报
错。这个应该没⼈使⽤吧!
4.0 版本后增加以下两种
- volatile-lfu(least frequently used) :从已设置过期时间的数据集(server.db[i].expires)中
挑选最不经常使⽤的数据淘汰 allkeys-lfu(least frequently used) :当内存不⾜以容纳新写⼊数据时,在键空间中,移
除最不经常使⽤的 keyHash 冲突怎么办?
Redis 通过链式哈希解决冲突:也就是同一个 桶里面的元素使用链表保存。但是当链表过长就会导致查找性能变差可能,所以 Redis 为了追求快,使用了两个全局哈希表。用于 rehash 操作,增加现有的哈希桶数量,减少哈希冲突。
Redis的内存用完了会发生什么?
如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可
以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进⾏恢复)
Redis 的⼀种持久化⽅式叫快照(snapshotting, RDB),另⼀种⽅式是只追加⽂件(append-only file, AOF)
快照(snapshotting)持久化(RDB)
RDB:RDB保存某一个时间点之前的快照数据。
默认方式
优点:性能最大化,fork子进程来完成写操作,让主进程继续处理命令,使用单独子进程来进行持久化,保证了redis的高性能。
- 当重启恢复数据的时候,数据量比较大时,Redis直接解析RDB二进制文件,生成对应的数据存储在内存中,比AOF的启动效率高。
缺点:
- 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障, 会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)
RDB持久化流程
AOF(append-only file)持久化
AOF:是指所有的命令行记录以 redis 命令请求协议的格式完全持久化存储)保存为 aof 文件。
优点:
1、数据安全, aof 持久化可以配置 appendfsync 属性, 有 always, 每进行一次命令操作就记录到
aof 文件中一次。
2、通过 append 模式写文件, 即使中途服务器宕机, 可以通过 redis-check-aof 工具解决数据一致性
问题。
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前( 文件过大时会对命令进行合并重写), 可
以删除其中的某些命令( 比如误操作的 flushall))
缺点:
1、AOF 文件比 RDB 文件大, 且恢复速度慢。
2、数据集大的时候, 比 rdb 启动效率低。
AOF持久化流程
Redis 事务
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
缓存穿透
什么是缓存穿透?
缓存穿透说简单点就是⼤量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本
没有经过缓存这⼀层。
有哪些解决办法?
最基本的就是⾸先做好参数校验,⼀些不合法的参数请求直接抛出异常信息返回给客户端。
1)缓存⽆效 key
将缓存和数据库中都找不到的Key缓存一份到Redis中,达到短时间不会访问数据库的目的
2)布隆过滤器
把所有可能存在的请求的值都存放在布隆过滤器中,当⽤户请求过来,先判断
⽤户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户
端,存在的话才会⾛下⾯的流程。
缓存雪崩
什么是缓存雪崩?
缓存在同⼀时间⼤⾯积的失效,后⾯的请求都直接落到了数据库上,造成数据库短时间内承受⼤量请求。
有哪些解决办法?
针对 Redis 服务不可⽤的情况:
- 采⽤ Redis 集群,避免单机出现问题整个缓存服务都没办法使⽤。
- 限流,避免同时处理⼤量的请求。
针对热点缓存失效的情况:
Redis 为单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对 Redis 的连接并不存在竞争关系。Redis 中可以使用 SETNX 命令实现分布式锁。一般使用 setnx(set if not exists) 指令,只允许被一个程序占有,使用完调用 del 释放锁。
高并发下,如何实现分布式锁?
Redis 与 数据库的双写一致性?
问题的根本原因:更新缓存的时间点与其他线程插入数据库的时间点不确定。
方案一: 分布式锁 — 可重入锁
在读的时候,使用Redisson 的读锁,在读的时候,使用Redisson 的写锁。
适合:读多写少。
方案二:设置缓存的超时时间
如何保证redis中存放的都是热点数据?
限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,留下热数据到内存。所以,计算一下 50W 数据大约占用的内存,然后设置一下 Redis 内存限制即可,并将淘汰策略为volatile-lru或者allkeys-lru。
Redis 常见性能问题和解决方案?
- Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件。如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次;
- 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内;
主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
Redis事务及其相关面试题
Redis事务的概念
Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。
redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。Redis事务的三个阶段
事务开始 MULTI
- 命令入队
- 事务执行 EXEC
事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排
Redis事务保证原子性吗,支持回滚吗
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。