大魔王你好,我看你简历写了精通redis,我们来聊下redis吧(这小子年纪不大,口气倒不小,还敢写精通)
为什么要选择redis作项目的缓存呢?
因为大家都这么用啊。(这玩意心里知道就行,可千万不能说出来)
咳咳,出门左转,下一位。
正确姿势:(抬眼镜,清嗓子)面试官你好,随着用户越来越多,项目各种活动的请求高峰,传统的关系型数据库已经无法满足业务需求,若不加上数据的缓存,很容易把数据库打崩。对比了业界中的缓存中间件,最终选择了redis做为项目缓存。
你们对比了业界中哪些中间件,以及为什么最后选择了redis?
对比了redis与memcached,主要是以下几方面:
1:数据结构
redis对比memcached,redis支持更多数据结构,能支持更丰富的数据操作
2:集群模式
redis原生就支持集群模式,可以支持更多的数据缓存
mencached没有原生的集群模式,需要业务自己处理
3:性能
redis单核,存储小数据性能高于memcached
mencached多核,存储大数据性能高于redis
综上所述,redis更适合我们的项目作为缓存中间件
介绍下redis的数据结构及应用场景吧
1:字符串String:最常用的
2:字典Hash:如java里面的map
3:列表List:如:粉丝列表
4:去重集合Set:如朋友圈的共同好友
5:有序列表Sort Set::如排行榜(排序,跳表)
以上五种常用的数据结构一个不能少说,不然面试官以为你只会简单的set,get操作。
除以上五种基本数据结构,还有:Bitmaps、HyperLogLogs、Streams
多个系统并发操作redis会产生什么问题?
假如A、B、C三个系统分别去操作redis的同一个Key,本来顺序是A,B,C,但是系统A网络突然抖动了一下,导致B,C在A前面操作了redis,这就出bug了。
解决:CAS(compare-and-swap)在每次要写之前,先判断当前 Value 的时间戳是否比缓存里的 Value 的时间戳要新。如果是的话,那么可以继续操作,否则,就不能用旧的数据覆盖新的数据。
怎么保证缓存数据与数据库数据一致性?
方案1:先更新数据库,再更新缓存
方案2:先更新缓存,再更新数据库
结论:无论采取哪种,都无法保持数据一致性
有效解决方案:
1.更新数据库后,删除缓存
2.X秒后再删除一次,双重删除
3.设置缓存过期时间
4.删除缓存失败时记录日志,脚本定时修复
redis的过期key删除和淘汰策略了解吗?
过期key删除:
过期key:redis为key设置一个过期时间,当key到达过期时间后,不会删除,而是存在过期字典中
解决方案:
1:定时删除:设置key的过期时间同时,也为该key设置一个timer,当到达过期时间时,timer自动删除key
缺点:随着key的增加,timer也在增加,造成cpu压力大
2:惰性删除:当key到达过期时间时,不进行删除。等到下一次查询时,再判断key是否还有效,若已过期,则对key进行删除
缺点:当有大量key过期时,而又没有业务进行访问,这些key依然存在内存中,浪费内存
3:定期删除:每隔时间对数据库进行一次检查,删除过期的key,至于删除多少key,检查多少数据库则由算法决定
默认算法:每隔100ms进行一次随机抽查
缺点:难以确定删除操作执行的时长和频率,因为全部检查可能导致服务器卡顿
redis结合定时删除和惰性删除一起使用!!!
以上策略都不能完全删除过期key,随着时间推移,内存中过期key数量越来越多,导致不必要的内存消耗越来越多,所以redis提供淘汰机制。
淘汰机制:默认为没配置
配置:在redis.conf中配置maxmemory属性
淘汰策略:
volatile-lru:在设定有效期的key中使用lru算法,淘汰上次使用时间最早且次数最少的key
allkey-lru:在所有的key中使用lru算法,淘汰上次使用时间最早且次数最少的key
volatile-lfu:在设置有效期的key中使用lfu算法,淘汰使用次数最少的
allkey-lfu:在所有的key中使用lfu算法,淘汰使用次数最少的
volatile-random:在设置有效期的key中,随机淘汰key
allkey-random:在所有的key中在设置有效期的key中,随机淘汰key
volatile-ttl:淘汰剩余有效期最短的
noecition:不删除数据,根据引用计时器进行释放。若内存不够会直接报错
总结:
1:过期key删除强调对过期key的操作,如果key过期了,而内存还足够,不会使用内存淘汰策略,会使用删除策略进行删除
2:淘汰策略强调对内存的操作,如果内存不够,即使key没过期,也要删除一部分,也针对未设置过期时间的key
3:过期key删除结合定时删除和惰性删除一起配置使用
4:淘汰策略结合实际业务进行配置
redis持久化机制知道吗?
redis数据持久化依靠:RDB和AOF
RDB和AOF都可在redis.conf进行配置
流程图:
RDB:Redis DataBase
默认为开启状态
文件名:dump.rdb(二进制压缩存储)
配置语法:save <seconds> <change>
redis重启后如没有开启AOF,则会创建子进程在src/dump.rdb文件来恢复数据
优点:
适合大规模数据恢复,且恢复速度快
适用于对数据完整性和一致性要求不高的业务
缺点:
数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机,从而数据丢失
备份时占用内存。备份时会创建子进程,将数据写入临时文件中,最后将临时文件替换之前的备份文件,当数据较大时,可能造成redis短暂暂停,卡顿
AOF:Apeend only file
默认为关闭状态
开启配置:redis.conf中将appendonly no 设置为yes
文件名:appendonly.aof
AOF同步写操作数据至文件三种策略:
appendsync always:写了就同步
appendsync everysec: 每秒同步一次 <默认>
appendsync no :不同步
优点:数据的完整性和一致性高
缺点:数据量大,恢复慢
说说缓存中经常出现的缓存问题及解决方案
缓存雪崩解决方案:
事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
缓存穿透
1.定义:查询到数据不存在缓存和数据库中。
2.预防方案:在查询缓存前加过滤器,如 布隆算法
解决方案1:
每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 set -999 UNKNOWN 。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
解决方案2
:布隆算法 BloomFilter
1.定义:一组二进制向量和一系列随机映射函数。
2.优点:时间和空间查询远超其他算法
3.缺点:存在误识别率——hash算法
删除困难——定义好之后,数据库的数据改动
4.解决方案:扩大二进制向量的长度
增加随机映射函数的个数
5.如图:
6.查询:找出X的位置,若有0,则不存在,否则,可能存在
缓存击穿
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库。
不同场景下的解决方式可如下:
若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。
面试官:今天先到这吧,看来你小子对redis是挺熟悉,等下次复试再来点干货来接待你,希望你也能像今天这样。
大魔王:(卧槽,还不给offer,沙包大的拳头你见过没啊?!!!)好的,面试官,我们下次再见
,HR小姐姐先别走,加个微信。