7.1 KiB
- redis 为什么这么快
- 基于内存来存储数据的,所以读写访问速度可以做到特别快
- 单线程的,可以避免多线程上下文切换带来的开销,同时加入了多路 IO 复用
- 内置了优化过的数据类型,性能高
-
分布式缓存常见的技术选型方案
-
为什么使用 redis/缓存
- 高性能,频繁访问的数据,访问数据更快
- 高并发,加大 QPS 访问量
- redis 的应用场景
- 分布式锁
- 限流,通过 redis 加 Lua 脚本实现,令牌桶
- 消息队列
- 延时队列
- 分布式 Session
- 复杂业务场景,通过 bitmap 统计用户活跃量,通过或 soted set 实现排行榜
- 常见缓存读写分类策略
- 如何实现分布式锁
set NX key value,del NX key value
- redis 数据结构
- String
- List
- Set
- Zset
- Hash
- HyperLogLogs
- Bitmap
- GeoSptial
- String 的应用场景
String 是一种二进制安全的数据结构,可以用来存储任何类型数据,字符串,浮点数,图片(base 64 或地址),序列化
- String 还是 Hash 存储对象更好
String 性能上比 Hash 节省内存,并且在存储多层嵌套的整个对象通过序列化也很方便,Hash 则是可以单独修改或者添加部分字段信息,如果是经常修改对象中的部分信息,就使用 Hash,否则用String
- String 的底层实现
C 语言字符串是以\0 结尾的字符数组,但是 redis 自己编写了 SDC(简单动态字符串)作为底层实现
- 购物车使用 String 还是 Hash
使用 Hash,用户修改时就只是修改内部的 key 和 value 值即可
- 如何实现排行榜
使用 Zset 实现
- Set 的使用场景
存储不能重复的数据,例如网站的 UV 统计,点赞的统计 计算交集并集差集,共同好友 需要随机获取数据中的元素的场景,抽奖,随机点名
- 使用 Set 实现抽奖系统
添加元素,随机移除并获取其中一个(不重复),随机获取一个元素(可重复)
- 使用 Bitmap 统计活跃用户
将用户 ID 作为 offset,如果用户活跃过,就将该 bit 位设为 1
- 使用 HyperLogLog 统计页面 UV
添加一个或多个元素到 HyperLogLog 中, 获取一个或多个 HyperLogLog 的唯一计数
-
Redis 线程模型
Redis 在 6.0 引入多线程来提供网络请求处理 IO 的性能,Redis 中的事件处理模型基于 Reacotor 模型,对应其中的文件时间处理器,这个是单线程运行的,Redis 利用 IO 多路复用程序来监听来自客户端的大量链接,无需创建多余的线程来监听,降低资源消耗
- Redis 6.0 之前为什么不使用多线程,6.0 为何应用多线程
为了提高网络 IO 的读写性能,Redis 瓶颈主要在于内存和网络
- Redis 后台线程
三个后台线程:
close_fileaof_fsynclazy_free
- Rdis 缓存过期时间,如何判断数据是否过期,过期数据删除策略
redis 内存有限,因此要给缓存设置一定的过期时间,防止 OOM,同时可以实现某些需要一定时间后过期的业务, 通过一个过期字典(一个 hash 表)来保存数据过期的时间,过期字典的 key 指向 redis 中的 key,value 是一个 long 类型的整数,存储时间戳
过期数据删除策略
- 惰性删除:只有在数据取出时判断是否过期,对 cpu 友好
- 定期删除:定期取出一批的 key 删除过期的 key,通过限制删除操作执行时长和频率来减少对 CPU 的影响,对内存友好
redis 使用的是定期删除+惰性删除的方式
- 内存淘汰机制
- volatile-lru:从已设置过期时间的数据集中挑选最少使用的进行删除
- volatile-ttl:从已设置过期时间的数据集中挑选将要过期的删除
- volatile-random:从已设置过期时间的数据集中随机选择数据删除
- allkeys-lru:内存不足时,移除最近最少使用的 key
- allkeys-random:从数据中随机选择数据淘汰
- no-eviction:禁止删除,新写入的操作报错
- volatile-lfu:从已设置过期时间的数据集中挑选最不经常使用的数据删除
- allkeys-lfu:从键空间中移除最不经常使用的数据
- 什么是 Rdis 事务
redis 事务将多个命令打包后顺序执行,通过 MULTI EXEC DISCARD WATCH 命令实现
- Redis 事务支持原子性吗,支持持久性吗
不支持,运行错误时也不支持回滚,除了出现错误的命令其他命令都会执行,无法保证持久性
- 如何解决 Redis 事务缺陷
利用 Lua 脚本来批量执行,一段 Lua 脚本可以看成一条命令,执行过程中不会有其他命令执行,但是出错的命令之后的命令也不会执行
- Redis 性能优化
- 使用批量操作减少网络传输
一个 redis 命令执行分为以下四部
- 发送命令
- 命令排队
- 命令执行
- 返回结果 1 和 4 的耗时为 RTT,就时网络延时,通过批量操作可以减少网络传输次数,减少网络开销
- pipeline
MGET,HMGET,SADD,原生支持批量操作 - 通过pipeline
- Lua 脚本
- 使用批量操作减少网络传输
一个 redis 命令执行分为以下四部
- 大量 key 集中过期问题
- 给 key 设置随机过期时间
- 设置 lazy-free(惰性删除/延迟释放)
- bigkey 分析及处理
String 类型超过 10kb,复合类型超过 5000 个元素, 使用 redis 的--bigkey 获取,借助开源工具分析,借助公有云的 Redis 分析服务
- 分割 BigKey
- 手动清理
- 采用合适的数据结构(HyperLogLog 统计页面 UV)
- 开启 lazy-free
- hotkey 危害及处理
频繁访问的 key,
使用--hotkey,使用 MONITOR,使用开源项目京东 hotkey,根据业务提前预估,业务代码中记录,借助公有云分析
- 读写分离
- 使用 Redis Cluster
- 二级缓存,将 hotkey 存放一份到 JVM 本地内存中(使用Caffeine)
- 慢查询命令
设置慢查询时间,通过查询慢查询日志获取
- 内存碎片
不可用的内存空间
- redis 存储数据时申请的空间大于实际需要空间
- 频繁修改 redis 中的数据
通过 redis 的内存整理功能清理碎片
-
生产问题
-
缓存穿透
请求的 key 时不存在于缓存,也不存在于数据库中,从而大量请求到数据库中查找,
- 缓存无效 key
- 布隆过滤器
-
缓存击穿
请求热点数据,但是热点 key过期,请求大量到数据库, - 设置热点key永不过期 - 热点数据提前预热 - 请求数据到达数据库前获取互斥锁,保证只有一个请求到达数据库-
缓存雪崩
缓存在同一时间大量失效,大量请求到达数据库,或者是服务器宕机 - 设置不同失效时间,随机失效 - 缓存永不失效 - 二级缓存 - 采用 redis 集群 - 限流
-
-
如何保证缓存数据和数据库一致性
旁路缓存模式 更新完 DB 后直接删除cache
-
为什么要使用分布式缓存
-
常见的缓存更新策略
-
如何实现自动化故障转移
-
缓存数据量太大怎么办