导图社区 Redis面试宝典
本思维导图主要从Redis基础、Redis持久化、IO多路复用、Pipeline、Redis哨兵、Redis集群部署、Redis面试等方面进行分析,希望对你有帮助!
编辑于2021-09-17 10:10:15Redis基础知识
入门使用
nil:表示null的意思,当获取不到值时会返回nil
连接redis服务器:redis-cli
如何验证连接成功?
输入 ping 回车,如果返回 pong 说明成功了
获取key
# 获取所有的key(注意:如果数据量过大,会导致卡死,所以数据量大时建议使用scan命令) > keys * # 查询当前库一共有多少个key > dbsize # 查询所有库分别有多少个key(只要记住info即可,一个info命令可以看好多内容,keyspace只是其中一个) > info keyspace
Memcache和Redis的区别
Memcache-使用简单,有点类似于Hash
01:支持简单的数据类型
02:不支持持久化存储
03:不支持主从(即类似于mysql那样的主从同步方案)
04:不支持分片(可以理解为将大数据分布到多个物理节点的方案)
05:单个value只能保存1MB以内的数据
Redis
01:数据类型丰富,比如set、list等
02:支持持久化存储
03:支持主从(即类似于mysql那样的主从同步方案)
04:支持分片
05:单个value能保存几百兆甚至1G的数据
Redis为什么这么快?
C语言编写,完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高,避免了磁盘的io
数据结构简单,对数据的操作也简单:Redis没有表的概念,也没有表以及数据的关联,其存储结构就是键值对,所以其性能相比传统的关系型数据库要高了很多很多;
采用单线程处理客户端的请求,即所有客户端的请求都是由一个主线程进行串行处理。因此多个客户端对同一个键进行写操作的时候就不会有并发的问题,也不会牵扯锁的问题;从而避免了频繁的上下文切换和锁竞争;
采用多路I/O复用模型,非堵塞IO
多路I/O复用
为什么使用多路IO复用
Redis处理客户端请求是采用的单线程,所有的操作都是按照顺序线性执行的; IO操作是堵塞的,所以某一个操作的IO发生了堵塞,会导致整个进程的堵塞; IO复用就是为了解决这个问题。
原理:根据平台不同选用不同的IO多路复用函数,以实现最高的效率; 优先选择时间复杂度为O(1)的函数; 以select函数作为保底(select函数的时间复杂度是O(n)); 基于react设计模式来监听IO事件;
另一种解释
多路指的是多个socket连接,复用指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),且Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈),主要以上两点造就了Redis具有很高的吞吐量。
你用过那些Redis的数据类型?
String-字符串
存储的就是key-value键值对
value值最大可以存储512M
value值可以是二进制的,也就是说可以存储图片、文件流等内容;
value值可以是Integer类型的,并且可以通过incr来实现计数器功能;
# 设置count的值为1 > set count 1 # 另count加1 > incr count # 此时获取到的值变成了2 > get count # 再加1 > incr count # 获取到的值变成了3 > get count
Hash-哈希
存储的是String类型的键值对,特别适合存储对象
一般会把对象序列化之后变成json,这时候存储到redis的hash中非常合适
使用方式
hmset:存储对象; hgetall:获取对象所有属性; hget:获取对象某个属性; hset:设置对象某个属性; 具体使用实例如下: 127.0.0.1:6379> HMSET redis name "zhangsan" sex "nan" age 19 father "lisi" OK 127.0.0.1:6379> hgetall redis 1) "name" 2) "zhangsan" 3) "sex" 4) "nan" 5) "age" 6) "19" 7) "father" 8) "lisi" 127.0.0.1:6379> hget redis name "zhangsan" 127.0.0.1:6379> hget redis father "lisi" 127.0.0.1:6379> hset redis age 20 (integer) 0 127.0.0.1:6379> hget redis age "20" 127.0.0.1:6379>
List-列表
字符串列表,按照插入顺序排序,先入后出;
由于后入先出的特性,可以用于实现最新消息排行;
与Set相比,List允许重复元素存在;
使用方式
利用List的后入先出特性实现来实现统计最新消息; 每次新来的消息lpush到列表里; 然后使用lrange命令来获取最新的消息; 如下所示: # 有了新消息,用lpush即可; lpush message "A is coming;" lpush message "B is coming;" lpush message "C is coming;" lpush message "D is coming;" lpush message "E is coming;" lpush message "F is coming;" lpush message "G is coming;" # 要获取最新的几条消息,用lrange即可 lrange message 0 3 # 要清空所有消息,用ltrim即可 ltrim message 1 0
Set-集合
存储的是String的无序集合;
其成员是唯一的,即不允许存在重复的元素;
通过哈希表实现,所以添加、删除、查找的复杂度都是O(1);
与List相比,Set不允许重复元素存在;
集合可以实现求交集、并集等操作,可以据此实现共同关注人等功能;
使用方式
# 插入元素,如果成功则返回1,失败返回0 > sadd student zhangsan (integer) 1 > sadd student lisi (integer) 1 > sadd student wangwu (integer) 1 # 插入重复的元素,返回0 > sadd student zhangsan (integer) 0 # 查看所有的元素 > smembers myset
SortedSet-有序集合
也是string类型元素的集合,且不允许重复的成员
每个元素都会关联一个double类型的分数,正是通过分数来为集合中的成员进行从小到大的排序
有序集合的成员是唯一的,但分数(score)却可以重复
如何做持久化?
RDB快照持久化:保存某个时间点的全量数据快照
简介
数据保存在dump.rdb文件中。 redis在启动的时候,如果检测到有dump.rdb文件,会自动载入文件里的数据。 可以通过配置文件里配置RDB的策略,比如: save 900 1 # 如果在900s内有一次写入请求,则进行一次备份 save 300 10 # 如果在300s内有10次写入请求,则进行一次备份 save 60 10000 # 如果在60s内有10000次写入请求,则进行一次备份 1、以上配置在持久化时都是自动触发的bgsave操作,而不是save操作,所以不会对正常的读写请求产生影响 2、为什么要配置这么多呢,因为Redis每个时段的读写请求是不均衡的,这样配置的好处是尽可能兼顾性能和数据备份。
通过save命令进行RDB持久化——不推荐
save方式让主线程来进行持久化,此时就会堵塞Redis的正常读写请求,所以正常情况下不推荐使用save
通过bgsave命令进行持久化——推荐
其原理是fork出一个新的线程来持久化,不影响主线程。
缺点: 1、由于是全量持久化,如果数据量大的话会由于大量的磁盘IO而影响性能; 2、由于两次持久化之间有时间间隔,如果redis突然挂掉,会存在丢失数据的风险;
AOF持久化:保存写状态(即保存除了查询之外的所有变更)
简介
AOF(Append-Only-File) * 记录所有非查询指令; * 以append的形式增量追加到aof文件中; * AOF持久化默认是关闭的,需要修改配置文件来打开; aof文件恢复的过程其实就是逐条执行指令的过程;
如果每个写命令都记录的话,会不会造成aof文件很大?
不会,因为aof有自动汇总能力; 比如,对同样的变量连续赋值100次,那么会在aof里记录100个命令; 每隔一段时间,redis会自动创建一个新的线程来汇总并重写aof文件; 1、新线程会创建一个新的临时文件,并从老的aof文件中把可以汇总的命令汇总后写入新的临时文件; 2、新的临时文件汇总完成后,redis会将老文件里这段时间的增量也同步到新文件中,然后删除老文件;
优缺点
RDB优点:全量数据快照,文件小,恢复快
RDB缺点:无法保存最近一次快照之后的数据
AOF优点:可读性高,适合保存增量数据,数据不易丢失
AOF缺点:文件体积大,恢复时间长
Redis4.0开始支持RDB-AOF混合持久化方式,并且作为默认的配置来使用,而且能很好的处理同时存在rdb文件和aof文件时数据的恢复;
是否了解Redis的PipeLine?
背景
1、redis客户端执行一条命令分4个过程:“发送命令->命令排队->命令执行->返回结果”,其中发送命令和返回接口都是需要网络开销的;
2、尽管有mget、mset等原生批命令,但是大部分命令并不支持批量操作;
3、pipeline是需要客户端和服务端相互配合的,也就是客户端有专门的pipeline的处理方式,服务端接收到pipeline的处理请求后,也有专门的处理方式;
4、实际使用中是需要关机客户端(即jedis等各个语言的相关组件)的实现即可。
事务问题
1、由于pipeline是多条命令的组合,所以可能导致事务问题;
2、为了保证pipeline操作的原子性,redis提供了简单的事务机制;
3、一组需要一起执行的命令放到multi和exec两个命令之间,其中multi代表事务开始,exec代表事务结束。
4、redis对pipeline只是提供了简单的事务控制,并不支持事务回滚等高级操作;
是否了解Redis的主从同步?
https://blog.csdn.net/weixin_42711549/article/details/83061052
看看4-11
Master-Slave
Master为写服务器,其余为读服务器; 第一次,Master将rdb文件同步给Slave,完成数据初始化; 后续Master将aof文件同步给Slave,保证数据一致性;
哨兵机制:保证主从同步正常进行
一旦检测到Master挂了,会自动选举一个Slave升级成Master并提供写服务,同时发出警告通知管理员;
缓存穿透、击穿、雪崩:https://blog.csdn.net/kongtiao5/article/details/82771694
Redis
第1组
Redis简介
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)
Redis是单线程的吗?
处理多个客户端请求(也就是网络请求)时,是单线程的。好处是避免了频繁的上下文切换,也避免了锁竞争;
其他处理是支持多线程的,比如持久化时就可以在不影响正常客户端请求的前提下,以子进程或子线程来处理;
如何从海量数据里查询某一固定前缀的key? ——需要看数据量大小
数据量不大时:keys命令
使用:keys patern
举例: # 列出所有XX开头的key命令 > keys XX*
存在问题:keys命令会扫描所有的key,并输出所有的结果。如果数据量比较大,会占用很大的资源,造成环境卡顿;
数据量很大时:scan命令; scan命令使用游标查找,每次只返回少量数据而不是进行全盘扫描。
说明: 1、基于游标的迭代器,需要基于上一次的游标来延续之前的迭代过程; 2、以0作为游标开始一次新的迭代,直到命令返回游标0表示完成了一次遍历; 3、一次返回的数量不可控,即使指定了count也不能保证每次执行都返回count个元素,只能大概率符合count参数 4、由于count元素并不准确,所以实际使用时需要应用程序循环遍历,最好自己排下重;
使用:SCAN CURSOR [MATCH pattern] [COUNT count]
举例: # 从0开始迭代,查询开头为XX的key,期望每次返回10个结果(实际不一定真的返回10个) > sacn 0 match XX* count 10
如何实现分布式锁?
如何实现异步队列? 订阅
错误方式一:使用List作为队列,RPUSH生产消息,LPOP消费消息
缺点:不管队列里有没有值,LPOP都会直接消费,不会等待有值了再消费;
弥补:可以通过在应用层引入Sleep机制去调用LPOP重试
错误方式二:使用List作为队列,RPUSH生产消息,BLPOP消费消息
优点:BLPOP会堵塞知道队列中有消=消息或者超时为止;
缺点:只能供一个消费者消费;
正确方式:pub/sub主体订阅者模式
发送者(pub)生产并发送消息;订阅者(sub)接收消息
多对多关系;
缺点:消息的发布是无状态的,无法保证可靠性;比如某个订阅者在发送者发送消息时下线了,那么该订阅者再次上线后是收不到这个消息的;
要保证可靠性的话,只能使用专业的消息队列来解决,如kafka等
如何用Redis来统计某个用户每天的访问次数?
用userId和日期的组合字符串作为key,用incr累加即可; # 每次用户访问时,使用incr累加一下即可。incr 命令如果key不存在时可以直接创建 > incr kxsh20180101 # 查询这个用户某天的访问次数 > get kxsh20180101
如何清空Redis的List?
ltrim key start stop:对一个List进行剪切,只保留start到stop之间的元素,其余元素将被删除; 所以如果想清空一个List,只需要让start>stop即可。 比如要清空students这个List,可以使用如下命令 > ltrim students 1 0
如果实现最新消息排行?
利用List的后入先出特性实现; 每次新来的消息lpush到列表里; 然后使用lrange命令来获取最新的消息; 如下所示: # 有了新消息,用lpush即可; lpush message "A is coming;" lpush message "B is coming;" lpush message "C is coming;" lpush message "D is coming;" lpush message "E is coming;" lpush message "F is coming;" lpush message "G is coming;" # 要获取最新的几条消息,用lrange即可 lrange message 0 3 # 要清空所有消息,用ltrim即可 ltrim message 1 0
如何实现微博的共同关注人?
将A的所有关注人放在一个set里; 将B的所有关注人放在另一个set里; 求这两个set的交集即可;
生产环境如何禁用危险命令?
在配置文件中将高危命令重命名成空即可; rename-command KEYS "" // 必禁命令,线上用这种查询方式绝对是不对的 rename-command FLUSHALL "" // 必禁命令,谁会清除数据呢 rename-command FLUSHDB "" // 必禁命令,谁会清除数据呢 rename-command CONFIG "" // 可以考虑重命名下
Redis哨兵模式了解吗?
参考:https://www.cnblogs.com/jaycekon/p/6237562.html
Redis如何实现高可用?
第2组
redis和etcd都是key-value,他们有什么区别?
redis的批处理命令有哪些?
redis的批处理命令和pipeline有何区别?