1. 说一下你在项目中的redis的应用场景
redis 有五大常用数据类型:String、List、Hash、Set、Sorted Set
分别有各自的应用场景,一般是作为数据缓存,分布式下提供分布式锁的解决方案
2. redis是单线程还是多线程?
redis 不管那个版本,工作线程只有一个;6.X版本之后提供了 IO 多线程,满足 redis 的串行原子,只不过 IO 多线程后,把输入/输出放到更多的线程里去并行,执行时间段,更快,充分利用 服务器的硬件资源
3. redis存在线程安全的问题吗?为什么?
redis 的工作线程只有一个 ,保证串行执行的原子性,因此 redis 内部是可以保证线程安全的,但是外界使用 api 来操作 redis 时是不能保证的,需要业务上自行保障
解决:
缓存 null 值:对查询结果为 null 的数据进行缓存,长期使用的话需要定期清理,设置短时限失效,例如:30——60秒,最高 5 分钟
白名单策略:提请预热各种分类的数据 id 对应的 bitmaps,id 作为 bitmaps 的 offset,相当于设置数据白名单,当加载正常数据时放行,加载异常数据时直接拦截;只用布隆过滤器,布隆过滤器的命中问题针对恶意访问造成服务器压力可以忽略
实时监控:实时监控 redis 命中率与 null 数据的占比:非活动时间段波动--通常检查 3-5 倍,超过 5 倍纳入重点排查对象;活动时段检查 10-50 倍,超过 50 倍纳入重点排查对象;根据倍数不同,启动不同的排出程序,然后使用黑名单进行防控。
key 加密:对 key 进行业务层传输加密服务,设定校验程序,发现不满足规则的 key ,直接驳回数据访问。
5. 遇到过缓存击穿吗?详细描述一下。
- 定时删除:对每一个设置了过期时间的 key 都创建了一个定时器,只要到达过期时间就立即删除。该策略可以立即清除过期数据,对内存友好,但是占用过多的 cpu 资源处理过期数据,会影响 redis 的吞吐量和响应时间
- 惰性删除:当访问一个 key 的时候判断是否过期,过期删除。该策略可以最大限度的节约 cpu 资源,但是对内存不友好。极端情况下,会存在大量过期的 key 不会被访问到,占用过多内存空间
- 定期删除:每个一段时间扫描 redis 中过期字典中的 key,进行过期 key 删除,通过调整扫描周期和扫描限定耗时,可以使 cpu 和内存资源达到最优的平衡效果
9. 缓存是如何淘汰的
当 redis 的内存达到 maxmemory 的极限时,会通过某种算法来决定清除掉哪些数据,这就是 redis 的缓存淘汰策略
redis 提供 8 中缓存淘汰的策略算法:
- volatile_lru : 在设置了过期时间的 key 空间中,移除最近最不经常使用的 key,和最后一次访问 key 的事件有关
- allkeys-lru : 在 key 空间中,移除最近最不经常使用的 key
- volatile-lfu : 在设置了过期时间的 key 空间中,移除最近最少使用的 key,和使用次数有关,淘汰使用次数最少的
- allkeys-lfu : 在 key 空间中,移除最近最少使用的 key
- volatile-random : 在设置了过期时间的 key 空间中,进行随机移除
- allkeys-random : 在 key 空间中,进行随机移除
- volatile-ttl : 针对设置了过期时间的健值对,根据过期时间先后进行删除,越早过期的越先删除
- noeviction : 一旦缓冲写满了,再有新的写请求过来时,redis 不再提供服务,直接返回错误
lru : redisObject 中维护了一个 lru 属性,记录 key 的访问时间,当从 redis 中筛选出 N 个数据,比较 lru 属性,淘汰距离现在最久的
10. Redis 是如何持久化的?
redis 提供两种持久化方式 rdb 和 aof
rdb: rdb 是 redis 默认的持久化方式;按照一定的时间将内存的数据以快照的形式保存到硬盘中,通过 配置文件 save 参数来定义快照的周期
aof:将 redis 执行的每次写命令记录到单独的日志文件中,当重启 reis 会重新将持久化的日志中文件恢复数据。当两种方式同时开启时,数据恢复优先考虑 aof 恢复,配置 no、every second、always,只会丢失一秒的数据;
11. Redis 集群实现原理
redis cluster 是一种服务端 sharding 技术。cluster 并没有使用一致性 hash,而是采用 slot 槽的概念,一共分成 10384 ([0, 10383])个槽。将请求发送到任意节点,接受到请求的节点会讲查询请求发送到正确的节点上执行
说明:
通过 hash 的方式,将数据分片,每个节点均分存储一定哈希槽区间的数据,默认分配了 16384 个槽;
每份数据分片会存储在多个互为主从的多节点上
数据写入先写入主节点,再同步到从节点;
同一分片多个节点间的数据不保持一致性;
读取数据时,当客户端操作的 key 没有分配在该节点上时,redis 会返回转向指令,指向正确的节点;
扩容时需要把旧节点的数据迁移一部分到新节点
redis cluster 架构下,每个 redis 需要开发两个端口,如 6379,16379;16379 用来进行节点间通信,进行故障检查、配置更新、故障转移授权。使用二进制 gossip 协议,用于节点间进行高效的数据交换,占用更少的网络宽带和处理时间
12. Redis 与 MySQL 的双写一致性是如何保证的
延时双删:先删除缓存,再更新数据库,在等待具体读业务逻辑数据的耗时+几百毫秒后,再次删除缓存
删除缓存重试机制:若是第二次删除失败了,那么缓存数据和持久化还是不一致的,所以引入删除缓存重试机制,将删除失败的key放到消息队列中,将要删除的key拉去出来做重试删除的操作
读取binlog异步删除缓存:可以将 binlog 日志采集发送到 mq 队列中,通过 ack 确认送达机制处理这条更新消息,删除缓存,保证数据缓存一致性
为什么要删除缓存而不用更新呢 -- 线程 A 发起一个写操作,先更新了数据库 ,线程 B 发起一个写操作 ,在更新了数据库,由于网络原因,线程 B 先更新了缓存,而后线程 A 更新了缓存,导致缓存保存的还是 A 的数据。
更新缓存相对于删除缓存:1)如果写入的缓存值,是经过复杂计算才得到的话,更新缓存频率高的话,就浪费性能了 ;2)在写数据库场景多,读数据场景少的情况下,数据很多时候还没被读取到,又被更新了,这也浪费性能
13. redis 为什么快?
1)绝大部分请求是存粹的内存操作,基于内存的操作响应非常快速;
2)redis工作现场是单线程的,避免了不必要的上下文切换和竞争条件;
3)redis 才用非阻塞 IO 多路复用 epoll 模型,减少了 IO 操作上的损耗时间
假如 redis 里面有一亿个key,其中有 10w 个 key 是以某个固定的已知的前缀开头的,如果将它们全部找出来?
使用 keys 命令可以扫出指定模式的 key 列表;但是 redis 是单线程的 keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复,这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,但是整体所花费的时间会比直接用 keys 指令长。