1.1. 谈下你对 Redis 的了解?
Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API
1.2. Redis 与其他 key – value 缓存产品有以下特点
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
- 使用底层模型不同 它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
- value 值大小不同:Redis 最大可以达到 512M;Memcache 只有 1mb
1.3. Redis 一般都有哪些使用场景?
- 缓存:减轻 MySQL 的查询压力,提升系统性能
- 排行榜:利用 Redis 的 SortSet(有序集合)实现
- 计数器/限速器:利用 Redis 中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等。这类操作如果用 MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个 API 的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力
- 好友关系:利用集合的一些命令,比如求交集、并集、差集等。可以方便解决一些共同好友、共同爱好之类的功能
- 消息队列:除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦
1.4. Redis 有哪些常见的功能?
-
数据缓存功能
-
分布式锁的功能
-
支持数据持久化
-
支持事务
-
支持消息队列
1.5. Redis为什么这么快
- 基于内存
- 数据结构简单,数据操作简单
- 采用单线程
- 使用多路I/O复用模型,非堵塞IO
1.6. Redis 数据类型
基础
- string
- hash
- list
- set
- zset 即
sorted set
高级
- HyperLogLog 基数统计
- Geo 地理位置
- Bitmap 位存储
1.7. Redis持久化
RDB
RDB( Redis Database ),按照一定的时间周期策略把Redis内存中的数据保存以快照的形式保存到硬盘的二进制文件
。生成dump.rdb 文件
AOF
AOF(Append-only file),将收到的Redis命令以日志的形式追加到文件,类似于MySQL的binlog。
生成.aof 文件
常用持久化模式
RDB+AOF
RDB做镜像全量持久化,AOF做增量持久化
1.8. RDB和AOF的区别
-
AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据
-
AOF比 RDB 更安全也更大
-
RDB 性能比 AOF 好
-
如果两个都配了优先加载 AOF
1.9. Redis常见问题
1.9.1. 缓存雪崩
解释
大量的key过期时间设置过于集中
,到过期时间节点,缓存大面积过期,Redis处理短暂卡顿现象,导致大量的查询请求打到数据库
,数据库可能宕机
如何处理
- 热点数据永不过期
- 随机设置缓存过期时间
1.9.2. 缓存穿透
解释
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,我们数据库的 id 都是1开始自增上去的,如发起为id值为 -1 的数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据库
如何处理
- 查询参数校验
- 查询数据为null 设置缓存 过期时间较短
- 布隆过滤器(Bloom Filter)
- Nginx网关限制单IP请求数
1.9.3. 缓存击穿
解释
热点key大并发期间突然失效,导致大量请求请求数据库
如何处理
- 热点数据永不过期
- 互斥锁
1.10. Redis 内存淘汰策略有哪些?
no-enviction
默认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(驱逐):禁止驱逐数据
- noeviction:达到内存限制时不保存新值。当数据库使用复制时,这适用于主数据库
- allkeys-lru:保留最近使用的密钥;删除最近最少使用 (LRU) 键
- allkeys-lfu : 保存常用键;删除最不常用 (LFU) 键
- volatile-lfuexpire :删除字段设置为的最不常用键true。
1.11. Redis 常见性能问题和解决方案?
- Master不做RDB和AOF备份,由Slave开启AOF备份
- 主从服务器在同一局域网内
- 主从复制不采用树结构,使用单项链表如:
Master <- Slave1 <- Slave2 <- Slave3…
- 尽量避免在压力很大的主库上增加从库
1.12. Redis如何做内存优化?
- 控制RedisKey数量
- key值名称长度越短越好
- value值内容越短越好
1.13. 缓存预热,缓存降级,缓存更新
缓存预热
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
如果不进行预热,那么Redis初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。
缓存预热解决方案:
数据量不大的时候,工程启动的时候进行加载缓存动作;
数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
数据量太大的时候,优先保证热点数据进行提前加载到缓存。
缓存降级
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。
缓存降级解决方案:
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
缓存刷新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰
缓存刷新解决方案:
- 定时去清理过期的缓存;
- 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。
1.14. 为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。
Redis的操作之所以是原子性的,是因为Redis是单线程的。(Redis新版本已经引入多线程,这里基于旧版本的Redis)
Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.
1.15. Redis实现分布式锁
Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。
将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作
解锁:使用 del key
命令就能释放锁
del lock-key
解决死锁:
- 通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。
- 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就可以实现。
1.16. Redis高可用
高可用是通过设计,减少系统不能提供服务的时间,是分布式系统的基础也是保障系统可靠性的重要手段
- 数据持久化(RDB+AOF)
- 主从数据同步(主从复制)
- Redis 哨兵模式(Sentinel)
- Redis 集群(Cluster)
其中数据持久化保证了系统在发生宕机或者重启之后数据不会丢失,增加了系统的可靠性和减少了系统不可用的时间(省去了手动恢复数据的过程);而主从数据同步可以将数据存储至多台服务器,这样当遇到一台服务器宕机之后,可以很快地切换至另一台服务器以继续提供服务;哨兵模式用于发生故障之后自动切换服务器;而 Redis 集群提供了多主多从的 Redis 分布式集群环境,用于提供性能更好的 Redis 服务,并且它自身拥有故障自动切换的能力
1.16.1. 数据持久化
RDB+AOF 混合配置
127.0.0.1:6379> config get aof-use-rdb-preamble
1) "aof-use-rdb-preamble"
2) "yes"
1.16.2. Redis 主从同步
主从同步是 Redis 多机运行中最基础的功能,它是把多个 Redis 节点组成一个 Redis 集群,在这个集群当中有一个主节点用来进行数据的操作,其他从节点用于同步主节点的内容,并且提供给客户端进行数据查询。
Redis 主从同步分为:主从模式和从从模式。
主从模式就是一个主节点和多个一级从节点,而从从模式是指一级从节点下面还可以拥有更多的从节点。
主从模式可以提高 Redis 的整体运行速度,因为使用主从模式就可以实现数据的读写分离,把写操作的请求分发到主节点上,把其他的读操作请求分发到从节点上,这样就减轻了 Redis 主节点的运行压力,并且提高了 Redis 的整体运行速度。
不但如此使用主从模式还实现了 Redis 的高可用,当主服务器宕机之后,可以很迅速的把从节点提升为主节点,为 Redis 服务器的宕机恢复节省了宝贵的时间。
并且主从复制还降低了数据丢失的风险,因为数据是完整拷贝在多台服务器上的,当一个服务器磁盘坏掉之后,可以从其他服务器拿到完整的备份数据。
优点
- 高可靠性:一方面,采用双机主备架构,能够在主库出现故障时自动进行主备切换,从库提升为主库提供服务,保证服务平稳运行;另一方面,开启数据持久化功能和配置合理的备份策略,能有效的解决数据误操作和数据异常丢失的问题
- 读写分离策略:从节点可以扩展主库节点的读能力,有效应对大并发量的读操作
缺点
-
故障恢复复杂,如果没有RedisHA系统(需要开发),当主库节点出现故障时,需要手动将一个从节点晋升为主节点,同时需要通知业务方变更配置,并且需要让其它从库节点去复制新主库节点,整个过程需要人为干预,比较繁琐;
-
主库的写能力受到单机的限制,可以考虑分片;
-
主库的存储能力受到单机的限制,可以考虑Pika;
-
原生复制的弊端在早期的版本中也会比较突出,如:Redis复制中断后,Slave会发起psync,此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时可能会造成毫秒或秒级的卡顿;又由于COW机制,导致极端情况下的主库内存溢出,程序异常退出或宕机;主库节点生成备份文件导致服务器磁盘IO和CPU(压缩)资源消耗;发送数GB大小的备份文件导致服务器出口带宽暴增,阻塞请求,建议升级到最新版本
1.16.3. Redis 哨兵模式
Redis 主从复制模式有那么多的优点,但是有一个致命的缺点,就是当 Redis 的主节点宕机之后,必须人工介入手动恢复,那么到特殊时间段,比如公司组织全体团建或者半夜突然发生主节点宕机的问题,此时如果等待人工去处理就会很慢,这个时间是我们不允许的,并且我们还需要招聘专职的人来负责数据恢复的事,同时招聘的人还需要懂得相关的技术才能胜任这份工作。既然如此的麻烦,那有没有简单一点的解决方案,这个时候我们就需要用到 Redis 的哨兵模式了。
Redis 哨兵模式就是用来监视 Redis 主从服务器的,当 Redis 的主从服务器发生故障之后,Redis 哨兵提供了自动容灾修复的功能.Redis 哨兵模块存储在 Redis 的 src/redis-sentinel
目录.
我们可以使用命令./src/redis-sentinel sentinel.conf
来启动哨兵功能。
有了哨兵功能之后,就再也不怕 Redis 主从服务器宕机了。哨兵的工作原理是每个哨兵会以每秒钟 1 次的频率,向已知的主服务器和从服务器,发送一个 PING 命令。如果最后一次有效回复 PING 命令的时间,超过了配置的最大下线时间(Down-After-Milliseconds)时,默认是 30s
,那么这个实例会被哨兵标记为主观下线。
如果一个主服务器被标记为主观下线,那么正在监视这个主服务器的所有哨兵节点,要以每秒 1 次的频率确认主服务器是否进入了主观下线的状态。如果有足够数量(quorum 配置值)的哨兵证实该主服务器为主观下线,那么这个主服务器被标记为客观下线。此时所有的哨兵会按照规则(协商)自动选出新的主节点服务器,并自动完成主服务器的自动切换功能,而整个过程都是无须人工干预的。
1.16.4. Redis 集群
Redis 集群也就是 Redis Cluster,它是 Redis 3.0 版本推出的 Redis 集群方案,将数据分布在不同的主服务器上,以此来降低系统对单主节点的依赖,并且可以大大提高 Redis 服务的读写性能。Redis 集群除了拥有主从模式 + 哨兵模式的所有功能之外,还提供了多个主从节点的集群功能,实现了真正意义上的分布式集群服务.
Redis 集群可以实现数据分片服务,也就是说在 Redis 集群中有 16384 个槽位用来存储所有的数据,当我们有 N 个主节点时,可以把 16384 个槽位平均分配到 N 台主服务器上。当有键值存储时,Redis 会使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位,再把此键值存储在对应的服务器上,读取操作也是同样的道理,这样我们就实现了数据分片的功能。
Redis脑裂
某个master所在机器突然脱离了正常的网络,跟其他slave机器不能连接,但是实际上master还运行着,此时哨兵可能就会认为master宕机了,然后开启选举,将其他slave切换成了master。这个时候,集群里就会有两个master,也就是所谓的脑裂。此时虽然某个slave被切换成了master,但是可能client还没来得及切换到新的master,还继续写向旧master的数据可能也丢失了。因此旧master再次恢复的时候,会被作为一个slave挂到新的master上去,自己的数据会清空,重新从新的master复制数据。 Redis