1.Redis实现语言以及Redis是什么?
答:C语言;redis是非关系型的键值对数据库,可以根据键以o(1)的时间复杂度查询或插入关联值,是以内存方式存储的,并内置了复制,磁盘持久化、事务、SSL客户端代理等功能,通过哨兵和自动化分区提高可用性;
- String类型的底层数据结构及实现原理?
答:redis自定义了一个数据类型sds(simple dynamic string)简单的动态字符串,通过动态扩容机制减少内存浪费,也就是用存储的业务数据长度判断用什么方法存储;Stirng是由多个sdshdr5、sdshdr8、sdshdr16、sdshdr32等方法组成,每个方法中有len(长度)、char[]、flags组成,其中flags是由type(数据范围)和len(数据长度 )组成,flags是在真实数据往前偏移1个byte的位置;
3.redis默认的DB(数据库)有多少个?
答:一共16个, 都是redisDb存储的, redisDb的数据结构共有9个 :1)dict(字典):主要用于Key的索引; 2)expires:存储过期时间; 3)blocking_keys(阻塞):存储被阻塞的key; 4)ready_keys:存储的是可以解除阻塞的键, key通过该方式与客户端建立关系;5)watched_keys:存储的是正在被 watch 命令监控的键;6)id:记录的是当前数据库的一个编号;7)avg_ttl:收集了所有键剩余存活时间的一个平均值;8)expires_cursor;9)defrag_later;其中RedisDB数据结构整体流向:

4.redis中的key和value的类型分别是什么?
答:key都是String类型,value有String、Set、List、Hash、Zset;
5.redis中的常用的命令有哪些?
答:redisCommand数据结构存储着redis命令,其中负数代表至少有多少个参数,整数代表只能有两个; 特殊:flushdb:清空数据库; String类型的命令有: 1)type key:查看key的类型都是String; 2)object encoding key:查看key在redis底层的实际存储类型;3)Incr key:对key进行递增或递减;4)strlen key:获取val的长度;5)help @string:查看String操作命令; 6)mset:批量插入数据;7)mget:批量查询数据;8)append key:字符串追加值;9)strlen key:查看长度;list类型的命令有: 1)help @list:查看操作命令;2)lpush key:从左插入(入队列);3)lrange key 0 -1:从左获取所有数据;4)lpop key:从左查数据(出队列);5)rpush key:从右插入(入队列);6)rpop key:从右查数据(出队列);7)ltrim key 0 3:删除不是0到3的数据;8)blpop key 秒:阻塞队列; hash类型的命令有: 1)help @hash:查看操作命令;2)hset:插入数据; set类型命令有: 1)help @set:查看操作命令;2)sadd key:插入数据;3)smembers key:查询数据; **zset类型命令有:**1)help @sorted_set:查看操作命令;
6.redis的key在底层是怎么存储的?
答:主要用redisObject对象,该对象中有type(数据类型:string、list、hash、set)、encoding(数据编码,内存的利用率极高)、iru:LRU_BITS(内存淘汰策略)、refcount(内存计数器)、 *ptr(len、free、buf[])指向数据编码对象也就是真实的数据;int类型存储逻辑:首先判断数据的长度是否在20以内,其次尝试转成int,如果是int的话ptr将不在创建内存地址,直接存储在redisObject对象上;embstr类型存储逻辑: 是SDS(Simple Dynamic String 简单动态字符串),其中cpu获取数据的缓存行是64byte, 如果业务数据少于44个字节,只需分配一次内存空间即可,而不在通过指针引用; raw类类型存储逻辑:存储大于 44 个字节的字符串,需要分配两次内存空间;
7.redis中mset(批量)和set(单次)操作的区别是什么?
答:第一是命令有却别,单次用set插入,用get获取数据,批量用mset插入,mget批量获取;第二是IO,每次操作会产生rasp协议,转成tcp,最后转成IP,如果是单次的话会重复以上操作,造成IO浪费,而批量只会损失一次即可;
8.系统有海量的活跃数据,怎么统计日活?
答:用bitmap(位图),比如:setbit插入数据;用getbit获取数据;用bitcount统计数据;bitmap(位图)的存储原理是: 在setbit插入数据时,在key后面有个offset偏移量,通过偏移量设置bit的值,bit的底层其实是String类型,最多能存512M,通过bitcount key 0 -1其中key是需要统计的用户,0和-1是偏移量,这样就实现了天的统计;夸天统计是将key按位于(and),而周和月的统计是将多天的key按位或(or)合并存到一个key中在通过bitcount统计即可;
9.list类型在redis中的实现?
答:list是有序的数据结构,按照插入顺序排序,通过quicklist(双向链表)和ziplist实现的;ziplist是紧凑的结构有:zibytes(当前list的占用空间)、zitail(定位结尾数据的偏移量)、zllen(当前list中存储的个数)、zlend(当前list的结尾)、entry(prerawlen表示前面第一个元素,为0、len表示前面数据长度、data)主要存储数据;

其中quicklist(双向链表)是ziplist和linkedList的优化,通过head指向头部,通过tail指向尾部,每个quicklist(双向链表)中用quicklistNode包含一个ziplist,多个ziplist用双向指针串联起来;

通过内存优化提供存取效率,在redis.conf文件中:1)list-max-ziplist-size -2:设置ziplist最大容量; 2)list-compress-depth 0:压缩范围;
10.Redis的线程模型,是怎么工作的?
答:redis在6.0版本以前针对客户端是单线程的,当有unlink和慢IO时会创建子进程处理,在6.0版本以后提供了多线程默认是关闭的,而且只是对read和write是多线程,其中command(处理客户端命令)是单线程的,另外客户端是通过TCP连接服务的,单个TCP连接内是有序的,多个TCP连接时不能保证有序;
11.hash类型在redis中的实现?
答:hash的数据结构其实是dict(字典)是无序的,用key和value存储,当数据小时用ziplist存储,数据的大小和元素阈值可通过参数设置;
12.redis怎么解决哈希冲突?
答:首先hash会根据数组的长度求模,这时key会拿到数组的索引位置,hash有两个特点:1)相同的输入一定能得到相同的输出;2)不同的输入有可能得到相同的输出,也就是哈希冲突;redis通过链表法解决冲突,通过next指针指向原有的key,如果没有就是null;
13.set和hset的区别?
答:首先set和String中的命令,hset是hash中的命令,set可以通过expire设置过期时间而hset在单个filed不行,set每执行一次就会多一个key,造成内存消耗过高,而hset以hash散列表的形式存储比较节省内存;
14.set类型在redis中的实现?
答:set是无序的自动去重的集合类型,底层数据结构value是为null的dict(字典),当数据是整形时用intset存储(有序),否则用hashtable存储(无序);
15.zset类型在redis中的实现?
答:zset是有序的,优先根据score排序,score相同用元素字典排序,会自动去重,底层数据结构是用dict(字典)+skiplist(跳表)其中跳表的底层使用链表存的,数据少时用ziplist结构存储;
16.redis如何实现持久化?
答:有三种:1)RDB(redis DataBase):保存某一个时间点之前的快照数据,通知配置方式或者手动执行save命令,优点是a)服务启动数据恢复比较快;、b)持久化使用子进程处理主进程可以继续处理客户端请求 ;缺点是:a)持久化有时间间隔;b)写入进程多的话会有大量的分页错误,造成性能耗费;2)AOF(Append-Only File):所有的命令行记录以redis命令请求协议的格式完全持久化存储保存为aof文件,优点是:数据安全,配置appendfsync属性,每执行一次命令就记录到aof文件中;缺点是:数据大的时候,比rdb启动效率低;3)混合持久化(4.0版本以后):进行aof重写时子进程将当前时间点的数据快照保存为rdb文件格式,而后将父进程命令保存为aof格式,优点是:兼顾了rdb的恢复速度和aof的安全性;
17.Redis是如何处理过期数据的?
答:有两种:1)惰性删除:当访问时才判断是否过期,是就删除返回null,否就返回数据;2)定期删除:通过serverCron(定时任务)每一秒执行10次;
18.当内存不够用时redis是如何处理的?
答:当内存不足时影响最大的是写请求会返回错误,通过两个算法:1)LRU(The Least Recently Used)最近少使用:当数据在最近一段时间没有被访问,将被认定为访问的可能性很小,当内存满时将被淘汰,通过链表实现;2)LFU(Least Frequently Used)最不请经常使用:当数据在最近一段时间很少被访问,将被认定为访问的可能性很小,当内存满时将被淘汰也是用redisObject中的lru在存储lfu数据,将该lru分为两部分1)高16位记录访问时间(分钟);2)低八位记录频率,简称counter;
19.什么是缓存雪崩?
答:在流量洪峰达到时,大量的请求导致缓存服务宕机,所有请求访问db造成不可用称为雪崩;解决办法:1)采用集群架构实现;2)对服务接口降级;3)对缓存监控,达到阈值时通过自动故障转移和不重要的接口;4)将比较常用的key缓存在本地,减少redis访问;
20.热KEY重建有什么风险,如何优化?
答:设置的缓存过期了,有大量的请求访问同一个key,缓存失效的话可能会有大量的线程重建,造成数据库压力比较大;解决办法:1)通过集群方式分发缓解;2)服务端先将请求本地缓存;3)通过多级请求,层层过滤key缓解;
21.Redis的并发竞争问题如何解决?
答:1)使用incr命令: incr key 将 key 中储存的数字值增一;2)使用乐观锁的方式进行解决(成本较低,非阻塞,性能较高):使用watch命令:watch这里表示监控该key值,后面的事务是有条件的执行,如果从watch的exec语句执行时,watch的key对应的value值被修改了,则事务不会执行;3)java代码级别:这个是针对客户端来的,在代码里要对redis操作的时候,针对同一key的资源,就先进行加锁(synchronized或lock);4)利用redis的setnx实现内置的锁;
22.了解Redis事务的CAS操作吗?
答:cas(check-and-set 乐观锁):Redis的事务:redis事务用的命令:MULTI、SET、HSET、EXEC。Redis会将所有EXEC命令之前的命令放入一个QUEUE中,当遇到EXEC时批量执行QUEUE中的命令,但是 Redis的事务是不支持回滚的,它只是顺序的执行命令,并批量返回结果,但是对于极端情况下,事务在没有完全执行完时宕机,导致事务日志只写入部分,这样在重启时会产生错误,用aof的修复工具修复后可以进行启动;Watch命令可以监控Redis中的一个key,当Key发生变化时终止事务的提交;如果watch一个不稳定(有生命周期)的key并且此key自然过期,exec仍然会执行事务队列的指令;
23.redis支持的数据类型到跳跃表?
答:zskiplist就是一个跳表,其中header 和 tail 指针分别指向表头和表尾节点,length 记录了节点数量,level 记录了所有节点中层级最高的节点的层级,表头节点的层高不计算在内,zskiplistNode是跳表的一个节点,每个节点的层级都是根据幂次定律(power law,越大的树出现的概率越小)随机生成的,它是1~32之间的一个数,作为level数组的大小;
24.redis主从的同步策略?
答:分为全量同步和增量同步,全量同步分为三个阶段:1)同步快照阶段:msster创建并发送快照给slave,slave载入并解析快照,master此时将产生的新命令存储到缓冲区;2)同步写缓冲阶段:master向slave同步存储在缓冲区的写命令;3)同步增量阶段:master想slave同步写命令;增量同步:slave完成初始化后正常工作时,master发生的写操作同步到slave的过程,master每执行一个写命令就向slave发送发送相同的命令,slave接收并执行;
25.什么是缓存穿透?
答:每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源;解决办法:1)采用布隆算法过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉;2)设置缓存的过期时间(短);
26.什么是缓存击穿?
答:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期就会从DB获取数据并回设到缓存;解决办法:1)使用互斥锁(mutex key) ,就是在缓存失效的时候,不是立即去load db,而是先用Redis的SETNX去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法;2)用二级缓存也就是在一层redis;
27.redis中大key(bigkey)解决办法?
答:存在的问题 :1)读写bigkey会导致超时严重,甚至阻塞服务;2)大key相关的删除或者自动过期时,会出现qps突降或者突升的情况,极端情况下,会造成主从复制异常,Redis服务阻塞无法响应请求;3)占用资源;解决办法:1)将大key分拆成多个key,value用multiGet获取值,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;2)将分拆的key,value用hash存储,用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性;3)按照存储元素进行规则分类,分散存储到多个redis实例中;4)可以利用pipeline管道,一次发送多个命令,无需等待服务端返回;
28.redis中哨兵的作用?
答:哨兵(sentinel) 系统用于管理多个 Redis 服务器,其实也是运行在特殊模式下的 Redis 服务器,主要有:1)监控(Monitoring): 哨兵会不断地检查你的Master和Slave是否运作正常;2) 提醒(Notification):当 Redis出现问题时, 通过 API 向管理员或者其他应用程序发送通知;3)自动故障迁移:当Master故障时,其中的Slave升级为新的Master,并让其它Slave改为新的master;
29.Redis为什么这么快?redis采用多线程会有哪些问题?
答:redis快:1)数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);2)数据结构简单,对数据操作也简单;3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU;4)使用多路I/O复用模型,非阻塞IO;5)使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制;redis线程:Redis在多线程高并发下出现数据错乱,也就是A的数据给了B,B的数据给到了C。
30.Redis跳跃表?
答:跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构;
31.单线程的Redis如何能够高并发?
答:Redis通过主从架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发;
32.Redis实现分布式锁?
答:分布式锁的实现原理:1)互斥性:保证同一时间只有一个客户端可以拿到锁,也就是可以对共享资源进行操作;2)安全性:只有加锁的服务才能有解锁权限; 3)避免死锁:出现死锁就会导致后续的任何服务都拿不到锁,不能再对共享资源进行任何操作了;4)保证加锁与解锁操作是原子性操作, 加锁分为a.设置key set(key,value)b.给key设置过期时间;使用分布式锁:1)使用redis命令 set key value NX EX max-lock-time 实现加锁;2)使用redis命令 EVAL 实现解锁。
33.redis集群 一致性hash的原理?
答:reids有一套被称作环的算法,所有的数据和集群都在这个环上,redis通过机器IP+编号对key求模取2的32次方存储,当添加和删除机器事能减少迁移数据的工作量;缺点是:容易发生数据倾斜,解决办法是添加虚拟节点;
34.Redis实现语言以及Redis是什么?
答:C语言;redis是非关系型的键值对数据库,可以根据键以o(1)的时间复杂度查询或插入关联值,是以内存方式存储的,并内置了复制,磁盘持久化、事务、SSL客户端代理等功能,通过哨兵和自动化分区提高可用性;
- String类型的底层数据结构及实现原理?
答:redis自定义了一个数据类型sds(simple dynamic string)简单的动态字符串,通过动态扩容机制减少内存浪费,也就是用存储的业务数据长度判断用什么方法存储;Stirng是由多个sdshdr5、sdshdr8、sdshdr16、sdshdr32等方法组成,每个方法中有len(长度)、char[]、flags组成,其中flags是由type(数据范围)和len(数据长度 )组成,flags是在真实数据往前偏移1个byte的位置;
36.redis的key在底层是怎么存储的?
答:主要用redisObject对象,该对象中有type(数据类型:string、list、hash、set)、encoding(数据编码,内存的利用率极高)、iru:LRU_BITS(内存淘汰策略)、refcount(内存计数器)、 *ptr(len、free、buf[])指向数据编码对象也就是真实的数据;int类型存储逻辑:首先判断数据的长度是否在20以内,其次尝试转成int, 如果是int的话ptr将不在创建内存地址,直接存储在redisObject对象上; embstr类型存储逻辑: 是SDS(Simple Dynamic String 简单动态字符串),其中cpu获取数据的缓存行是64byte, 如果业务数据少于44个字节,只需分配一次内存空间即可,而不在通过指针引用; raw类类型存储逻辑:存储大于 44 个字节的字符串,需要分配两次内存空间;
10.list类型在redis中的实现?
答:list是有序的数据结构,按照插入顺序排序,通过quicklist(双向链表)和ziplist实现的;
ziplist是紧凑的结构有:zibytes(当前list的占用空间)、zitail(定位结尾数据的偏移量)、
zllen(当前list中存储的个数)、zlend(当前list的结尾)、 entry(prerawlen表示前面第一个元素,为0、len表示前面数据长度、data)主要存储数据; 其中quicklist(双向链表)是ziplist和linkedList的优化,通过head指向头部,通过tail指向尾部, 每个quicklist(双向链表)中用quicklistNode包含一个ziplist,多个ziplist用双向指针串联起来;通过内存优化提供存取效率,在redis.conf文件中: 1)list-max-ziplist-size -2:设置ziplist最大容量; 2)list-compress-depth 0:压缩范围;
12.hash类型在redis中的实现?
答:hash的数据结构其实是dict(字典)是无序的,用key和value存储,当数据小时用ziplist存储,数据的大小和元素阈值可通过参数设置;
14.set和hset的区别?
答:首先set和String中的命令,hset是hash中的命令,set可以通过expire设置过期时间而hset在单个filed不行,set每执行一次就会多一个key,造成内存消耗过高,而hset以hash散列表的形式存储比较节省内存;
15.set类型在redis中的实现?
答:set是无序的自动去重的集合类型,底层数据结构value是为null的dict(字典),当数据是整形时用intset存储(有序),否则用hashtable存储(无序);
16.zset类型在redis中的实现?
答:zset是有序的,优先根据score排序,score相同用元素字典排序,会自动去重,底层数据结构是用dict(字典)+skiplist(跳表)其中跳表的底层使用链表存的,数据少时用ziplist结构存储;
4.redis默认的DB(数据库)有多少个?
答:一共16个, 都是redisDb存储的, redisDb的数据结构共有9个 : 1)dict(字典):主要用于Key的索引; 2)expires:存储过期时间; 3)blocking_keys(阻塞):存储被阻塞的key; 4)ready_keys:存储的是可以解除阻塞的键, key通过该方式与客户端建立关系;5)watched_keys:存储的是正在被 watch 命令监控的键; 6)id:记录的是当前数据库的一个编号; 7)avg_ttl:收集了所有键剩余存活时间的一个平均值;8)expires_cursor;9)defrag_later
5.redis中的key和value的类型分别是什么?
答:key都是String类型,value有String、Set、List、Hash、Zset;
6.redis中的常用的命令有哪些?
答:redisCommand数据结构存储着redis命令,其中负数代表至少有多少个参数,整数代表只能有两个;特殊 :flushdb:清空数据库;String类型的命令有 :1)type key:查看key的类型都是String;2)object encoding key:查看key在redis底层的实际存储类型;3)Incr key:对key进行递增或递减;4)strlen key:获取val的长度;5)help @string:查看String操作命令; 6)mset:批量插入数据;7)mget:批量查询数据;8)append key:字符串追加值;9)strlen key:查看长度;list类型的命令有 :1)help @list:查看操作命令;2)lpush key:从左插入(入队列);3)lrange key 0 -1:从左获取所有数据;4)lpop key:从左查数据(出队列);5)rpush key:从右插入(入队列);6)rpop key:从右查数据(出队列);7)ltrim key 0 3:删除不是0到3的数据; 8)blpop key 秒:阻塞队列; hash类型的命令有 : 1)help @hash:查看操作命令;2)hset:插入数据;set类型命令有 : 1)help @set:查看操作命令;2)sadd key:插入数据;3)smembers key:查询数据;zset类型命令有 :1)help @sorted_set:查看操作命令;
7.Redis中的Lua有没有使用过?可以用来做什么?
答: 1、减少网络开销:可以将多个请求通过脚本的形式一次发送,减少网络时延和请求次数。2、原子性的操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。 3、代码复用:客户端发送的脚步会永久存在redis中,这样,其他客户端可以复用这一脚本来完成相同的逻辑。4、速度快:见 与其它语言的性能比较, 还有一个 JIT编译器可以显著地提高多数任务的性能; 对于那些仍然对性能不满意的人, 可以把关键部分使用C实现, 然后与其集成, 这样还可以享受其它方面的好处。5、可以移植:只要是有ANSI C 编译器的平台都可以编译,你可以看到它可以在几乎所有的平台上运行:从 Windows 到Linux, 同样Mac平台也没问题, 再到移动平台、游戏主机,甚至浏览器也可以完美使用 (翻译成JavaScript);6、源码小巧:20000行C代码,可以编译进182K的可执行文件,加载快,运行快。
8.redis中mset(批量)和set(单次)操作的区别是什么?
答:第一是命令有却别,单次用set插入,用get获取数据,批量用mset插入,mget批量获取;第二是IO,每次操作会产生rasp协议,转成tcp,最后转成IP,如果是单次的话会重复以上 操作,造成IO浪费,而批量只会损失一次即可;
9.系统有海量的活跃数据,怎么统计日活?
答:用bitmap(位图),比如:setbit插入数据;用getbit获取数据;用bitcount统计数据;bitmap(位图)的存储原理是: 在setbit插入数据时,在key后面有个offset偏移量,通过偏移量设置bit的值,bit的底层其实是String类型,最多能存512M,通过bitcount key 0 -1其中key是需要统计的用户,0和-1是偏移量,这样就实现了天的统计;夸天统计是将key按位于(and),而周和月的统计是将多天的key按位或(or)合并存到一个key中在通过bitcount统计即可;
11.Redis的线程模型,是怎么工作的?
答:redis在6.0版本以前针对客户端是单线程的,当有unlink和慢IO时会创建子进程处理,在6.0版本以后提供了多线程默认是关闭的,而且只是对read和write是多线程,其command(处理客户端命令)是单线程的,另外客户端是通过TCP连接服务的,单个TCP连接内是有序的,多个TCP连接时不能保证有序;
13.redis怎么解决哈希冲突?
答:首先hash会根据数组的长度求模,这时key会拿到数组的索引位置,hash有两个特点:1)相同的输入一定能得到相同的输出;2)不同的输入有可能得到相同的输出,也就是哈希冲突;redis通过链表法解决冲突,通过next指针指向原有的key,如果没有就是null;
17.redis如何实现持久化?
答:有三种:1)RDB(redis DataBase):保存某一个时间点之前的快照数据,通知配置方式或者手动执行save命令,优点是1)服务启动数据恢复比较快;2)持久化使用子进程处理主进程可以继续处理客户端请求 ;缺点是:1)持久化有时间间隔;2)写入进程多的话会有大量的分页错误,造成性能耗费;2)AOF(Append-Only File):所有的命令行记录以redis命令请求协议的格式完全持久化存储保存为aof文件,优点是:数据安全,配置appendfsync属性,每执行一次 命令就记录到aof文件中;缺点是:数据大的时候,比rdb启动效率低;3)混合持久化(4.0版本以后):进行aof重写时子进程将当前时间点的数据快照保存为rdb文件格式,而后将父进程命令保存为aof格式,优点是:兼顾了rdb的恢复速度和aof的安全性;
18.redis宕机之后如何恢复数据?
答:有三种:1)RDB(redis DataBase):保存某一个时间点之前的快照数据,通知配置方式或者手动执行save命令,优点是1)服务启动数据恢复比较快;2)持久化使用子进程处理主进程可以继续处理客户端请求 ;缺点是:1)持久化有时间间隔;2)写入进程多的话会有大量的分页错误,造成性能耗费;2)AOF(Append-Only File):所有的命令行记录以redis命令请求协议的格式完全持久化存储保存为aof文件,优点是:数据安全,配置appendfsync属性,每执行一次命令就记录到aof文件中;缺点是:数据大的时候,比rdb启动效率低; 3)混合持久化(4.0版本以后):进行aof重写时子进程将当前时间点的数据快照保存为rdb文件格式,而后将父进程命令保存为aof格式,优点是:兼顾了rdb的恢复速度和aof的安全性;
18.Redis是如何处理过期数据的?
答:有两种:1)惰性删除:当访问时才判断是否过期,是就删除返回null,否就返回数据;2)定期删除:通过serverCron(定时任务)每一秒执行10次;
19.当内存不够用时redis是如何处理的?
答:当内存不足时影响最大的是写请求会返回错误,通过两个算法:1)LRU(The Least Recently Used)最近少使用:当数据在最近一段时间没有被访问,将被认定为访问的可能性很小,当内存满时将被淘汰,通过链表实现;2)LFU(Least Frequently Used)最不请经常使用:当数据在最近一段时间很少被访问,将被认定为访问的可能性很小,当内存满时将被淘汰也是用redisObject中的lru在存储lfu数据,将该lru分为两部分1)高16位记录访问时间(分钟);2)低八位记录频率,简称counter;
20.Redis的LRU过期策略的具体实现?
答:Redis中的键与值都是redisObject对象,unsigned的低24 bits的lru记录了redisObj的LRU time, LRU_CLOCK_RESOLUTION代表了LRU算法的精度,即一个LRU的单位是多长。server.hz代表服务器刷新的频率, 如果服务器的时间更新精度值比LRU的精度值要小,LRU_CLOCK()直接使用服务器的时间,减小开销。
20.什么是缓存雪崩?
答:在流量洪峰达到时,大量的请求导致缓存服务宕机,所有请求访问db造成不可用称为雪崩;解决办法:1)采用集群架构实现;2)对服务接口降级;3)对缓存监控,达到阈值时通过自动故障转移和不重要的接口;4)将比较常用的key缓存在本地,减少redis访问;
21.热KEY重建有什么风险,如何优化?
答:设置的缓存过期了,有大量的请求访问同一个key,缓存失效的话可能会有大量的线程重建,造成数据库压力比较大;解决办法:1)通过集群方式分发缓解;2)服务端先将请求本地缓存;3)通过多级请求,层层过滤key缓解;
22.Redis的并发竞争问题如何解决?
答:1)使用incr命令: incr key 将 key 中储存的数字值增一;2)使用乐观锁的方式进行解决(成本较低,非阻塞,性能较高):使用watch命令:watch这里表示监控该key值,后面的事务是有条件的执行,如果从watch的exec语句执行时,watch的key对应的value值被修改了,则事务不会执行;3)java代码级别:这个是针对客户端来的,在代码里要对redis操作的时候,针对同一key的资源,就先进行加锁(synchronized或lock); 4)利用redis的setnx实现内置的锁;
23.了解Redis事务的CAS操作吗?
答:cas(check-and-set 乐观锁):Redis的事务:redis事务用的命令:MULTI、SET、HSET、EXEC。Redis会将所有EXEC命令之前的命令放入一个QUEUE中,当遇到EXEC时批量执行QUEUE中的命令,但是 Redis的事务是不支持回滚的,它只是顺序的执行命令,并批量返回结果,但是对于极端情况下,事务在没有完全执行完时宕机,导致事务日志只写入部分,这样在重启时会产生错误,用aof的修复工具修复后可以进行启动;Watch命令可以监控Redis中的一个key,当Key发生变化时终止事务的提交;如果watch一个不稳定(有生命周期)的key并且此key自然过期,exec仍然会执行事务队列的指令;
24.redis支持的数据类型到跳跃表?
答:zskiplist就是一个跳表,其中header 和 tail 指针分别指向表头和表尾节点,length 记录了节点数量, level 记录了所有节点中层级最高的节点的层级,表头节点的层高不计算在内,zskiplistNode是跳表 的一个节点,每个节点的层级都是根据幂次定律(power law,越大的树出现的概率越小)随机生成的,它是1~过期策略
25.redis主从的同步策略?
答:分为全量同步和增量同步,全量同步分为三个阶段:1)同步快照阶段:msster创建并发送快照给slave ,slave载入并解析快照,master此时将产生的新命令存储到缓冲区;2)同步写缓冲阶段:master向slave同步存储在缓冲区的写命令;3)同步增量阶段:master想slave同步写命令;增量同步:slave完成初始化后正常工作时,master发生的写操作同步到slave的过程,master每执行一个写命令就向slave发送发送相同的命令,slave接收并执行;
26.什么是缓存穿透?
答:每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源;解决办法:1)采用布隆算法过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉;2)设置缓存的过期时间(短);
27.什么是缓存击穿?
答:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期就会从DB获取数据并回设到缓存;解决办法:1)使用互斥锁(mutex key) ,就是在缓存失效的时候,不是立即去load db,而是先用Redis的SETNX去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法;2)用二级缓存也就是在一层redis;
28.redis中大key(bigkey)解决办法?
答:存在的问题:1)读写bigkey会导致超时严重,甚至阻塞服务;2)大key相关的删除或者自动过期时,会出现qps突降或者突升的情况,极端情况下,会造成主从复制异常,Redis服务阻塞无法响应请求;3)占用资源;解决办法:1)将大key分拆成多个key,value用multiGet获取值,将操作压力平摊到多个redis实例中,降低对单个redis的IO影响;2)将分拆的key,value用hash存储,用hget,hmget来获取部分的value,使用hset,hmset来更新部分属性;3)按照存储元素进行规则分类,分散存储到多个redis实例中;4)可以利用pipeline管道,一次发送多个命令,无需等待服务端返回;
29.redis中哨兵的作用?
答:哨兵(sentinel) 系统用于管理多个 Redis 服务器,其实也是运行在特殊模式下的 Redis 服务器,主要有:1)监控(Monitoring): 哨兵会不断地检查你的Master和Slave是否运作正常;2) 提醒(Notification):当 Redis出现问题时, 通过 API 向管理员或者其他应用程序发送通知;3)自动故障迁移:当Master故障时,其中的Slave升级为新的Master,并让其它Slave改为新的master;
147.Redis为什么这么快?redis采用多线程会有哪些问题?
答:redis快:1)数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);2)数据结构简单,对数据操作也简单;3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU;4)使用多路I/O复用模型,非阻塞IO;5)使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制;redis线程:Redis在多线程高并发下出现数据错乱,也就是A的数据给了B,B的数据给到了C。
149.Redis跳跃表?
答:跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构;
150.单线程的Redis如何能够高并发?
答:Redis通过主从架构,实现读写分离,主节点负责写,并将数据同步给其他从节点,从节点负责读,从而实现高并发;
151.Redis实现分布式锁?
答:分布式锁的实现原理:1)互斥性:保证同一时间只有一个客户端可以拿到锁,也就是可以对共享资源进行操作;2)安全性:只有加锁的服务才能有解锁权限; 3)避免死锁:出现死锁就会导致后续的任何服务都拿不到锁,不能再对共享资源进行任何操作了;4)保证加锁与解锁操作是原子性操作,加锁分为a.设置key set(key,value)b.给key设置过期时间;使用分布式锁:1)使用redis命令 set key value NX EX max-lock-time 实现加锁;2)使用redis命令 EVAL 实现解锁。
152.redis集群 一致性hash的原理?
答:reids有一套被称作环的算法,所有的数据和集群都在这个环上,redis通过机器IP+编号对key求模取2的32次方存储,当添加和删除机器事能减少迁移数据的工作量;缺点是:容易发生数据倾斜,解决办法是添加虚拟节点;
1.Redis的并发竞争问题如何解决?
答:1)使用incr命令: incr key 将 key 中储存的数字值增一。2)使用乐观锁的方式进行解决(成本较低,非阻塞,性能较高):使用watch监控key值,如果从watch的exec语句执行时,watch的key对应的value值被修改了,则事务不会执行。3)java代码级别:在代码里要添加对同一key的操作先加锁(synchronized或lock)。 4)利用redis的setnx实现内置的锁。
178.Redis实现消息队列?
答:Redis的消息队列有两种模式,一种是发布者和订阅着模式,另外一种是生产者和消费者模式, 发布者和订阅者模式:发布者发送消息到队列,每个订阅者都能收到一样的消息。 生产者和消费者模式:生产者将消息放入队列,多个消费者共同监听,谁先抢到资源,谁就从队列中取走消息。注意,每个消息只能被一个消费者接收。
223.Redis持久化的几种方式,优缺点是什么,怎么实现的?
答:1)RDB:是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化;优点:a)RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的数据,这种多个数据文件的方式,非常适合做冷备。b)可以让 redis 保持高性能,因为 redis主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行RDB 持久化即可。c)对于灾难恢复,可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。d)如果数据集很大,RDB的启动效率会更高。缺点:a)RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 redis 进程宕机,那么会丢失最近 5 分钟的数据。b)RDB是通过fork子进程来协助完成数据持久化工作的,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。实现方式:a)通过配置自动进行的持久化: save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。b)通过SAVE与BGSAVE命令进行持久化。c)通过执行FLUSHALL命令,会清空内存中的数据。d)执行复制的时候; 2)AOF:将Reids的操作日志以追加的方式写入文件;优点:a)每隔 1 秒,通过一个后台线程执行一次fsync操作,最多丢失 1 秒钟的数据。b)日志文件以 append-only 模式写入,没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损。c)如果日志过大,Redis可以自动启用rewrite机制。
d)日志文件的命令通过非常可读的方式进行记录,格式清晰、易于理解。 缺点:a)相同数量的数据集而言,AOF文件通常要大于RDB文件。c)根据同步策略的不同,AOF在运行效率上往往会慢于RDB。
224.Redis的缓存失效策略?
答:1)FIFO:First In First Out,先进先出。判断被存储的时间,离目前最远的数据优先被淘汰。 2)LRU:Least Recently Used,最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。 3)LFU:Least Frequently Used,最不经常使用。在一段时间内,数据被使用次数最少的,优先被淘汰。
229.redis的数据淘汰(过期)策略?
答:1)定时删除: 在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。 2)惰性删除:放任过期键不管,每次从键空间读写操作时,都检查键是否过期,如果过期,删除该键,如果没有过期,返回该键。 3)定期删除:每隔一段时间执行一次定时删除,并通过限制删除操作执行的总时长和总频率来限制删除操作对CPU占用时间的影响。 通过定期删除过期键,有效减少了因为过期键而带来的内存浪费。4)主动清理:当前已用内存超过maxmemory限定时,触发主动清理策略。清理时会根据用户配置的maxmemory-policy来做适当的清理。
280.redis的hash算法用的是啥?
答:redis应该是使用一致性hash算法---MurmurHash3 算法,具有低碰撞率优点,google改进的版本cityhash也是redis中用到的哈希算法。现有的主流的大数据系统都是用的 MurmurHash本身或者改进。
282.什么是索引为啥nosql没索引?nosql有索引滴?
答:索引分为聚簇索引和非聚簇索引两种,聚簇索引是按照数据存放的物理位置为顺序的,而非聚簇索引就不一样了;聚簇索引能提高多行检索的速度,而非聚簇索引对于单行的检索很快。聚簇索引:有主键时,根据主键创建聚簇索引;没有主键时,会用一个唯一且不为空的索引列做为主键,成为此表的聚簇索引;如果以上两个都不满足那innodb自己创建一个虚拟的聚集索引。非聚簇索引:非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引。
129.Redis的并发竞争问题如何解决?
答:Redis的并发竞争问题,主要是发生在并发写竞争;解决办法:1)利用redis自带的incr(1.为key储存的数字值加上一,2.如果key储存的值不能被解释为数字,那么INCR 命令将返回一个错误。)命令;2)使用乐观锁的方式进行解决(成本较低,非阻塞,性能较高),使用watch命令:WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值);3)这个是针对客户端来的,在代码里要对redis操作的时候,针对同一key的资源,就先进行加锁(java里的synchronized或lock);4)利用redis的setnx实现内置的锁,当且仅当key不存在,将key的值设置为value,并且返回1;若是给定的key已经存在,则setnx不做任何动作,返回0。
130.Redis持久化的几种方式,优缺点是什么,怎么实现的?
答:共有两种方式:1)RDB持久化:是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储;2)AOF持久化:以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录;RDB优点:a)非常适用于灾难恢复,它只有一个文件,并且内容都非常紧凑,因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上;b)性能最大化,对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了;c)相比于AOF机制,如果数据集很大,RDB的启动效率会更高;RDB缺点:a)在保证数据的高可用性,RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失;b)由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟;AOF优点:a)让 Redis 变得非常耐久(much more durable):你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync ,AOF 的默认策略为每秒钟fsync 一次;b)由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题;c)如果日志过大,Redis可以自动启用rewrite机制,即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行;d)AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作;AOF缺点:a)对于同一份数据来说,AOF 日志文件通常比RDB 数据快照文件更大;b)根据同步策略的不同,AOF在运行效率上往往会慢于RDB。
133.Redis集群,高可用,原理?
答:1)主观下线: 集群中每个节点都会定期向其他节点发送ping消息,接收节点回复pong消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节点会认为接收节点存在故障,把接受节点标记为主观下线(pfail)状态; 2)客观下线:a)当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播;b)假设节点a标记节点b为主观下线,一段时间后节点a通过消息把节点b的状态发送到其他节点,当其他节点收到消息并解析出消息体中含有b的pfail状态,把节点b加入下线报告链表;c)当某一节点c收到节点b的pfail状态时,此时有超过一半的槽主节点都标记了节点b为pfail状态时,则标记故障节点b为客观下线;d)向集群广播一条pfail消息,通知集群内的所有节点标记故节点b为客观下线状态并立刻生效,同时通知故障节点b的从节点触发故障转移流程;3)故障恢复:a)资格检查:若从节点与主节点断线时间超过一定时间,则不具备资格;b)准备选举时间:当从节点符合故障转移资格后,要等待一段选举时间后才开始选举在故障节点的所有从节点中,复制偏移量最大的那个从节点最先开始(与主节点的数据最一致)进行选举,然后是次大的节点开始选举.....剩下其余的从节点等待到它们的选举时间到达后再进行选举;c)发起选举;d)选举投票:只有持有槽的主节点才具有一张唯一的选票,从从节点收集到N/2 + 1个持有槽的主节点投票时,从节点可以执行替换主节点操作;e)替换主节点。
134.Redis缓存分片?
答:Cluster分片最少要有6个才能组成高可用的集群,其中三个主节点,三个从节点,三个主节点会分配槽处理客户端的请求,通过范围分片,哈希分片,一致性哈希算法,哈希槽等方法对数据分片;
108.redis能存哪些类型?
答:1)String字符串;2)Hash哈希;3)List列表;4)Set集合;5)zset(Sorted set:有序集合)
109.redis数据类型存储的场景?
答:1)string :一个 key 对应一个 value。value其实不仅是String,也可以是数字。string 类型是二进制安全的,使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。2)hash: 是一个键值(key => value)对集合,hash 是一个 string 类型的 field 和 value 的映射表,使用场景:存储部分变更数据,如用户信息等。Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口;3)list: 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),使用场景:比如微博ID等;4)set是string类型的无序集合。集合是通过hashtable实现的,概念和数学中个的集合基本类似,可以交集,并集,差集等等;使用场景:存储全班同学成绩;
109.Redis有几种部署方式?哨兵机制和集群的区别是啥?
答:单机模式:优点:架构简单,部署方便,高性能,性价比高;缺点:不保证数据的可靠性,缓存失效后数据丢失;性能受限;哨兵(Sentinel)模式:优点:部署简单;能够解决主从的高可用切换问题;能通过哨兵监控各个节点; 缺点:部署主从比较繁琐;资源浪费备节点不提供服务;不能读写分离;集群模式;优点:无中心架构;可数据共享,动态分布数据;可扩展性高;高可用,运维成本较低;缺点:客户端实现复杂;数据通过异步,不能保证数据强一致性;批量操作KEY受限制;
110.redis的集群是怎么实现的?
答:在redis6.0版本以后, 用redis-cluster-proxy方式(集群代理),可以与组成Redis集群的一组实例进行通讯,就像是单个实例一样。Redis群集代理是多线程的,使用多路复用通信模型,因此每个线程都有自己与群集的连接,该连接由属于该线程本身的所有客户端共享。在某些特殊情况下(例如MULTI事务或阻塞命令),多路复用将被禁用;并且客户端将拥有自己的集群连接。这样客户端仅发送诸如GET和SET之类的简单命令就不需要Redis集群的专有连接。redis-cluster-proxy的特点有:路由:每个查询都会自动路由到集群的正确节点;多线程;支持多路复用和专用连接模型;在多路复用上下文中,可以确保查询执行和答复顺序;发生ASK(类似于 http 协议中 301 跳转与 302 跳转) | MOVED(客户端给节点发送指令,如果处理的槽位不按顺序执行)错误后自动更新集群的配置:当答复中发生此类错误时,代理通过获取集群的更新配置并重新映射所有插槽来自动更新集群。更新完成后所有查询将重新执行,因此,从客户端的角度来看,一切正常进行,集群配置已更新;跨槽/跨节点查询:支持许多命令,这些命令涉及属于不同插槽(甚至不同集群节点)的多个键。这些命令会将查询分为多个查询,这些查询将被路由到不同的插槽/节点。 这些命令的回复处理是特定于命令的。 某些命令(例如MGET)将合并所有答复,就好像它们是单个答复一样。其他命令(例如MSET或DEL)将汇总所有答复的结果。 由于这些查询实际上破坏了命令的原子性,因此它们的用法是可选的(默认情况下禁用)。 一些没有特定节点/插槽的命令(例如DBSIZE)将传递到所有节点,并且将对映射的回复进行映射缩减,以便得出所有回复中包含的所有值的总和。可用于执行某些特定于代理的操作的附加PROXY命令
153.Redis⾼性能的原因大概可以讲一些?
答:1)存储速度很快: Redis读写数据完全基于内存,所以数据存取速度很快。它的最大吞吐量能达到10万,平均每秒能进行11万的集合操作。2)数据类型存储丰富: Redis支持多种不同类型的数据结构, 包括集合、有序集合、哈希等。不同的数据类型使Redis能应用于不同的业务场景。3)单线程单进程模型: Redis内部主要是使用单线程的IO复用模型,并支持使用select、poll、epoll的等方式的事件处理器, 单线程模型可以充分利用CPU资源,将速度优势发挥到最大。4)分布式集群:Redis除了支持单机模式,主从模式以外还自带了Redis集群