Redis阅读笔记

Redis数据结构类型

SDS字符串结构:buf,len,free;

内存分配:采用空间预分配,如果需要扩展空间时对于小于1M的分配修改后的len同样大的free,如果free够则不用分配;大于的分配多1M,空间预分配减少连续执行字符串增长操作所需的内存重分配次数;释放空间:惰性释放,不立即释放空间用free保存释放空间

优点:跟C字符串相比,获取长度O1,不会造成缓冲区溢出,修改字符串长度N次最多需要执行N次内存重分配,可以保存除了文本以外的二进制(不会因为空字符而结束)

链表:双端,无环,带表头和表尾指针,带表长计数器,void*多态可以保存不同类型,用来实现列表建,发布与订阅,慢查询,监视器,

字典:KV键值对,用来实现数据库和hash键,每个字典带有两个hash表,一个主用另一个用来rehash时使用,拉链法解决冲突,对hash表进行扩展和收缩时需要rehash到新hash表中并且是渐进式完成的

扩容:渐进性rehash

满足任意一个时会自动rehash:1.目前没有执行BGSAVE或者BGREWRITEAOF命令,并且负载因子》=1;2.目前在执行命令但负载必须大于=5;3.小于0.1时会自动收缩

因为执行命令时会创建子进程为了延迟COW时机尽量避免子进程存在时期rehash,避免不必要的写入内存节约内存

1.为字典的另一个hash表分配空间,拓展和收缩都一样大小为第一个大于=当前hash表节点数的两倍的2的幂次数;2、将当前hash的所有kv对rehash到新表上,需要重新计算hash和索引;3、迁移结束后,释放当前hash并将新表设置为当前hash,创建一个新的空白hash表为下一次rehash准备

渐进式的过程:

会在字典中维持一个rehashidx,刚开始设为0表示rehash开始,每次对字典的curd会顺带将rehashidx索引上的kv对rehash到新表上,每次rehash完当前索引会自增rehahsidx,结束后索引值为-1;

渐进式的查找:会先在当前hash表查找没找到会继续在新表中查找;并且在rehash过程新增节点会保存在新表中,保证了当前hash表节点在rehash开始后节点数(kv对)数量只会减少不会增加并最终成为空表;

跳表:每个节点维护多个指向其他节点的指针,实现快速访问节点,平均查找logN最坏On,是redis有序集合建实现的一种

节点定义:层,层的高度是随机生成的,每层有一个向前指针和跨度,前进指针指向同一个层的下一个节点,跨度是用来计算当前数的排名的;每个节点由后退指针(执行前序节点)分值(排序的key)对象,层数组

查找过程:从当前数最高层开始找如果小于同一层的下一个数则从下一层开始直到大于下一个数才前进;这样如果最高层只有两个索引层数是logn时就可以快速完成

压缩列表:节约内存,每个节点会有前序节点的长度

对象:Redis基于数据结构创建了对象系统,分别是字符串对象,列表对象,hash对象,集合对象,有序集合对象,使用了引用计数的内存回收机制,对象带有访问时间记录用于计算空转时长(lru)

hash对象:编码有两种,1.ziplist编码压缩列表保证节点相邻,2hashtable编码用字典作为底层实现

有序集合对象:压缩列表或跳表编码,zset跳表编码底层用一个跳表和字典实现,这两种同时使用可以利用跳表有序来实现范围操作(比如返回给定索引范围的数),字典来快速查找

数据库:Redis是kv数据库,每个数据库redisDB结构成员是dict数组键空间保存了所有的键值对,和expire字典,保存键的过期时间;

过期键的删除策略:1.定时删除,通过定时器在键过期时间到时删除键,对CPU时间不友好;2.惰性删除,放任键过期不管,从键空间获取键检查是否过期才删除,对内存不友好,会产生内存泄漏;3、定期删除:两者的折中。生成RDB文件时会过滤掉过期键;载入RDB时如果是主服务器也会,从服务器则不会;AOF写入时当过期键删除后AOF会追加命令,AOF重写时会过滤过期键;在复制模式下,从服务器只会在收到主服务器删除过期键命令后才会删除

RDB持久化:RDB用于保存和还原服务器所有数据库的键值对;SAVE命令由当前进程执行保存操作,改命令会阻塞服务器;BGSAVE命令子进程进行;RDB文件是压缩的二进制文件;

文件结构:REDIS,文件版本,多个database,EOF,校验和

AOF持久化:由命令追加,文件写入,文件同步(同步是因为操作系统写入文件时先放在内存缓冲区中而不是直接写入文件需要调用同步函数),每次命令都追加到aof缓冲区中,每次事件循环结束前将aof缓冲区写入文件(三种行为,总是同步,每秒同步,不同步由交给操作系统同步)

AOF文件的载入与数据还原:1.创建不带网络连接的伪客户端(因为命令在客户端执行)2.从AOF分析并读取出写命令;3.执行写命令4.重复2和3

AOF重写:即BGREWRITEOFAOF命令为了解决AOF体积膨胀,重写可以创建新的AOF文件来替代现有AOF,去除冗余命令;实现:重写是通过遍历数据库的kv对来实现的跟现有aof文件无关,只包含还原数据库状态所必须的最少命令;AOF重写放到子进程中执行,使用子进程带有服务器进程的数据副本可以避免锁(子线程需要考虑锁)

AOF重写的一致性问题:重写完成后服务器可能又执行了新的命令,跟重写AOF不一致;解决:设置AOF重写缓冲区,每次写命令会同时发送给AOF缓冲区和AOF重写缓冲区,当子进程完成AOF重写后会向父进程发送信号,父进程回调信号处理函数,将AOF重写缓冲区中的所有内容写入新AOF文件中,对新AOF改名原子地覆盖现有AOF文件完成新旧AOF的替换

 事件:文件事件;时间事件:周期性事件,将所有时间事件放到无序链表中,遍历整个链表查找到期事件调用相应的事件处理器。servercron负责:更新服务器时间缓存,更新LRU时钟,管理客户端资源,释放超时连接,处理服务器接收的sigterm信号,检查并执行持久化操作

客户端:服务器用clients链表保存多个客户端状态,每个客户端节点保存sds输入缓冲区(记录命令请求)char输出缓冲区(超过硬性限制会关闭,一定时间一直超过软限制也会关闭)

服务器:初始化过程:1初始化服务器状态;2.载入服务器配置3.初始化服务器数据结构(数据库客户端链表等)4.还原数据库状态开启AOF优先AOF还原;5.执行事件循环

复制:PSYNC命令两种模式:1.完整重同步:用于初次复制,通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里的写命令来进行同步;2、部分重同步:用于处理断线后重复制,主服务器将连接断开期间执行的写命令发给从服务器

部分重复制功能的实现:1.主服务器和从服务器的复制偏移量,主从服务器维护一个复制偏移量,每次发送多少字节各自更新相应的offset;2、复制积压缓冲区,主服务器维护的一个固定长度先进先出队列,默认1MB,保存最近传播的写命令,为每个字节记录相应的偏移量,如果偏移后的数据任然存在队列中就执行部分重同步否则进行完整重同步;3.服务器运行ID,每个从服务器会保存主服务器的运行ID,如果断线重连上主服务器后(有可能不是之前的),从服务器将向当前连接的主服务器发送之前保存的运行ID,如果跟当前ID不同则会执行完整同步相同则执行部分同步。命令第一次SLAVEOF-PSYNC

复制的步骤:1.客户端向从服务器发送slaveof命令,设置主服务器地址和端口;2.建立套接字连接;3.发送ping:两个作用,1检查套接字读写状态是否正常2.检查主服务器能否正常处理命令请求;4.收到pong后,身份验证;5.发送端口信息;6、同步;7、命令传播:从服务器会心跳检测:1.检测主从服务器的网络连接状态;2、辅助实现min-slaves选项;3、检测命令丢失

sentinel哨兵:redis高可用的解决方案:由一个或多个sentinel实例组成的sentinel系统可以监视任意多个主服务器以及这些主服务器下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器

故障转移操作:当下线时长超过上限时,会对当前服务器进行故障转移:1.sentinel系统挑选从服务器升级为主服务器;2.sentinel系统会向之前服务器的所有从服务器发送新的复制指令,让他们成为新服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕;3.sentinel系统会继续监视之前下线的服务器,等他重新上线时设置为新的主服务器的从服务器

启动并初始化sentinel:1、初始化服务器;2、将普通redis服务器使用代码替换成sentinel专用码;3.初始化sentinel状态;4、根据配置文件,初始化sentinel监视主服务器列表;5.创建连向主服务器的网络连接

sentinel初始化状态:sentinelstate,主要包含了masters字典,每个字典记录了所有被sentinel监视的主服务器相关信息,键表示被监

视主服务器的名字,字典值表示被监视的主服务器对应的sentinel实例(可以是主服务器,从服务器,或者另外一个sentinel实例)

创建网络连接:sentinel成为主服务器的客户端,可以向主服务器发送命令,并从命令回复中获取相关信息(两个连向主服务器的异步网络连接:一个是命令连接,发送命令和回复;一个是订阅连接,订阅主服务器的频道)

获取主服务器信息:每10s向主服务器发送INFO命令,获取两方面信息:1.主服务器本身的信息;2.从服务器的信息,用于更新主服务器实例结构的slaves字典:从服务器名字ip+port和对应从服务器的实例结构。为每个新的从服务器创建两个连接:对从服务器也定时发送INFO命令

检测主观下线状态:sentinel会1s一次向所有与它创建了命令连接的实例发送ping命令,通过回复判断实例是否在线;当将一个主服务器判断为主观下线后,为了确认这个主服务器是否真的下线会向同样监视该主服务器的其他sentinel询问,接受到足够数量的已下线判断后会将主服务器判断为客观下线,并对主服务器进行故障转移;

选举领头sentinel:当主服务器被判断为客观下线时,会进行选举领头sentinel来进行故障转移;按先到先得,每个sentinel同时向其他sentinel发送

故障转移:选新主服务器依次按照优先级最高,复制偏移量最大(表示保存着最新数据),按照运行ID最小。

集群:redis提供的分布式数据库方案,集群通过分片进行数据共享,并提供复制和故障转移功能;需要启动节点开启集群模式,每个节点状态会有集群名单字典,用三次握手来将b节点放到a节点的集群中

槽指派:集群通过分片的方式保存数据库的kv对,集群的整个数据库分为1638个槽,数据库中的每个键都在其中一个槽中,集群的每个节点可以处理0到16384个槽;当每个槽都有节点在处理时,集群处于上线状态,否则下线状态;每个节点clusternode的slots数组和num表示长度,slots是16384/8个字节长度的char数组,包含16384bit,索引i(8bit)某个bit为1表示节点k(k在(i)*8和(i+1)*8中)负责槽k;每个节点的节点状态中记录了所有槽对应的节点指针

槽指派结束后,集群会进入上线状态,客户端发送命令,接受命令的节点计算键属于哪个槽,并检查这个槽是否指派给了自己(通过节点状态找出该槽对应的节点指针是否是自己不是会返回moved错误指引客户端转向正确节点)

节点数据库:每个节点的节点状态会用跳表记录槽和键之间的关系,跳表的节点分值是一个槽号,成员是一个键,可以方便对属于某个或某些槽的所有数据库键进行批量操作(返回n个属于属于槽slot的数据库键)

重新分片:可以将任意数量已经指派给某个节点的槽改为指派给另一个节点,并且相关槽所属的键值对也会从源节点被移动到目标节点;过程:从目标节点准备导入槽slot的键值对,源节点准备迁移槽的键值对,将这些键全部迁移至目标节点

ASK错误:重新分片期间查找key时不在源节点在目标节点返回的错误,会指引客户端转向导入槽的目标节点(moved错误会让以后命令请求转向moved错误所指向的节点,ask只会在接下来的的命令请求进行转向)

复制与故障转移:集群中的节点分为主节点和从节点,主节点用于处理槽,从节点用于复制主节点,并在主节点下线是代替主节点处理命令请求

节点数据结构:每个节点的slaves属性表示从节点指针数组和从节点数如果是从节点则会有正在复制的主节点

故障检测:集群中的每个节点都会定期向集群中的其他节点发送PING消息,以此来检测对方是否在线,如果接收到PING消息的节点没有在规定时间内,向发送PING消息的节点返回PONG消息,那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线PFAIL,如果半数以上负责处理槽的主节点都认为某主节点x报告为疑似下线,所有标记为pfail的主节点都会广播fail收到消息后将x标记为下线

故障转移:当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移:1.复制下线主节点的所有从节点里面,会有一个从节点被选中;2.被选中的从节点会执行slaveof no one命令,成为新的主节点;3.新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己;4,新的主节点向集群广播一条pong消息,这条pong消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,以及接管了原本已下线的槽;5.新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成

选举新节点:raft算法的领头选举方法:每个负责处理槽的主节点都有投票一次机会,当一个从节点收到半数以上时则选举,否则进入一个新的配置纪元再次选举

集群消息:ping,pong,fail,publish

redis和memcached什么区别?为什么高并发下有时单线程的redis比多线程的memcached效率要高?

区别:

1.mc可缓存图片和视频。rd支持除k/v更多的数据结构;

2.rd可以使用虚拟内存 ,rd可持久化和aof灾难恢复,rd通过主从支持数据备份;

3.rd可以做消息队列。

原因:mc多线程模型引入了缓存一致性和锁,加锁带来了性能损耗。

redis主从复制如何实现的?redis的集群模式如何实现?redis的key是如何寻址的?

主从复制实现:主节点将自己内存中的数据做一份快照,将快照发给从节点,从节点将数据恢复到内存中。之后再每次增加新数据的时候,主节点以类似于mysql的二进制日志方式将语句发送给从节点,从节点拿到主节点发送过来的语句进行重放。

分片方式:

-客户端分片

-基于代理的分片

● Twemproxy

● codis

-路由查询分片

● Redis-cluster(本身提供了自动将数据分散到Redis Cluster不同节点的能力,整个数据集合的某个数据子集存储在哪个节点对于用户来说是透明的)

redis-cluster分片原理:Cluster中有一个16384长度的槽(虚拟槽),编号分别为0-16383。每个Master节点都会负责一部分的槽,当有某个key被映射到某个Master负责的槽,那么这个Master负责为这个key提供服务,至于哪个Master节点负责哪个槽,可以由用户指定,也可以在初始化的时候自动生成,只有Master才拥有槽的所有权。Master节点维护着一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从0开始)是不是为1即可。这种结构很容易添加或者删除节点。比如如果我想新添加个节点D, 我需要从节点A、B、 C中得部分槽到D上

使用redis如何设计分布式锁?说一下实现思路?使用zk可以吗?如何实现?这两种有什么区别?

redis:

1.线程A setnx(上锁的对象,超时时的时间戳t1),如果返回true,获得锁。

2.线程B 用get获取t1,与当前时间戳比较,判断是是否超时,没超时false,若超时执行第3步;

3.计算新的超时时间t2,使用getset命令返回t3(该值可能其他线程已经修改过),如果t1==t3,获得锁,如果t1!=t3说明锁被其他线程获取了。

4.获取锁后,处理完业务逻辑,再去判断锁是否超时,如果没超时删除锁,如果已超时,不用处理(防止删除其他线程的锁)。

zk:

1.客户端对某个方法加锁时,在zk上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点node1;

2.客户端获取该路径下所有已经创建的子节点,如果发现自己创建的node1的序号是最小的,就认为这个客户端获得了锁。

3.如果发现node1不是最小的,则监听比自己创建节点序号小的最大的节点,进入等待。

4.获取锁后,处理完逻辑,删除自己创建的node1即可。

区别:zk性能差一些,开销大,实现简单。

知道redis的持久化吗?底层如何实现的?有什么优点缺点?

RDB(Redis DataBase:在不同的时间点将redis的数据生成的快照同步到磁盘等介质上):内存到硬盘的快照,定期更新。缺点:耗时,耗性能(fork+io操作),易丢失数据。

AOF(Append Only File:将redis所执行过的所有指令都记录下来,在下次redis重启时,只需要执行指令就可以了):写日志 。使用 AOF 持久化会让 Redis 变得非常耐久。缺点:体积大,恢复速度慢。bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会消耗比较长的时间,不够实时,在停机的时候会导致大量的数据丢失,需要aof来配合,在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复。Redis会定期做aof重写,压缩aof文件日志大小。Redis4.0之后有了混合持久化的功能,将bgsave的全量和aof的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性。bgsave的原理,fork和cow, fork是指redis通过创建子进程来进行bgsave操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。

 

redis过期策略都有哪些?LRU算法知道吗?写一下java代码实现?

过期策略:

定时过期(一key一定时器),惰性过期:只有使用key时才判断key是否已过期,过期则清除。定期过期:前两者折中。

LRU:new LinkedHashMap(capacity, DEFAULT_LOAD_FACTORY, true);

//第三个参数置为true,代表linkedlist按访问顺序排序,可作为LRU缓存;设为false代表按插入顺序排序,可作为FIFO缓存

LRU算法实现:1.通过双向链表来实现,新数据插入到链表头部;2.每当缓存命中(即缓存数据被访问),则将数据移到链表头部;3.当链表满的时候,将链表尾部的数据丢弃。

 

LinkedHashMap:HashMap和双向链表合二为一即是LinkedHashMap。HashMap是无序的,LinkedHashMap通过维护一个额外的双向链表保证了迭代顺序。该迭代顺序可以是插入顺序(默认),也可以是访问顺序。

缓存穿透、缓存击穿、缓存雪崩解决方案?

缓存穿透:指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉。

解决方案:1.查询返回的数据为空,仍把这个空结果进行缓存,但过期时间会比较短;2.布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对DB的查询。

缓存击穿:对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把DB压垮。

解决方案:1.使用互斥锁:当缓存失效时,不立即去load db,先使用如Redis的setnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法。2.永远不过期:物理不过期,但逻辑过期(后台异步线程去刷新)。

缓存雪崩:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。

解决方案:将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

在选择缓存时,什么时候选择redis,什么时候选择memcache

选择redis的情况:

1、复杂数据结构,value的数据是哈希,列表,集合,有序集合等这种情况下,会选择redis, 因为memcache无法满足这些数据结构,最典型的的使用场景是,用户订单列表,用户消息,帖子评论等。

2、需要进行数据的持久化功能,但是注意,不要把redis当成数据库使用,如果redis挂了,内存能够快速恢复热数据,不会将压力瞬间压在数据库上,没有cache预热的过程。对于只读和数据一致性要求不高的场景可以采用持久化存储

3、高可用,redis支持集群,可以实现主动复制,读写分离,而对于memcache如果想要实现高可用,需要进行二次开发。

4、存储的内容比较大,memcache存储的value最大为1M。

选择memcache的场景:

1、纯KV,数据量非常大的业务,使用memcache更合适,原因是,

 a)memcache的内存分配采用的是预分配内存池的管理方式,能够省去内存分配的时间,redis是临时申请空间,可能导致碎片化。

 b)虚拟内存使用,memcache将所有的数据存储在物理内存里,redis有自己的vm机制,理论上能够存储比物理内存更多的数据,当数据超量时,引发swap,把冷数据刷新到磁盘上,从这点上,数据量大时,memcache更快

c)网络模型,memcache使用非阻塞的IO复用模型,redis也是使用非阻塞的IO复用模型,但是redis还提供了一些非KV存储之外的排序,聚合功能,复杂的CPU计算,会阻塞整个IO调度,从这点上由于redis提供的功能较多,memcache更快些

d) 线程模型,memcache使用多线程,主线程监听,worker子线程接受请求,执行读写,这个过程可能存在锁冲突。redis使用的单线程,虽然无锁冲突,但是难以利用多核的特性提升吞吐量。

缓存与数据库不一致怎么办

假设采用的主存分离,读写分离的数据库,如果一个线程A先删除缓存数据,然后将数据写入到主库当中,这个时候,主库和从库同步没有完成,线程B从缓存当中读取数据失败,从从库当中读取到旧数据,然后更新至缓存,这个时候,缓存当中的就是旧的数据。发生上述不一致的原因在于,主从库数据不一致问题,加入了缓存之后,主从不一致的时间被拉长了

处理思路:在从库有数据更新之后,将缓存当中的数据也同时进行更新,即当从库发生了数据更新之后,向缓存发出删除,淘汰这段时间写入的旧数据。

主从数据库不一致如何解决

场景描述,对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致

1、忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时时一致性

2、强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能。

3、选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库,如果缓存当中没有这个主键,就到对应的从库中读取。

Redis常见的性能问题和解决方案

  1、master最好不要做持久化工作,如RDB内存快照和AOF日志文件

  2、如果数据比较重要,某个slave开启AOF备份,策略设置成每秒同步一次

  3、为了主从复制的速度和连接的稳定性,master和Slave最好在一个局域网内

  4、尽量避免在压力大得主库上增加从库

  5、主从复制不要采用网状结构,尽量是线性结构,Master<--Slave1<----Slave2 ....

Redis的数据淘汰策略有哪些

voltile-lru 从已经设置过期时间的数据集中挑选最近最少使用的数据淘汰

voltile-ttl 从已经设置过期时间的数据库集当中挑选将要过期的数据

voltile-random 从已经设置过期时间的数据集任意选择淘汰数据

allkeys-lru 从数据集中挑选最近最少使用的数据淘汰

allkeys-random 从数据集中任意选择淘汰的数据

no-eviction 禁止驱逐数据

Redis当中有哪些数据结构

字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。如果是高级用户,那么还会有,如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。

假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会有什么问题?

这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长。

使用Redis做过异步队列吗,是如何实现的

 使用list类型保存数据信息,rpush生产消息,lpop消费消息,当lpop没有消息时,可以sleep一段时间,然后再检查有没有信息,如果不想sleep的话,可以使用blpop, 在没有信息的时候,会一直阻塞,直到信息的到来。redis可以通过pub/sub主题订阅模式实现一个生产者,多个消费者,当然也存在一定的缺点,当消费者下线时,生产的消息会丢失。

Redis如何实现延时队列

使用sortedset,使用时间戳做score, 消息内容

什么是Redis?

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。

Redis 与其他 key - value 缓存产品有以下三个特点:

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势

性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis与其他key-value存储有什么不同?

Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

Redis的数据类型?

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zsetsorted set:有序集合)。我们实际项目中比较常用的是string,hash如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。如果你说还玩过Redis Module,像BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。

使用Redis有哪些好处?

1、速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O1)

2、支持丰富数据类型,支持string,list,set,Zset,hash等

3、支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

4、丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

4、Redis相比Memcached有哪些优势?

1、Memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类

2、Redis的速度比Memcached快很

3、Redis可以持久化其数据

5、Memcache与Redis的区别都有哪些?

1、存储方式 Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。 Redis有部份存在硬盘上,这样能保证数据的持久性。

2、数据支持类型 Memcache对数据类型支持相对简单。 Redis有复杂的数据类型。

3、使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。 Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

6、Redis是单进程单线程的?

Redis是单进程单线程的,redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销。

7、一个字符串类型的值能存储最大容量是多少?

512M

8、Redis的持久化机制是什么?各自的优缺点?

Redis提供两种持久化机制RDB和AOF机制:

1、RDBRedis DataBase)持久化方式: 是指用数据集快照的方式半持久化模式)记录redis数据库的所有键值对,在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。

优点:

1、只有一个文件dump.rdb,方便持久化。

2、容灾性好,一个文件可以保存到安全的磁盘。

3、性能最大化,fork子进程来完成写操作,让主进程继续处理命令,所以是IO最大化。使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能) 4.相对于数据集大时,比AOF的启动效率更高。

缺点:

1、数据安全性低。RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)

2、AOFAppend-only file)持久化方式: 是指所有的命令行记录以redis命令请求协议的格式完全持久化存储)保存为aof文件。

优点:

1、数据安全,aof持久化可以配置appendfsync属性,有always,每进行一次命令操作就记录到aof文件中一次。

2、通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。

3、AOF机制的rewrite模式。AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall))

缺点:

1、AOF文件比RDB文件大,且恢复速度慢。 2、数据集大的时候,比rdb启动效率低。

9、Redis常见性能问题和解决方案:

1、Master最好不要写内存快照,如果Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务 2、如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一 3、为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网 4、尽量避免在压力很大的主库上增加从 5、主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

10、redis过期键的删除策略?

1、定时删除:在设置键的过期时间的同时,创建一个定时器timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。

2、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。

3、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

11、Redis的回收策略(淘汰策略)?

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

使用策略规则:

1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru

2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

12、为什么edis需要把所有数据放到内存中?

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

13、Redis的同步机制了解么?

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

14、Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

15、是否使用过Redis集群,集群的原理是什么?

1)、Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

2)、Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

16、Redis集群方案什么情况下会导致整个集群不可用?

答:有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用。

20、说说Redis哈希槽的概念?

答:Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。

21、Redis集群的主从复制模型是怎样的?

答:为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.

22、Redis集群会有写操作丢失吗?为什么?

答:Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

23、Redis集群之间是如何复制的?

答:异步复制

24、Redis集群最大节点个数是多少?

答:16384个。

25、Redis集群如何选择数据库?

答:Redis集群目前无法做数据库选择,默认在0数据库。

26、怎么测试Redis的连通性?

答:使用ping命令。

27、怎么理解Redis事务?

答:

1)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

2)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

28、Redis事务相关的命令有哪几个?

答:MULTI、EXEC、DISCARD、WATCH

29、Redis key的过期时间和永久有效分别怎么设置?

答:EXPIRE和PERSIST命令。

30、Redis如何做内存优化?

答:尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面.

31、Redis回收进程如何工作的?

答:一个客户端运行了新的命令,添加了新的数据。Redi检查内存使用情况,如果大于maxmemory的限制, 则根据设定好的策略进行回收。一个新的命令被执行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

32、都有哪些办法可以降低Redis的内存使用情况呢?

答:如果你使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。

33、Redis的内存用完了会发生什么?

答:如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。34、一个Redis实例最多能存放多少的keys?List、Set、Sorted Set他们最多能存放多少元素?

答:理论上Redis可以处理多达232的keys,并且在实际中进行了测试,每个实例至少存放了2亿5千万的keys。我们正在测试一些较大的值。任何list、set、和sorted set都可以放232个元素。换句话说,Redis的存储极限是系统中的可用内存值。

35、MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

答:Redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

相关知识:Redis提供6种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据

36、Redis最适合的场景?

1、会话缓存(Session Cache)

最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

2、全页缓存(FPC)

除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

3、队列

Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。 如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

4,排行榜/计数器

Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“userscores”,我们只需要像下面一样执行即可: 当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE userscores 0 10 WITHSCORES Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

5、发布/订阅

最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!

38、如果有大量的key需要设置同一时间过期,一般需要注意什么?

答:如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。

39、使用过Redis做异步队列么,你是怎么用的?

答:一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果对方追问可不可以不用sleep呢?

list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。

如果对方追问pub/sub有什么缺点?

在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如RabbitMQ等。

如果对方追问redis如何实现延时队列?

我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。40、使用过Redis分布式锁么,它是什么回事?

先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。这时候对方会告诉你说你回答得不错,然后接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样?这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,然后回答:我记得set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!对方这时会显露笑容,心里开始默念:摁,这小子还不错。

 

你可能感兴趣的:(Redis)