1.redis为什么这么快?
1.基于内存,数据存放是类似哈希加链表的形式,查找速度快,时间复杂度低。(对内存的使用有很多优化策略,内存淘汰采用近似LRU算法通过抽样的方式来淘汰key以减少内存消耗...https://www.cnblogs.com/niejunlei/p/12898225.html)
2.单线程,避免上下文切换、避免死锁。
(多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换)
3.网络方面:非阻塞IO、多路复用IO,pipeline,并且在6.0版本后对网络请求采用多线程,提高了网络性能。
4.各种数据类型都用了非常巧妙的实现,比如数据类型如string采用简单动态字符串,减少修改字符串长度时候所需的内存重分配次数,比如zset、hash在存储数据较少时采用ziplist代替dict存储数据、set数据较少且为整数时采用intset代替dict存储数据,比如5.0之后新增了listpack代替ziplist,解决了ziplist级联更新的问题,并且有位图、hyperloglog这种专门处理海量数据的方案。
5.在6.0之后新增了客户端缓存,一部分请求不用通过网络(lua脚本就可以缓存在客户端)
6.根据环境可自行采用无盘复制或者磁盘复制,复制默认采用异步。
这是一道对redis考察非常全面的问题,根据回答基本可以判定你对redis的掌握程度。一般这是redis开场问题,会根据你的回答进行接下来的提问,所以对答到的点需要稍微深入的了解,如果一问就断太尴尬了。
2.redis内存淘汰策略有哪些?
- noeviction(默认):不淘汰,超出内存报错
- allkeys-lru:从所有键中淘汰最近最少使用的键
- volatile-lru:从设置了过期时间的键中淘汰最近最少使用的键(最常用)
- allkeys-random:从所有键中随机淘汰
- volatile-random:从设置了过期时间的键中随机淘汰
- volatile-ttl:从设置了过期时间的键中淘汰最快要过期的键
- volatile-lfu:从设置了过期时间的键中淘汰最少使用的键(最常用)
- allkeys-lfu:从所有键中淘汰最少使用的键
如果系统中有需要持久配置的键,一定不能使用all-keys参与淘汰的键,尤其是allkeys-random,推荐使用volatile-lfu、volatile-lru,即只对有过期时间的键进行清理。
不仅要知道策略,还要理解LRU和LFU实现原理,还要知道为什么这么实现,问到频率很高(即问题3,尤其是LRU使用非常广泛,如果能说出标准LRU和列举几个使用优化,会非常加分。很多人不知道LFU,如果能说出也很加分)。
(ps:在说到lfu时可以扯到自己项目中热门课程就是模仿lru实现的,学生点击课程+1分、学习一个流程+5分等,每过去一天减20分,这样既能突出自己理解lfu,还能突出自己学以致用,还能突出自己项目中redis不仅仅用来做缓存)
(LRU是一种非常经典的淘汰策略,不止redis,在数据库比如innodb的缓冲池也是通过LRU来管理,InnoDB 存储引擎对传统的 LRU 算法做了一些优化。 LRU 列表中加入了 midpoint 位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到 LRU 列表的首部,而是插入到 LRU 列表的 midpoint 位置。该位置默认在 LRU 列表长度的 5/8 处。midpoint之后的列表称为 old列表,之前的列表称为 new 列表,new 列表中的页通常是最活跃的热点数据。
这样做的好处是:缓冲池中的页大小默认为 16KB,如果将直接读取的页放入到 LRU 的首部,那么某些返回值较大的SQL 操作可能会占据过大空间,如果它被放入 LRU 列表的首部,那么非常可能将所需要的热点数据页从 LRU 列表中移除,而在下一次需要读取改页时,InnoDB 存储引擎需要再次访问磁盘。
参考:https://blog.youkuaiyun.com/u013164931/article/details/82423613
)
3.对redis内存淘汰策略LRU和LFU的了解?
LRU是为每页数据增加一个最后一次使用时间的key,在需要淘汰时,淘汰最后一次使用时间离现在最久的。
redis作者认为大部分情况下,缓存的访问遵循“二八定律”的情况下,使用近似LRU和使用真正LRU效果差不多,但是却能极大减少开销(因为不用再单独维护一个链表了),每次从内存中随机取5个key,淘汰掉最后一次使用距离现在最久的,如果内存还不够再取和淘汰,直到够用,这样做是为了节省内存,redis已经做了优化可以近似LRU的效果,如果需要,可以增大每次随机取的值,值越大越更接近LRU,但是开销也相对增大,更改后需要测试增大后缓存命中率是否增大。
redis4之后新增了:LFU。主要是为了解决LRU算法误把暂时不用但以后还要用的数据删掉的问题,可能某些key在最近被访问过后就不再访问了,而有些key是很久以前访问过,但是以后很可能也要访问的,如果根据LRU算法,依靠最近一次使用时间来淘汰,就会导致前一类key保留下来,反而后一类真正需要的key被淘汰的问题,LFU算法在Redis中是通过一个计数器来实现的,每个key都有一个计数器,访问频度越高,计数器的值就越大,Redis就根据计数器的值来淘汰key,当然计数器的值也是会随着时间减少的,相对于LRU多了评测依据。它可以设置每分钟递减值和计算器最大值、新添加的key的初始值等,官方认为它比LRU要好。
(redis的近似lur由于保存时间戳的字段在194天后会溢出,会造成时间错乱,但是通常不常出现194天还没被驱逐的缓存)
Redis作为LRU Cache的实现-阿里云开发者社区 (aliyun.com)
4.过期的key如何删除?
主动和被动两种策略相结合。
主动:redis把有过期时间的key放入一个字典结构,每次从中随机取20个key,如果其中超过25%过期就删除他们重新取,直到过期数据量占比小于25%,这个扫描过程默认每秒执行10次。
被动:去查询某个key时发现已过期,就清除它并返回null。
这两种策略需要结合使用,以避免大量过期key占用内存,又避免需要过度频繁主动查找删除占用内存。
(RDB:save、bgsave和AOF:rewriteaof、bgrewriteaof都会过滤掉已过期未删除的数据
为了保证一致性,在AOF持久化模式中,当key过期时候,会同时发送DEL命令给AOF文件和所有节点,从节点不会主动的删除过期key除非它升级为主节点或收到主节点发来的DEL命令)
5.lua了解多少?
lua脚本具有原子性,执行脚本时不会执行其他脚本或Redis命令,这点和事务是一样的,redis官方也考虑用lua完全替代事务。
优点:它在执行过程中还可以根据执行情况进行下一步动作(if then else)
并且还可以进行语句缓存,减少传输语句的带宽压力。
可以脚本复制:有复制脚本本身和脚本运行后产生的效果两种模式。
缺点:内存溢出问题:lua的第一个命令就会导致内存溢出的话,lua会整个停止,否则就算之后的命令会导致内存溢出,也会完整执行,内存使用量可能会超过maxmemory,为了避免这种情况,maxmemory-policy可以不使用“noeviction”(不淘汰)或使lua脚本保持简短。(redis事务也有此‘缺点’)
6.redis有哪些数据结构?
1.字符串
2.列表
3.哈希
4.集合
5.有序集合
6.位图
7.hyperloglog超日志
8.streams
9.GEO
7.redis有哪些使用场景?
缓存
秒杀
布隆过滤器
位图:大数据处理(也可以通过位图自行实现布隆过滤器)
hyperloglog:大数据统计、网站日活、月活用户
发布订阅消息
stream:消息队列
列表或有序集合:记录各种系统日志
有序集合:统计接口访问频次、数据表查询/修改频次、统计访问较慢的接口和数据表、数据统计与计算、绘制各种统计图表
hash:记录用户token信息
记录和统计网站访客的ip、访问时间分布等信息
集合+哈希+有序集合:
集合记录每个课程学员id,哈希记录全局每个学生用户名、头像(userid:用户名_头像)。
每个流程未完成学生复制课程学员集合(这个过程可以在第一次访问已完成或未完成列表时完成),当有学员完成后,就将该学生从未完成移除,增加到记录完成的有序集合里,userid:提交时间(签到时间)
签到也适用,只不过未签到最后会被细分到不同集合:缺勤、请假。
集合记录每个课程学员id,每节课复制一份,上课选学生回答问题时spop一个,这样本节课不会重复选某个学生答题。
hash记录全校教师id:教师名字,检索教师时可以ssan,
有序集合:
系统登录日志,每个有序集合记录每天用户登录日志(在分布式系统中可以为key添加{}以确保日志key在同一个服务器上。每个有序集合记录当天数据而不是更久的是为了防止出现过大key影响性能,又更适合删除过期key,而且之前的日志基本不怎么看,可以不加载到内存占空间,又因为每个key限制512MB),value就是时间戳,需要单独的key记录日志总数。
每个用户的操作记录也可以记录为有序集合。
8.项目中哪些地方用到过redis?
布隆过滤器:redis布隆过滤器插件实现登录过滤,白名单上用户才可以登录(需要考虑误判的情况,并且弄明白其实现原理)
list消息队列:请求第三方资源时一段时间内没有回复就认为任务失败,放到消息队列中延迟进行。
缓存:二级缓存
缓存预热:教师备完课后,用redis执行 pipeline模拟本班学生访问课程页面,使数据库产生缓存,学生上课时可以直接访问服务端缓存
系统初始化时,模拟用户登录,对用户身份、分组等相关信息进行缓存预热,在真正投入使用时可以直接访问缓存
统计图表:图表统计使用有序集合、列表等填充统计数据,更加快捷
有序集合:实现热门课程,有用户点进本课程并浏览就加1分、学完一个学习模块加5分,以课程积分来计算最热门课程排行榜,并且每天凌晨对课程热度减去100(这类似于redis的内存逐出策略:最少使用。做热门之类的业务一定要强调自己有热度降低机制,可以逐小时、逐天递减等)。
集合:实现最新课程
hash:存储登录用户的token、个人信息
有序集合:统计单个、整个班级、年级、学校的学生学习时长,学习时间段分布。个人中心时间线(为什么要用有序集合:可以排序,可以做交集、并集,统计方便。帮助教师了解学生学习各个模块所花的时间,包括预习和做题)
列表:自动补全信息:网站热门搜索课程补全(学生本人搜索记录由前端缓存实现)
收藏文章、题目、点赞、关注好友
统计资源受欢迎程度:收藏一个,+1分,教师备课使用一次,+20分。
由于系统中学生所加的课程比较固定,因此会用集合记录每个课程下学生id,用
9.sentinel故障转移过程?
(面试官会问使用过sentinel还是cluster,一旦回答会针对所选回答刨根问底,sentinel集群原理相对来说更简单一些,sentinel架构最少1主2从3sentinel,sentinel负责监控集群状态并进行故障转移,cluster架构至少3主3从,每个节点都参与集群监控,主节点参与故障转移投票,需要注意的是cluster涉及数据槽的概念,即数据分散存储在不同主节点的不同数据槽里,单个操作是不能涉及多个数据槽的,涉及分布式事务相关问题)
一个哨兵在一段时间内向master发送ping一直没有得到有效回复,就将master标为sdown状态,一段时间内达到法定人数的哨兵都将master标为sdown后,哨兵们会把master标为odown并推选出一个哨兵,这个哨兵在取得大多数哨兵的授权后,负责根据从主机优先级等信息选出一台从主机将其变更为新的master,并把新主机配置信息和配置版本号发给所有哨兵和从主机们让他们更新配置,如果这个哨兵一段时间内没有完成切换任务,其他主机会依次尝试进行切换。
sdown状态的从节点不参与选举,如果sentinel集群中大部分sentinel故障,将永远完不成故障转移过程。
10.cluster故障转移过程?
一个节点在一段时间内向某个节点发送ping一直没得到有效回复,就将它标记为pfail,一段时间内,如果大部分主节点都将此主节点标为pfail,此节点就被标记为fail,节点失效,它失效的消息会(用流言协议)很快扩散到整个集群,如果这个fail节点是从节点,它重新上线后fail状态即可解除,如果是主节点,它fail后它的从节点们会参与选举,选出一个主节点后它重新上线后会变为新主节点的从节点,要是它下线后新的主节点还没选出它就又上线了,它就仍然是主节点。
主节点从出现问题到被标记为fail的过程中,原主节点上的槽是不可用的,其他主机上的槽不受影响。某个主节点被标记为fail到新的主节点选举出来并完成故障转移的过程中,整个集群都处于fail状态,无法接收任何请求(包括read)。
在大部分主节点都进入pfail状态时,集群也会进入fail状态。
11.redis有哪些持久化方案?
有AOF和RDB两种
AOF即只追加日志,可以每秒写入磁盘或者每次写操作都写入磁盘。
优点:服务器故障时丢失的数据较少,只有1秒内或者完全不丢失。
缺点:对性能有一定的影响,尤其是每次写操作都写入磁盘时,如果每秒写入一次会丢失1秒内的数据。并且AOF文件会越来越大,不过redis支持bgrewriteaof,它可以以最小体积重写当前AOF文件。
RDB是每隔指定周期(比如5分钟内有100个更改)生成当前数据的快照(执行bgsave),父进程会fork()出一个子进程来生成快照。
优点:可以将数据恢复到指定时间点,对于灾难恢复非常有用,结构紧凑,并且父进程只需要fork()出一个子进程来执行快照,在备份期间父进程工作不受影响,且用它重启数据集比AOF更快。
缺点:会丢失上次保存至今的数据,并且数据量很大或CPU性能不佳时,fork()过程耗时增加,这会造成主进程在一段时间内暂停接受读写请求。
我们可以两种方式结合使用,并且配合定期bgrewriteaof。在集群或哨兵模式下,可以仅在从节点进行备份工作,以减少主节点的压力。
在某些时候,还可以通过手动bgsave来进行RDB备份,它与真正的RDB相比优势在于可以指定执行时间,另外,虽然save会阻塞redis直到快照生成完毕,但是由于它不需要创建子进程并且没有子进程争抢资源,通常比bgsave快很多。
ps:这是redis.conf中相关配置(我在面试中有面试官问到详细配置)
AOF:
#开启aof
appendonly yes
#always 每次写都刷盘,everysec每秒刷盘,no不主动刷盘,按系统配置。系统通常配置30s一次
appendfsync always
RDB:
save 3600 1 #每3600s有1次更改就bgsave一次
save 300 100 #每300s有100次更改就bgsave一次
save 60 10000 #每60s有10000次更改就bgsave一次

https://redis.io/topics/persistence
12.redis主从数据如何同步?
13.redis常用配置有哪些?
14.mysql、redis怎样保持数据一致性
15.阻塞IO、非阻塞IO、多路复用
阻塞、非阻塞、多路复用都是针对线程而言的
阻塞IO:在建立连接、读取数据等需要网络连接的操作时,需要等待当前客户端处理完成后才能出来其他客户端的请求。
非阻塞IO:在建立连接、读取数据等需要网络连接的操作时,当前客户端处理过程中也能出来其他客户端的请求(通过将主动套接字转化为监听套接字)。
IO多路复用:一个线程处理多个 IO 流,内核同时存在多个监听套接字和已连接套接字,内核会一直监听这些套接字上的连接请求或读写操作
(IO多路复用实现方式:
轮询:如select、poll
通知:如epoll(redis、nginx、linux都是采用epoll实现多路复用的)它可以看做select 和 poll 的增强版,没有文件描述符的限制,消息在内核和用户空间传递时不需要拷贝,也不是通过事件轮询获取就绪的套接字,而是通过事件就绪通知的机制:通过 epoll_ctl 注册 fd,一旦该 fd 就绪,内核就会采用类似 callback 的回调机制来激活该 fd,epoll_wait 便可以收到通知,然后进行相应的处理)
基于select实现 IO 多路复用类似于 Ajax,要一直来查询,epoll类似Websocket,基于主动通知。
16.redis安全
更改默认端口
设置密码
设置内存限制、逐出方式:默认是不进行逐出的,即超出后不逐出,只是新的写入、修改之类增加内存的操作会返回错误,这在线上是不能忍受的,推荐使用volatile-lru、volatile-lfu
限制写入频率:主要是为了减少恶意攻击带来服务瘫痪问题
通信方式加密:默认通信方式是基于TCP的RESP协议明文传输,可以借助第三方代理进行加密,例如Spiped,它的文本在客户端加密后到服务端再解密,
任何分布式事务的执行,都需要先加锁以确认能对数据进行排他性访问,在事务处理完成后还需要及时的释放锁,
17.redis优化
不要设置太大key
增删改时候一次操作大量内存,在分布式系统中还更可能在迁移中带来卡顿等问题,可能造成倾斜。
分开缓存和内存数据库
缓存数据库关闭AOF等,内存数据库开启,二者使用场景不同需要的数据安全级别也不同
分库(单机情况下)
分库可以使每个库中key减少,hash冲突的概率减少,扩容频率等也会减少。
设置合适内存
在机器配置允许范围内把内存调到最大,否则内存太小频繁逐出,势必降低命中率,并需要经常检查redis服务器内存占用情况,坚决避免出现占满后写不进数据的情况。
选择合适回收策略
官方推荐LFU、LRU,可以根据自己服务器内存配置,避免出现有些永久key还未来得及持久化就被逐出,也避免很多不常用的key一直占据内存,LFU、LRU很多参数可以根据自己系统进行配置。
选择合适持久化方案
append only + RDB两种方案结合,设定合适频率,bgrewrite
使用最新版本redis
redis5.0新增了streams等,6.0新增了I/O多线程
安全
更改默认端口、通信内容根据业务考虑加密、写入频率限制
性能测试、info
redis-benchmark -n 10000 -q
测试当前配置是否符合预期
日志
配置合适日志级别redis.log并定时查看日志
1. debug:会打印出很多信息,适用于开发和测试阶段
2. verbose(冗长的):包含很多不太有用的信息,但比debug要清爽一些
3. notice:适用于生产环境
4. warning : 警告信息
18.缓存穿透、击穿、雪崩分别是什么,怎样处理
缓存穿透
访问数据库中不存在的记录时,由于缓存中也没有结果,所以请求会穿透缓存直接到达数据库
解决:1.为空的返回结果也设置缓存,但这样无法提前拦截对数据库的写操作,如果是类似CC攻击这种多个不同空请求就无法减轻服务器的压力,特别是涉及一些比较费时的查询就更加明显。2.布隆过滤器:根据业务场景提前把不可能/允许的数据全部存进去。但是有一定的限制
缓存击穿
击穿主要针对访问量较大的热点key来说,数据库中有的记录原本应该有缓存,但是缓存因为失效、被更新或者还未来得及缓存就突发热点,造成大量读请求几乎同时访问数据库。
解决:对数据库的读写操作加读写锁,避免写的同时大量读请求的到来。
采用二级缓存互相更新热点key的缓存、适当延长缓存时间甚至永不过期
缓存预热,对热搜等可以预判到的热点key提前缓存。
缓存雪崩
缓存雪崩是指大量key同时失效,使大量访问直接到达数据库,比起穿透,它更强调“同时”,可能单个key读压力并不是很大,但是大量key失效就压力极大了。
解决:给不同的key设置不同缓存时长,加上一个随机值
二级缓存、适当延长缓存时间
173万+

被折叠的 条评论
为什么被折叠?



