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