前言
其中包含了Redis篇、数据库篇、框架篇、微服务篇等题
Redis 篇
redis使用场景
其中 缓存、分布式锁是比较常见的。
其他的还有 redis集群、redis事务
缓存
- 缓存穿透:请求不存在的 key
- 缓存击穿:Redis 中 key 过期,请求数据库
- 缓存雪崩:大量 key 过期或 Redis 宕机
缓存穿透
正常使用 redis 的情况
缓存穿透:查询一个不存在的数据,myqsl查询不到数据也不会直接写入缓存,就会导致每次请求都会查询数据库。
解决方案一:缓存空数据,查询返回的空数据,让把这个空数据写入缓存。
优点:实现简单
缺点:大量数据都为空,会消耗内存,可能发生不一致问题(本来查询的数据为空,存入缓存,后来添加了该数据,但是缓存仍然为空)。
解决方案二:布隆过滤器
优点:内存占用较少,没有多余key
缺点:实现复杂,存在误判
缓存击穿
缓存击穿:给某个 key 设置了过期时间,当 key 过期的时候,恰好这时间点对这个 key 有大量的并发请求,这些请求可能会瞬间把 DB 压垮
解决方案一:互斥锁(分布式锁)
特点:强一致性,但是性能较差(需要等待互斥锁)
解决方案二:逻辑过期
特点:高可用,性能较优(不需要等待互斥锁)
缓存雪崩
缓存雪崩:在同一时间段大量的缓存key同时失效或者 Redis 服务宕机,导致大量请求到达数据库,给数据库带来巨大压力。
解决方案:
- 给不同的 key 的 TTL(过期时间)添加随机值
- 利用 Redis 集群提高服务的可用性(Redis宕机情况) 哨兵模式、集群模式
- 给缓存业务添加降级限流策略(可作为系统的保底策略,适用于穿透、击穿、雪崩) ngxin或者spring cloud gateway
- 给业务添加多级缓存
一道面试题
面试官: 什么是缓存雪崩 ?怎么解决 ?
答:
缓存雪崩意思是设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到数据库,数据库 瞬时压力过重雪崩。与缓存击穿的区别: 雪崩是很多key,击穿是某一个key缓存。
解决方案主要是可以将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
双写一致性
双写一致性:缓存 和 数据库 保持数据一致
根据业务场景可分为:要求高一致性 和 允许延迟一致性
读写数据流程
-
读数据:查询缓存,命中,直接返回;未命中,查数据库,写入缓存,设置超时时间
-
写数据:延迟双删(极大降低脏数据风险,但仍可能出现脏数据)
删除缓存 ---> 修改数据库 ---(延时)---> 删除缓存
延时的目的:为了等待主数据库往从数据库同步数据
保持强一致性:使用读写锁,读加共享锁,写加排他锁。当然性能必然会下降
允许短暂不一致
-
异步通知保持最终一致性
-
基于 Canal 的异步通知
一道面试题
面试官: redis做为缓存,mysql的数据如何与redis进行同步呢? (双写一致性)
答:我最近做的这个项目,里面有xxxx (根据自己的简历上写)的功能,数据同步可以有一定的延时(符合大部分业务) 我们当时采用的阿里的canal组件实现数据同步:不需要更改业务代码,部署一个canal服务。canal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,canal会读取binlog数据,然后在通过canal的客户端获取到数据,更新缓存即可。
持久化
- RDB
- AOF
RDB 全称 Redis Database Backup file(Redis数据备份文件),也叫数据库备份快照。就是把内存中的所有数据记录到磁盘中,当 Redis 实例故障重启后,从磁盘读取快照文件,回复数据。
主动备份
[root@localhost ~]# redis-cli
127.0.0.1:6379>save # 由Redis主线程执行RDB,会阻塞所有命令
ok
127.0.0.1:6379>bgsave # 开启子线程执行RDB,避免主线程受到干扰
Background saving started
被动备份,Redis 内部有出发 RDB 的机制,可以在 redis.conf 文件中找到,格式如下
# 900秒内,至少有一个 key 被修改,则执行bgsave
save 900 1
save 300 10
save 600 10000
RDB的执行原理:以后补充
AOF 全称 Append Only File(追加文件)。Redis 处理的每一个命令都会记录在 AOF 文件,可看作是命令日志文件。
AOF 默认是关闭的,需要在 redis.conf 配置文件中开启;
# 是否开启 AOF,默认为 no
appendonly yes
# AOF的文件名称
appendfilename "appendonly.aof"
AOF 的命令记录的频率,也可以通过 redis.conf 文件配置;
# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always
# 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案
appendfsync everysec
# 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
| 配置项 | 刷盘时机 | 优点 | 缺点 |
|---|---|---|---|
| Always | 同步刷盘 | 可靠性高,几乎不丢失数据 | 性能影响大 |
| everysec | 每秒刷盘 | 性能适中 | 最多丢失1秒数据 |
| no | 操作系统控制 | 性能最好 | 可靠性较差,可能丢失大量数据 |
对比
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用
| RDB | AOF | |
|---|---|---|
| 持久化方式 | 定时对整个内存做快照 | 记录每一次执行的命令 |
| 数据完整性 | 不完整,两次备份之间会丢失 | 相对完整,取决于刷盘策略 |
| 文件大小 | 会有压缩,文件体积小 | 记录命令,文件体积很大 |
| 宕机恢复速度 | 很快 | 慢 |
| 数据恢复优先级 | 低,因为数据完整性不如AOF | 高,因为数据完整性更高 |
| 系统资源占用 | 高,大量CPU和内存消耗 | 低,主要是磁盘IO资源。但AOF重写时会占用大量CPU和内存资源 |
| 使用场景 | 可以容忍数分钟的数据丢失,追求更快的启动速度 | 对数据安全性要求较高常见 |
一道面试题
面试官:redis做为缓存,数据的持久化是怎么做的?
答:在Redis中提供了两种数据持久化的方式: 1、RDB 2、AOF
面试官:这两种持久化方式有什么区别呢?
答:RDB是一个快照文件,它是把redis内存存储的数据写到磁盘上,当redis实例宕机恢复数据的时候,方便从RDB的快照文件中恢复数据。 AOF的含义是追加文件,当redis操作写命令的时候,都会存储这个文件中,当redis实例宕机恢复数据的时候,会从这个文件中再次执行一遍命令来恢复数据
面试官:这两种方式,哪种恢复的比较快呢?
答:RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是它有可能会丢数据,我们通常在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一些,但是它丢数据的风险要小很多,在AOF文件中可以设置刷盘策略,当时设置的就是每秒批量写入一次命令
数据过期策略
-
惰性删除策略 惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key
优点:对CPU友好,只会在使用该key时才会进行过期检查,对于很多用不到的key不用浪费时间进行过期检查
缺点:对内存不友好,如果一个key已经过期,但是一直没有使用,那么该kev就会一直存在内存中,内存永远不会释放
-
定期删除策略
定期删除:每隔一段时间,我们就对一些key进行检查,删除里面过期的key(从一定数量的数据库中取出一定数量的随机key进行检查,并删除其中的过期key)。
定期清理有两种模式:
- SLOW模式是定时任务,执行频率默认为10hz,每次不超过25ms,以通过修改配置文件redis.conf 的hz 选项来调 整这个次数
- FAST模式执行频率不固定,但两次间隔不低于2ms,每次耗时不超过1ms
优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键 占用的内存。
缺点:难以确定删除操作执行的时长和频率
Redis的过期删除策略:惰性删除 + 定期删除两种策略进行配合使用