标题
此处填写本题目的摘要
# Redis是什么?简述它的优缺点?
Redis本质上是一个Key-Value类型的内存数据库,很像Memcached,整个数据库加载在内存当中操作,定期通过异步操作把数据库中的数据flush到硬盘上进行保存。
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value 数据库。
优点:
- 读写性能极高, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务, Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 数据结构丰富,除了支持string类型的value外,还支持hash、set、zset、list等数据结构。
- 高可用性,支持主从、集群、哨兵等多种模式。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等特性。
缺点:
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
- 主从同步数据一致性问题难保证,主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
# Redis事务
# Redis持久化
为了能够重用Redis数据,或者防止系统故障,我们需要将Redis中的数据写入到磁盘空间中,即持久化。
Redis提供了两种不同的持久化方法可以将数据存储在磁盘中,一种叫快照RDB
,另一种叫只追加文件AOF
。
# RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘(Snapshot
),它恢复时是将快照文件直接读到内存里。
优势:适合大规模的数据恢复;对数据完整性和一致性要求不高
劣势:在一定间隔时间做一次备份,所以如果Redis意外down
掉的话,就会丢失最后一次快照后的所有修改。
# AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时, Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.。
优势
- 每修改同步:
appendfsync always
同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好 - 每秒同步:
appendfsync everysec
异步操作,每秒记录,如果一秒内宕机,有数据丢失 - 不同步:
appendfsync no
从不同步
劣势
- 相同数据集的数据而言
aof
文件要远大于rdb
文件,恢复速度慢于rdb
aof
运行效率要慢于rdb
,每秒同步策略效率较好,不同步效率和rdb
相同
# RDB+AOF
Redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。
简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。
优势:混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
劣势:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。
# Redis 6.0后的线程模型
# RedisVM机制处理磁盘和内存的交换数据
# Redis持久化数据和缓存怎么做扩容?
# Redis过期键的删除策略
Redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用。
- 惰性删除:惰性删除不会去主动删除数据,而是在访问数据的时候,再检查当前键值是否过期,如果过期则执行删除并返回 null 给客户端,如果没有过期则返回正常信息给客户端。它的优点是简单,不需要对过期的数据做额外的处理,只有在每次访问的时候才会检查键值是否过期,缺点是删除过期键不及时,造成了一定的空间浪费。
- 定期删除:Redis会周期性的随机测试一批设置了过期时间的key并进行处理。测试到的已过期的key将被删除。
# Redis内存淘汰策略
那在内存用完的时候,还继续往Redis里面添加数据不就没内存可用了吗?实际上Redis定义了几种策略用来处理这种情况:
- noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)
- allkeys-lru:从所有key中使用LRU算法进行淘汰
- volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰
- allkeys-random:从所有key中随机淘汰数据
- volatile-random:从设置了过期时间的key中随机淘汰
- volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰
# 缓存知识点
# 缓存穿透
缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。
解决方法:
- 将无效的key存放进Redis中:当出现Redis查不到数据,数据库也查不到数据的情况,我们就把这个key保存到Redis中,设置value="null",并设置其过期时间极短,后面再出现查询这个key的请求的时候,直接返回null,就不需要再查询数据库了。但这种处理方式是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。
- 使用布隆过滤器:如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个 key 存在,那么很大可能是存在(存在一定的误判率)。于是我们可以在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,不让其访问数据库,从而避免了对底层存储系统的查询压力。
# 缓存雪崩
如果在某一个时期出现了大批的缓存失效,大量的请求直接访问数据库,这回导致数据库的压力巨大,严重的情况可能会让数据库宕机。如果运维马上重启数据库,又会导致新的流量把数据库再次打挂。
造成缓存雪崩的情况一般有两种:
- Redis宕机
- 大量key采用相同的过期时间
解决方案可以按照事前、事中和事后三个维度去思考
事前预防(防止批量过期、防止Redis挂机):
- 随机过期时间:设置不同的过期时间,让缓存失效不会集中在某一段时间内。
- 分级缓存:设置多级缓存,在第一级缓存失效的基础上再访问第二级缓存,每一级缓存的失效时间都不一样,这也可以离散缓存的过期时间。
- 热点数据配置永不过期:可以不设置过期时间,或者开一个线程来给热点数据延长过期时间。
- 采用主从+哨兵、集群等模式保证Redis缓存高可用,避免Redis宕机情况。
事中处理(多个方面限流):
- 使用熔断机制,限流降级。即当访问数据库的流量达到一定阈值,可以直接返回「系统拥挤」等提示,防止过多的请求打垮数据库。
- 使用互斥锁、消息队列控制数据读写缓存的线程数据,在访问缓存层面上限流也可以在一定程度上保护数据库被高并发打挂。
事后恢复(快速恢复):
- Redis宕机了,使用Redis持久化机制,尽快恢复缓存数据。
# 缓存预热
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统中,避免用户对数据库的大量请求导致的高负载。一般有一下几种方案:
- 数据量不大:工程启动的时候,执行同步缓存加载
- 数据量大:工程启动的时候,设置定时任务脚本,进行缓存的异步加载
- 数据量太大:在工程启动之前,先将数据提前加载到缓存中
# 缓存降级
缓存降级是指缓存失效或者有高并发流量访问风险的时候,不让全部请求访问数据库,而是直接返回默认的数据(访问失败提醒)来减少对数据库系统崩溃的风险。