赞
踩
Redis的应用场景有很多,比如缓存、分布式锁、消息队列、延迟队列、排行榜,每一个场景都会有对应的问题和成熟的解决方案,都需要进行了解,特别要结合自身的项目进行侧重。下面会列出多种面试会问到的点,以及一些解决方案。
char *
定义的字符串,因为其有很多问题,比如:内存分配带来的开销、安全性问题(C 语言的字符串没有保存字符串的长度信息,容易导致缓冲区溢出和越界访问等安全问题)、复杂的字符串操作问题,所以SDS的优点有如下:O(1) 时间获取字符串的长度、支持动态扩容、使用预分配来减少内存分配次数、拥有二进制安全(涉及到二进制字符、字符串长度信息、越界访问等等)。
String:编码方式RAW编码,基于SDS实现,存储上限为512mb,如果SDS长度小于44字节,则会采用EMBSTR编码,如果存储的字符串是整数值,并且大小在LONG_MAX范围内,则会采用INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节)
List:3.2版本之前,Redis采用ZipList和LinkedList来实现List,当元素数量小于512并且元素大小小于64字节时采用ZipList,超过则采用LinkedList编码实现。在3.2版本之后,Redis统一采用QuickList实现List
Set:采用HT编码(Dict)。Dict中的key用来存储元素,value统一为null(和Java中的HashMap与HashSet类型),当存储的所有数据都是整数,并且数量不超过set-max-intset-entries时,set会采用IntSet编码,以节省内存
ZSet:也就是sorted set,其中每一个元素都需要指定一个score和member值,需要根据score值排序,member必须唯一,可以根据member查询分数,能够总结出三个点:键值存储、键必须唯一、可排序,自然用到的编码方式就有:SkipList(可以排序,并且可以同时存储score和member值)、HT(Dict):可以键值存储、并且可以根据 key 找 value,优化点:当元素数量不多时,HT和SkipList的优势不明显,而且更消耗内存,因此zset还会采用ZipList接口来节省内存,不过需要同时满足两个条件:1.元素数量小于zset_max_ziplist_entries(默认128),2.每个元素都小于zset_max_ziplist_value字节(默认64)。ZipList本身没有排序功能,没有键值对的概念,因此需要由zset通过编码实现:ziplist是连续内存,而且score和element是紧挨在一起的entry,element在前,score在后,score越小越接近队首,越大越接近队尾,按照score值升序排列,这种方式只是为了节省内存,查询效率并不提升,这是兼顾内存节省和查询性能的表现。
Hash:Hash底层采用的编码和Zse基本一致,只需要把排序有关的SkipList去掉即可,Hash结构默认采用ZipList编码,以节省内存,ZipList中相邻的两个entry分别保存field和value,当数据量较大的时候,Hash结构会转为HT编码,也就是Dict,必须满足:ZipList中的元素数量超过了hash-max-ziplist-entries(默认512),ZipList中任意entry大小超过了hash-max-ziplist-value字节(默认64)
save 900 1
的意思是900秒内,如果至少有一个 key 被修改,则执行 basave。RDB数据安全性不高,因为是周期性生成的,可能会丢失最近一次持久化的之后的数据。appendonly yes
,指定文件:appendfilename "filename"
,同时AOF的命令记录的频率也可以通过redis.conf文件来配置,有三种刷盘策略,1.同步刷盘,2.每秒刷盘,3.操作系统控制。AOF还可以通过设置重写命令的触发阈值,来自动的对所记录的命令做简化,用最少的命令达到相同的效果,具体的可以在redis.conf中配置,触发阈值:一个是超过上次文件大小的百分比,一个是和上次文件体积相差多大。AOF持久化方式相对于RDB具有更高的数据安全性,尤其是在数据丢失方面具有更好的保障,因为AOF文件记录了每个操作。AOF数据恢复比较慢,是因为AOF文件记录了每个写操作的详细信息,而在数据恢复过程中需要逐条回放这些操作,导致了恢复的耗时惰性删除:设置该key过期时间后,我们不去管它,当需要该 key 时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key
优点:性能影响较少,对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该key就会一直存在内存当中,内存永远不会自主释放,造成内存泄漏
定期删除:每隔一段时间,Redis就会对一些key进行检查,删除里面过期的key(从一定量的数据库中取出一定量的随机key进行检查,并删除其中的过期key),SLOW模式是定时任务,执行策略默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整这个次数,FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点:可以通过限制删除操作的执行时长和频率来减少删除操作对CPU的影响,能有效释放过期key占用的内存
缺点:删除不够实时
定时删除:设置一个key的过期时间时,增加一个定时任务,检测key将要过期时,直接将其删除
优点:删除key比较及时,对内存比较友好
缺点:如果本身业务操作频繁时,刚好有一大批key同时过期时会导致cpu过高
Redis的过期删除策略:惰性删除 + 定期删除,配合使用能够有效的删除过期key
服务器集群部署模式下,如果不对某些业务加分布式锁,就会出问题!好在Redis帮我们实现了分布式锁,来看看具体的分布式锁吧。
Redis实现分布式锁主要利用Redis的setnx命令。setnx时 set if not exists 的简写,在之前setnx实现的分布式锁其实是两条命令,不是原子的,后来Redis官方给 set 命令添加了 nx 选项,所以Redis分布式锁是set命令来执行的:set lock value NX EX 10
(EX表示设置超时时间,单位是秒),业务结束使用del key
释放锁,但是要考虑到死锁的情况,就是我执行完了业务,但是服务器宕机了,释放锁的动作无法执行,导致其他线程一直在死等,就造成了死锁。我们可以发现这个分布式锁需要设置过期时间,万一我们设置的时间段比较小,业务还在执行中,锁过期了,其他线程抢到了锁,这不就完蛋了吗,这个时候有两种解决方案:
单节点模式的Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,来实现读写分离,主节点称为 master ,从节点称为 slave 或者 replica,一般会有多个从节点,因为缓存主要是读多写少的,从节点就会负责读操作,主节点会负责写操作,在主节点写操作的时候,就需要考虑到一个问题,就是主从结点的数据一致性问题。
两种方式:主从全量同步、主从增量同步
比较重要的是从节点和主节点都会有replid和offset的概念,一个是数据集的标识,另外一个偏移量标识的是当前结点的数据集在哪一个”位置处“,下面的图是具体的流程步骤:
Redis的哨兵机制是考虑了主从集群的故障问题,实现集群的高可用,该机制能够提供以下作用:
监控:Sentinel会不断检查master和slave是否按照预期工作,也就是检查是不是出故障了
自动恢复故障:如果主节点挂掉了,Sentinel会将一个slave提升为master。当故障实例恢复后也是以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis客户端,主要就是目前新的主节点是哪个服务器会通知给Redis客户端,Sentinel是根据心跳机制来检测服务状态,每隔一段时间向集群主节点实例发送ping命令,这个时候会有两个现象,一个是某个主节点实例在某一个Sentinel检测下未响应,则是主观下线,如果超过半数(这个阈值可以自行设定)Sentinel都未接到响应,则该实例会客观下线,就要选取一个从节点当作新的主节点,这个选举的规则,具体的可以查文档,要记住的是哪个从节点的offset越大,优先级就越高,因为它的数据越完整嘛
因为网络原因,导致主节点、从节点和Sentinel在不同的网络分区,哨兵无法感知主节点的存在,这就是脑裂问题,脑裂问题会导致原来的(真正的)主节点数据丢失,因为从节点会进行选主操作,在网络恢复后的话,选出来的主节点会将数据同步到真正的主节点中,导致数据丢失,因为这期间,真正的主节点在正常工作,接收客户端发送的写操作。
解决方案:我们在配置参数中可以设置一些选项,比如当主结点至少有一个从节点的时候,才会接收客户端发送的数据,另外一个是缩小主从数据同步的延迟,因为在短延迟中,同步操作会识别你有没有从节点,如果没有的话,就不会接收客户端发送的数据
分片集群可以解决海量数据存储、高并发写问题,分片集群结构就是有多个master节点,每个master节点的数据不同,并且可以持有属于自己的从节点slave,这样就可以解决上面的两个问题了。master之间通过ping检测彼此的健康状态,会进行主客观下线的操作,并且客户端的请求可以访问到集群中的任何节点,最终会通过路由转发到正确的节点。
数据读写:Redis分片集群架构引入了哈希槽的概念,Redis集群中共有16384个哈希槽,客户端发过来的每一个key的有效部分通过CRC16校验后对哈希槽的数量16384取模来决定放置哪个槽,集群的每个节点负责一部分的哈希槽
Redis是纯内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,IO多路复用模型就是实现了高效的网络请求。
Redis通过IO多路复用模型来提高网络性能,并且支持各种不同的多路复用实现(select、poll、epoll),并且将这些实现进行了封装,提供了统一的高性能事件处理器库(连接应答、命令回复、命令请求处理器等等)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。