Java面试之redis

61 阅读4分钟

Redis

image.png

1.redis 常见数据类型,应用场景, 底层实现

String  微博数量   基于sds

Hash   存储的对象,例如商品信息  

List    微博粉丝列表

Set    共同关注    去重

Zset    弹幕消息   去重后有序

Bitmap:布隆过滤器(false is always false)

Hyperloglog:不精确的去重计数功能  - 统计网站月活

Geospatial:地理位置,附近的人,最优地图路径

2.redis事务

只有一致性和隔离性,

不具有原子性的原因:redis同一个事务中,如果一条命名执行失败,剩下的会继续执行,不会回滚

持久性:开启持久化模式(rdb和aof后)

Rdb:把当前内存数据以快照形式写入磁盘,实际操作是通过fork子进程执行,采用二进制压缩存储。  适合用来灾备  --缺点是快照保存完成之前宕机,这段时间数据将丢失,保存快照时可能导致服务短时间不可用

Aof: 以文本日志记录redis处理的每一个写入或删除操作,有追加模式,灵活的同步策略,支持每秒同步,每次修改同步和不同步,--缺点 效率低于rdb

**注:不管是rdb还是aof都无法完全保证redis数据不丢失!! **

3.缓存击穿,缓存穿透,缓存雪崩概念和解决方案?

1.缓存击穿:某个key失效时,有大量并发请求访问该key

解决方案:key的操作添加全局互斥锁

使用:在缓存失效时(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(Redis的SETNX)去set一个mutex key.当操作返回成功时,再load db的操作并回设缓存;否则,就重试整个get缓存的方法

2.缓存穿透:大量并发查询不存在的key,直接访问到数据库。

解决方案

1.缓存空值的KEY,这样第一次不存在也会被加载会记录,下次拿到有这个KEY

2.Bloom过滤或RoaringBitmap 判断KEY是否存在

3.缓存雪崩:缓存数据同一时刻大规模不可用或都更新

原因:

1.key的过期时间都设置一致

2.更新策略

3.数据热点

4.缓存服务宕机

解决方案

1.在原有失效时间基础上增加一个随机值,比如1~5分钟的随机,这样每个缓存的过期时

重复率就会降低,集体失效概率也会大大降低。

2.更新策略在时间上做到比较均匀

3.使用的热数据尽量分散到不同的机器上

4.1多台机器做主从复制或者多副本,实现高可用

4.2 实现熔断限流机制,对系统进行负载能力控制

4.mysql和redis  数据一致性解决?(*)

1.缓存延时双删  问题:第二步缓存删除失败会导致脏数据,而且阻塞时间不太好评估。影响吞吐

2.删除缓存重试机制  解决上面删除失败的问题 问题:业务代码入侵

3.读取binlog 异步删除缓存  问题:binlog延时,宕机?

  总结:

根本原因是因为cache和db不是原子性

要彻底解决的话 必须将cache和db的更新操作归在一个事务下。

根据实际场景来权衡落地方案

4.1.为什么不是更新缓存,而是删除缓存?

答:更新缓存的缺点

1.可能会造成数据一致性问题

2.如果缓存值计算复杂,然后更新频率高,会浪费性能

3.读少写多的场景, 数据还没读到又更新,会浪费性能。

实际上写多的场景 用缓存不划算。

4.2双写的情况下 为什么要先操作数据库而不是缓存

答:还是造成数据一致性问题 

5.redis 分布式锁

实现:redisson 。 继承lock ,所以使用和lock没啥区别

还是基于lua,后台会开启一个watch dog线程监听是否过期。

6.高可用 - 主从,哨兵,集群分析

 cluster集群:分片机制,内部分为16384个slot插槽,分布在所有master节点。数据写入按key做CRC16算法计算在哪个slot。数据冗余通过slave节点保障

 哨兵:- 必须至少3个实例保证健壮性。主从+哨兵 并不能保证数据不丢失,但是可以保证集群的高可用

 主从 - 主写从读,轻松实现水平扩容

7.淘汰策略

7.1 为什么需要淘汰?

不管是本地缓存还是分布式缓存,为了保证高性能,都是使用内存来存储数据,但是由于成本和内存限制,当数据超过缓存容量时,就需要对缓存的数据进行剔除。

7.2 常见的淘汰策略

fifo -淘汰最早数据 lru- 淘汰最近最少使用 lfu- 淘汰最近使用频率最低