什么是redis
RemoteDictionaryServer(Redis)是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是字符串(String),哈希(Map),列表(list),集合(sets)和有序集合(sortedsets)等类型。
redis数据结构
字符串String、字典Hash、列表List、集合Set、有序集合SortedSet。
如果你是Redis中高级用户,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。
如果你说还玩过RedisModule,像BloomFilter,RedisSearch,Redis-ML,面试官的眼睛就开始发亮了。
使用过Redis分布式锁么,它是什么回事?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
如果在setnx之后执行expire之前进程意外crash或者要重启维护了,这个锁就永远得不到释放了。
假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。redis的单线程的。keys指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会比直接用keys指令长.
Scan0matchnamecount5
Redis做异步队列如何使用
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。
Redis做延时队列
使用zset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
zaddmessagequethreemessage
redis过期时间设置策略
如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
Redis如何做持久化
bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会耗费较长时间,不够实时,在停机的时候会导致大量丢失数据,所以需要aof来配合使用。在redis实例重启时,会使用bgsave持久化文件重新构建内存,再使用aof重放近期的操作指令来实现完整恢复重启之前的状态。
bgsave工作流程
redis通过创建(fork)子进程来进行bgsave操作,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
aof如何配置
appendonlyyes开启持久化,
appendfsynceverysec异步持久化,每秒记录
auto-aof-rewrite-percentage重写aof文件,文件过大
pipeline的作用
可以将多次IO往返的时间缩减为一次,使用Pipeline执行速度比逐条执行要快,特别是客户端与服务端的网络延迟越大,性能体能越明显。
redis主从同步过程
第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
从节点执行slaveof[masterIP][masterPort],保存主节点信息
从节点中的定时任务发现主节点信息,建立和主节点的socket连接
从节点发送Ping信号,主节点返回Pong,两边能互相通信
连接建立后,主节点将所有数据发送给从节点(数据同步)
主节点把当前的数据同步给从节点后,便完成了复制的建立过程。接下来,主节点就会持续的把写命令发送给从节点,保证主从数据一致性。
主节点发送数据给从节点过程中,主节点还会进行一些写操作,这时候的数据存储在复制缓冲区中。从节点同步主节点数据完成后,主节点将缓冲区的数据继续发送给从节点,用于部分复制。
主节点响应写命令时,不但会把命名发送给从节点,还会写入复制积压缓冲区,用于复制命令丢失的数据补救。
redis的线程模型
redis内部使用文件事件处理器fileeventhandler,这个文件事件处理器是单线程的,所以redis才叫做单线程的模型。它采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器进行处理。
redis6哪些新特性
新增ACL权限控制
(1)接入权限:用户名和密码(2)可以执行的命令(3)可以操作的KEY
新的Redis通信协议
Resp3redisserializationprotoal
客户端缓存
IO多线程
IO多线程其实指客户端交互部分的网络IO交互处理模块多线程,而非执行命令多线程
Disque
Distributejobqueue分布式任务队列,一个插件。
redis单线程模型效率高的原因
纯内存操作
核心是基于非阻塞的IO多路复用机制
单线程反而避免了多线程的频繁上下文切换问题
redis的过期策略
定期删除和惰性删除结合。
内存淘汰机制
noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)。
allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的key给干掉啊。
volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)。
volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
redis的哨兵是什么
sentinel用来监控redis实例运行状况,负责多个实例之间交换监控信息。
主从Redis的主服务器不能正常工作时,Sentinel会开始进行故障迁移操作。将一个从服务器升级新的主服务器。让其他从服务器挂到新的主服务器。同时向客户端提供新的主服务器地址。
redis集群如何配置
使用rediscluster来搭建集群,然后采用master-salve配置高可用。支持更多的数据,只需要扩充cluster节点redis实例数即可。
Rediscluster可以自动将数据进行分片,每个master节点保存部分数据,一个节点坏了,其它的节点依然可以正常工作。
redis集群节点如何通信
集中式:底层采用zookeeper进行调度,将所有节点信息保存在一个节点上。时效性非常好,数据存储有压力。
Gossip:gossip协议是可扩展的,一般需要O(logN)轮就可以将信息传播到所有的节点,其中N代表节点的个数。每个节点仅发送固定数量的消息,并且与网络中节点数目无关。在数据传送的时候,节点并不会等待消息的ack,所以消息传送失败也没有关系,因为可以通过其他节点将消息传递给之前传送失败的节点。系统可以轻松扩展到数百万个进程。节点数据存储无压力,但是元数据更新有延迟。
gossip消息类型有哪些
Pingpongmeetfail
一致性hash算法
将hash值组成一个虚拟的圆环,整个空间按照顺时针方向排列,当一个节点挂掉,只会影响当前节点和下一个节点之间的环形区域。
一开始节点过少的时候,可以增加虚拟节点,这样可以实现数据的分布均匀。
hashslot算法
Rediscluster一共有个hashslot,对每个key计算CRC16值,然后对取模,可以获取key对应的hashslot。
rediscluster中每个master都会持有部分slot,比如有3个master,那么可能每个master持有多个hashslot。hashslot让node的增加和移除很简单,增加一个master,就将其他master的hashslot移动部分过去,减少一个master,就将它的hashslot移动到其他master上去。
移动hashslot的成本是非常低的。客户端的api,可以对指定的数据,让他们走同一个hashslot,通过hashtag来实现。
任何一台机器宕机,另外两个节点,不影响的。因为key找的是hashslot,不是机器。
主从复制中,主节点选举机制
每个从节点,都根据自己对master复制数据的offset,来设置一个选举时间,offset越大(复制数据越多)的总节点,选举时间越靠前,优先进行选举。
所有的masternode开始slave选举投票,给要进行选举的slave进行投票,如果大部分masternode(N/2+1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成master。
从节点执行主备切换,从节点切换为主节点。
redis的雪崩和缓存穿透
缓存雪崩:因为某种原因,redis机器挂掉,所有的请求打到mysql数据库上,造成数据库挂掉。
解决方案:主从配置,配置redis-cluster,避免redis全部宕机,增加限流算法,避免请求数据全部打到数据库上,redis一定要开启rdb和aof备份,方便快速恢复。
缓存穿透:所有的请求全部没有命中缓存,导致请求全部打到数据库上。
解决方案:
数据库中没有的数据设置一个空值,这样请求就会走缓存。
数据库的双写一致性
读请求,写请求串行化。
Cashasidepattern先读缓存,没有命中读数据库,写入缓存。更新的时候,更新数据库,删除缓存。
更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个jvm内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个jvm内部队列中。
一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
这里有一个优化点,一个队列中,其实多个更新缓存请求串在一起是没意义的,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。
Rediscas机制
将key保存到数据库的时候加上时间戳,如果更新的时候时间戳比缓存新的话,可以直接更新,否则不能更新。
使用wath监控多个键,然后使用multi开启事务,如果执行的过程中还想数据被修改了,则执行失败,回滚整个操作。
redis事物执行过程
multi开始事物
执行操作入队
exec提交事物
Redis中的常用命令哪些
ncr让当前键值以1的数量递增,并返回递增后的值
incrby可以指定参数一次增加的数值,并返回递增后的值
decrby可以指定参数一次递减的数值,并返回递减后的值
incrbyfloat可以递增一个双精度浮点数
append作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value。返回值是追加后字符串的总长度。
mget/mset作用与get/set相似,不过mget/mset可以同时获得/设置多个键的键值
del根据key来删除value
flushdb清除当前库的所有数据
hset存储一个哈希键值对的集合
hget获取一个哈希键的值
hmset存储一个或多个哈希是键值对的集合
hmget获取多个指定的键的值
hexists判断哈希表中的字段名是否存在如果存在返回1否则返回0
hdel删除一个或多个字段
hgetall获取一个哈希是键值对的集合
hvals只返回字段值
hkeys只返回字段名
hlen返回key的hash的元素个数
lpushkeyvalue向链表左侧添加
rpushkeyvalue向链表右侧添加
lpopkey从左边移出一个元素
rpopkey从右边移出一个元素
llenkey返回链表中元素的个数相当于关系型数据库中selectcount(*)
lrangekeystartendlrange命令将返回索引从start到stop之间的所有元素。Redis的列表起始索引为0。
lrange也支持负索引lrangenn-2-1如-1表示最右边第一个元素-2表示最右边第二个元素,依次类推。
lindexkeyindexnumber如果要将列表类型当做数组来用,lindex命令是必不可少的。lindex命令用来返回指定索引的元素,索引从0开始
如果是负数表示从右边开始计算的索引,最右边元素的索引是-1。
Lsetkeyindexnumbervalue是另一个通过索引操作列表的命令,它会将索引为index的元素赋值为value。
saddkeyvalue添加一个string元素到,key对应的set集合中,成功返回1,如果元素已经在集合中返回0
scardkey返回set的元素个数,如果set是空或者key不存在返回0
smemberskey返回key对应set的所有元素,结果是无序的
sismemberkeyvalue判断value是否在set中,存在返回1,0表示不存在或者key不存在
sremkeyvalue从key对应set中移除给定元素,成功返回1,如果value在集合中不存在或者key不存在返回0
zaddkeyscorevalue将一个或多个value及其socre加入到set中
zrangekeystartend0和-1表示从索引为0的元素到最后一个元素(同LRANGE命令相似)
zrangekey0-1withscores也可以连同score一块输出,使用WITHSCORES参数
zremrangebyscorekeystartend可用于范围删除操作
ping测试redis是否链接如果已链接返回PONG
echovalue测试redis是否链接如果已链接返回echo命令后给定的值
keys*返回所有的key可以加*通配
existskey判断string类型一个key是否存在如果存在返回1否则返回0
expirekeytime(s)设置一个key的过期时间单位秒。时间到达后会删除key及value
ttlkey查询已设置过期时间的key的剩余时间如果返回-2表示该键值对已经被删除
persist移除给定key的过期时间
selectdbindex选择数据库(0-15)
movekeydbIndex将当前数据库中的key转移到其他数据库中
dbsize返回当前数据库中的key的数目
info获取服务器的信息和统计
flushdb删除当前选择的数据库中的key
flushall删除所有数据库中的所有key
quit退出连接
Reids比memcache有什么优势
有丰富的数据类型
redis数据更快一些
redis支持持久化
一个字符串最多可以存储多大
M
redis主要使用场景
会话缓存,保存用户session等信息
全页缓存,缓存整个网页页面
用做消息队列
进行排行榜
Redishash如何使用
hash主要是用来存储对象的,可以非常快速的存储对象的各个属性。
Redis回收进程如何工作
客户端执行了一条命令,如果查过了redis的默认内存大小,那么redis就会根据设置的内存回收策略进行回收。
Redis分布式锁怎么使用
先用setnx增强锁,发送lua脚本给redis.然后给锁加一个expire过期时间。
watchdog自动延期机制
后台线程每10s进行查询,没过期,延长锁的过期时间
Redis如何设置过期时间
Expirepersist设置时间
Expirekeysecond
Pexpirekeyms
Exipreatkeytimestamp
Pexpireatkeytimestamp
Redis集群最大节点个数
Redis默认端口号
查看内存使用信息查看
Infomemory
used_memory_human:分配的总内存
Used_memory_rss_human不包括虚拟内存
Used_memory_peak_human内存使用最大值,就是used_memory峰值
Total_system_memory系统总内存
mem_fragmentation_ratio=used_memory_rss/used_memory
Redis调整内存上限
Configsetmaxmemroy1G
Redis为什么是二进制安全的
因为它内部使用sds来存储字符,它有length字段来表示何时截断字符串,而不是使用\0来截断字符串。
查看一个key的过期时间
Ttlkey
Redis中的ziplist是一种什么数据结构
zlbytes记录着整一个压缩表的长度
zltail记录着最后一个元素的偏移量,这是为了倒序遍历整个ziplist
zlen用来记录压缩表中节点的数量
entryX列表中的节点,节点用来存储具体的数据。
zlend一个特殊值0XFF,用来标记压缩列表已经结束了。
Redis内存优化方法
关闭vm,提升内存使用效率
设置maxmemory防止redis使用swap内存
zset底层数据结构
Ziplistskiplist
Ziplist条件,元素数量小于,每个元素小于64个字节
skiplist
有序列表的基本操作
zadd(key,score,member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。
zrem(key,member):删除名称为key的zset中的元素member
zincrby(key,increment,member):如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment
zrank(key,member):返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”
zrevrank(key,member):返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”
zrange(key,start,end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素
zrevrange(key,start,end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素
zrangebyscore(key,min,max):返回名称为key的zset中score=min且score=max的所有元素zcard(key):返回名称为key的zset的基数
zscore(key,element):返回名称为key的zset中元素element的scorezremrangebyrank(key,min,max):删除名称为key的zset中rank=min且rank=max的所有元素zremrangebyscore(key,min,max):删除名称为key的zset中score=min且score=max的所有元素