面向面试知识-Redis
什么是Redis
运行于内存的基于key-value的非关系型数据库。
一款开源的内存数据结构存储,用作数据库、缓存、消息代理等。(可以基于Redis实现分布式锁、以及消息队列)
发布订阅??
对数据类型的操作都是原子性的,因为执行命令由单线程负责,不存在并发竞争的问题。
除此之外,Redis还支持:
Redis与Memcached的区别?
Redis和Memcached的共同点:
- 都是基于内存的数据库,性能都很高,一般可以用做缓存;
- 都有过期策略。
Redis和Memcached的区别:
- Redis支持的数据类型更加丰富(String、Hash、Set、List、Zset);而Memcached只支持最简单的key-value数据类型;
- Redis支持数据的
持久化,如何持久化??
,可以将内存中的数据保持在磁盘中,重启时可以再次加载使用;而Memcached没有持久化功能; - Redis原生支持集群;
- Redis支持订阅模型、Lua脚本、事务等功能,而Memcached不支持。
为什么使用Redis作为MySQL的缓存
主要是因为高性能和高并发
- Redis具备高性能
运行在内存的key-value缓存,读取速度快。
但是需要一定的方法解决Redis和MySQL数据库数据一致性的问题。 - Redis具备高并发
单台设备的Redis的QPS(Query Per Second),是MySQL的10倍,Redis单机的QPS能轻松破10w,而MySQL单机的QPS很难破1W。
因此可以考虑把数据库的部分数据转移到缓存中,这样会直接到缓存而不用经过数据库。
Redis的数据类型
类型类型:存储的值:结构的读写能力;
String字符串;缓存对象、常规计数、分布式锁、共享session信息等;
Hash哈希;缓存对象、购物车等??
List列表;消息队列(有两个问题①生产者需要自行实现全局唯一;②不能以消费组形式消费数据等;);
Set集合;聚合计算(并集、交集、差集)场景,比如点赞、共同关注、==抽奖活动,为嘛?==等
Zset有序集合;排序场景,比如排行榜、电话和姓名排序等。
Redis常见数据类型:
数据类型:存储的值:结构的读写能力
Redis各数据类型的数据结构实现:
- String类型内部实现:
主要是SDS,Simple Dynamic String,简单动态字符串。
- SDS不仅可以保存文本数据,还可以保存二进制数据。
- SDS获取字符串长度的时间复杂度为O(1)。
- Redis的SDS API是安全的,拼接字符串不会造成缓冲区溢出。
- List类型内部实现:
底层数据结构是由双向链表或压缩列表实现的:
- if 列表元素个数小于512,每个item的值都小于64字节,Redis会使用List类型的底层;
- else 使用双向链表;
在Redis3.2之后,List数据类型的底层DS只由quickList实现。
- Hash类型内部实现:
压缩列表或者哈希表
- if 列表元素个数小于512,且所有值小于64字节,用压缩列表;
- else 使用Hash表;
Redis 7.0之后只由quickList实现
- Set类型内部实现:
Hash表或者整数集合实现
- if 集合中的元素都是整数,且元素个数小于512,使用整数集合;
- else 使用哈希表;
- Zset类型内部实现
压缩列表或者跳表:
- if 有序集合的元素个数小于128个,且所有元素的值小于64个字节时,使用压缩列表;
- else 使用跳表;
Redis7.0之后使用listpack实现。
Redis的线程模型
Redis是单线程吗?
在处理一系列请求时,即接受客户端请求,解析请求,进行数据读写等操作,返回数据给客服端的过程,是单线程;
但是Redis程序并不是单线程的,Redis在启动的时候,会启动后台线程(BIO)。
- Redis在2.6版本加了两个后台线程,分别处理关闭文件、AOF刷盘两个任务;
- Redis 4.0之后,新增了一个后台线程,用来异步释放Redis内存,也就是lazyfree线程。
详细内容见Redis 常见面试题
之所以 Redis 为「关闭文件、AOF 刷盘、释放内存」这些任务创建单独的线程来处理,是因为这些任务的操作都是很耗时的,如果把这些任务都放在主线程来处理,那么 Redis 主线程就很容易发生阻塞,这样就无法处理后续的请求了。
此图太大了,内容有点多,慢慢看一下;
为什么Redis采用单线程都能这么快?
- 运行在内存;
- 非多线程,避免了多线程竞争,免去了线程切换等的资源消耗;
- 采用I/O多路复用处理大量的客户端Socket请求;
Redis在6.0之后引入了多线程,但是主要面向I/O,采用了多个I/O线程来处理网络请求,因为随着网络硬件的性能提升,Redis的性能瓶颈有时会出现在网络I/O的处理上。
Redis持久化
Redis如何实现数据不丢失
Redis运行在内存,当重启或者崩溃,内存中的数据会丢失,因此需要将数据保存至磁盘实现数据的持久化,在Redis重启后重新恢复保存在磁盘中的数据,避免数据丢失。
三种持久化方式:
- AOF日志:每执行一条写操作,就将该命令以追加的方式写入一个特定文件中。
- RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘。
- 混合持久化方式:集成了AOF和RDB的优点。
AOF日志是如何实现的?
AOF,Append only File(追加文件)
记录写操作命令到一个文件中,然后Redis重启时,读取文件记录的命令,并逐一执行命令,进行数据恢复。
为什么先执行命令再保存到文件?
- 避免额外的检查开销:防止保存完执行过程发现语法错误等;
- 不会阻塞写命令的执行:;
可能的风险: - 数据可能会丢失:两个过程,如果写文件过程中,服务器宕机,那么数据就会有丢失的风险;
- 可能阻塞其他操作:因为AOF日志是需要主线程执行的;
AOF的写回策略有哪些?
内核缓冲区的数据何时写入硬盘?
- Always,
- Everysec,
- No,
AOF文件过大时,会出发AOF重写机制。
即去除冗余操作,精简文件内容。
RDB是如何实现的?
RDB,Redis DataBase,内存快照。
上两部分见参考:Redis 如何实现数据不丢失?
RDB的优点是:数据恢复速度快,但是快照的时机把握是一个比较难的问题;
AOF的优点是:丢失数据少,但是恢复慢;
;
混合持久化
发生在AOF日志重写过程。
使用混合持久化的AOF文件,前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。
优点:
- 前半部分是RDB数据,所以加载速度快;
- 后半部分是AOF数据,加载完RDB数据,再加载AOF,所以数据的完整性较好,丢失的数据少。
缺点: - AOF文件中加入RDB文件,可读性变差;
- 兼容性差,不能用于Redis4.0之前的版本;
Redis集群
主从复制
主从服务器的命令复制是异步进行的。无法实现强一致性,数据不一致在所难免。
哨兵模式
主从复制模式有一个问题,当主节点宕机时,需要手动恢复。为了自动恢复,Redis增加了哨兵模式,因为哨兵模式可以监控主从服务器,且提供主从节点故障转移功能。
切片集群模式
当Redis缓存数据量大到一台服务器无法缓存时,就需要使用Redis切片集群。
使用哈希槽
的方案,
Redis的内存淘汰策略
Redis使用的过期删除测试是惰性删除+定期删除
-
惰性删除:只有被访问时,才会检查过期时间,若过期则删除。问题:如果一直不被访问,就一直无法删除。
-
定期删除:每过一段时间,随机从数据库中取出一定数量的key值进行检查,删除过期的key。
-
redis的定期删除:如上图
缺点是无法准确的设定删除执行时长和频率。
持久化时,对过期键的处理
RDB:
AOF:
Redis如何保证数据一致性
Redis如何解决数据一致性问题?
先更新数据库,再更新缓存
先更新缓存,再更新数据库
先删除缓存,再更新数据库
先更新数据库,再删除缓存
延迟双删