跳转到内容

sql

标签「sql」下的 1 篇文章

redis核心原理与实战应用

Redis 常被用作缓存、分布式锁、排行榜、计数器和会话存储。它的核心优势不是“能存数据”这么简单,而是用内存访问、丰富数据结构、持久化和高可用机制,支撑高并发场景下的读写性能与系统稳定性。

这篇文章按实际使用路径整理 Redis:先看基础能力,再看高可用与缓存问题,最后整理一致性、性能优化和典型场景。

Redis 支持多种常用数据结构:

  • String:适合缓存简单值、计数器、分布式锁标记。
  • Hash:适合存储对象字段,例如用户信息。
  • List:适合队列、消息列表、时间线等场景。
  • Set:适合去重、集合交并差。
  • Sorted Set(ZSet):适合排行榜、权重排序、延迟队列。

这些结构让 Redis 不只是一个 Key-Value 缓存,而是可以承接一部分高频读写业务逻辑。

Redis 主要有两种持久化方式:RDB 和 AOF。

RDB(快照)

  • 定期生成内存快照,例如通过 bgsave 命令。
  • 优点:文件体积较小,数据恢复速度快。
  • 缺点:如果 Redis 异常退出,可能丢失最后一次快照之后的数据。

AOF(追加日志)

  • 记录每一次写操作命令,例如 SET key value
  • 优点:数据安全性更高,可以配置更高频率的落盘。
  • 缺点:文件体积更大,恢复速度通常慢于 RDB。

实际生产中常见策略是 RDB + AOF 组合使用:RDB 用于快速恢复,AOF 用于降低数据丢失风险。

Redis 主从同步分为全量同步和增量同步。

全量同步

通常发生在从节点初次连接主节点,或断线时间过长无法增量追赶时:

  1. 主节点生成 RDB 文件。
  2. 主节点把 RDB 文件发送给从节点。
  3. 从节点清空旧数据并加载 RDB。
  4. 主节点继续把同步期间缓冲区中的写操作发送给从节点。

增量同步

当从节点短暂断线后重新连接,可以通过 psync 根据复制偏移量(offset)同步缺失的写操作,避免重新做一次全量同步。

Redis 常见高可用方案主要有两类:

  • 哨兵模式(Sentinel):监控主从节点状态,在主节点故障时自动完成故障转移。
  • 集群模式(Cluster):通过分片存储数据,提升容量上限,并支持水平扩展。

哨兵更偏向主从高可用,集群更偏向容量扩展和分片治理。

缓存击穿指的是一个热点 Key 在高并发访问时突然过期,大量请求同时打到数据库。

常见解决方案:

  • 热点 Key 不设置过期时间,通过后台任务主动刷新。
  • 互斥锁或分布式锁,只允许一个请求回源重建缓存。
  • 逻辑过期,缓存中保存过期时间,由后台异步刷新数据。

缓存穿透指的是大量请求访问不存在的数据,缓存无法命中,请求直接落到数据库。

常见解决方案:

  • 布隆过滤器:在请求进入缓存和数据库之前过滤明显不存在的 Key。
  • 缓存空对象:对不存在的数据短时间缓存空值,避免重复打到数据库。
  • 参数校验:拦截非法 ID、异常参数和明显无效请求。

缓存雪崩指的是大量 Key 在同一时间过期,导致请求集中打到数据库。

常见解决方案:

  • 随机过期时间,避免大量 Key 同时失效。
  • 限流降级,在数据库压力过高时保护核心服务。
  • 多级缓存或集群部署,减少单点缓存失效带来的冲击。

四、数据一致性与 Redis-MySQL 同步

Section titled “四、数据一致性与 Redis-MySQL 同步”

业务中常见的数据源组合是 MySQL 存储主数据,Redis 作为缓存。这里的核心问题是:数据库更新后,缓存如何保持一致。

常见方案:

  • 先写 MySQL,再更新 Redis:逻辑直接,但并发下可能出现旧值覆盖新值。
  • 先写 MySQL,再删除 Redis:更常见,后续请求回源数据库并重建缓存。
  • 延迟双删:写库后删除缓存,短暂延迟后再次删除,降低并发读写导致的脏缓存概率。
  • 最终一致性:通过消息队列或 Canal 监听 MySQL Binlog,把数据变更异步同步到 Redis。
  • 对实时一致性要求很高的场景,需要更严格的事务或强一致方案。
  • 对短暂延迟可接受的场景,通常使用缓存删除、消息队列、Binlog 同步等最终一致性方案。

缓存一致性没有绝对通用答案,重点是结合业务对“延迟、正确性、复杂度”的要求做取舍。

Redis 提供 slowlog 用于排查慢查询。常见优化方向:

  • 避免 KEYS *SORT、大范围 SUNION 等高复杂度命令。
  • 对批量操作使用 Pipeline,减少网络往返。
  • 控制单次命令处理的数据规模,避免阻塞主线程。

当内存达到 maxmemory 限制时,Redis 会根据配置的淘汰策略处理 Key。

常见策略:

  • noeviction:内存不足时拒绝写入。
  • volatile-lru:只在设置了过期时间的 Key 中淘汰最近最少使用的 Key。
  • volatile-lfu:只在设置了过期时间的 Key 中淘汰最近最不常用的 Key。
  • allkeys-lru:在所有 Key 中淘汰最近最少使用的 Key。
  • allkeys-lfu:在所有 Key 中淘汰最近最不常用的 Key。
  • allkeys-random:在所有 Key 中随机淘汰。

缓存场景通常更常见 allkeys-lruallkeys-lfu,但具体选择要看访问分布和业务容忍度。

BigKey 指单个 Key 占用过大内存或包含过多元素,会导致网络传输、删除、迁移和持久化变慢。

HotKey 指少数 Key 被高频访问,容易造成单点压力。

优化方向:

  • 拆分过大的 Hash、List、Set、ZSet。
  • 避免一次性读取或删除大 Key。
  • 对热点数据做本地缓存、多副本缓存或请求合并。

Redis 的命令执行主要是单线程模型,这让它避免了大量锁竞争,命令执行顺序也更容易理解。

Redis 6.0 之后引入多线程处理网络 I/O,但命令执行本身仍然保持单线程语义。因此,慢命令、大 Key 操作、复杂聚合仍然可能阻塞 Redis。

Redis 对过期 Key 主要使用两种删除策略:

惰性删除

  • 获取 Key 时检查是否过期,过期则删除。
  • 优点:不主动消耗额外资源。
  • 缺点:如果过期 Key 长时间不被访问,可能继续占用内存。

定期删除

  • Redis 定期随机抽样部分 Key,删除其中已经过期的 Key。
  • 优点:可以主动清理过期数据。
  • 缺点:不保证所有过期 Key 都会立刻被删除。

Redis 默认使用 惰性删除 + 定期删除 的组合策略。

Redis 变慢时,可以从以下方向排查:

  • 检查内存是否耗尽,是否发生 Swap,例如使用 free -m
  • 检查磁盘 I/O,尤其是 AOF 写入和重写期间。
  • 使用 SSD 替代传统磁盘,降低持久化带来的 I/O 压力。
  • 排查是否使用了 KEYSSORTSUNION 等高复杂度命令。
  • 检查是否存在大批量读取、大批量删除或大集合遍历。
  • 对批量请求使用 Pipeline,减少网络延迟。

AOF 重写期间可能与 fsync 竞争磁盘资源。可以根据场景配置:

no-appendfsync-on-rewrite yes

这个配置可以降低 AOF 重写期间的写入阻塞风险,但也会增加极端情况下的数据丢失窗口,需要结合业务容忍度评估。

如果数据库和 Redis 同时出现压力波动,需要排查是否存在:

  • 大量 Key 同时过期。
  • 热点 Key 突然失效。
  • 缓存重建逻辑没有互斥保护。
特性RedisMemcached
数据类型String、Hash、List、Set、ZSet 等主要支持简单 Key-Value
持久化支持 RDB、AOF不支持持久化
内存回收支持多种淘汰策略支持 LRU 等缓存淘汰机制
原子操作支持较丰富的原子操作支持有限原子操作
线程模型命令执行以单线程为主,Redis 6.0 后支持 I/O 多线程多线程网络模型
场景适用复杂缓存、计数、排行榜、分布式锁简单缓存、高吞吐 Key-Value

简单说:如果只是做非常简单的缓存,Memcached 也可以胜任;如果需要更丰富的数据结构、持久化、高可用和分布式能力,Redis 更适合。

Redis 常见业务场景包括:

  • 缓存:存储热点数据,减少数据库压力。
  • 会话保持:存储用户 Session 或登录态。
  • 排行榜:通过 ZSet 实现 Top N 榜单。
  • 限流降级:通过计数器、滑动窗口或令牌桶实现接口限流。
  • 分布式锁:使用 SET key value NX PX 等命令实现基础锁能力。
  • 延迟队列:通过 ZSet 分数存储执行时间,实现定时任务调度。

Redis 的核心价值在于 高性能访问、灵活数据结构、持久化能力和分布式支持

实际使用 Redis 时,需要重点关注四件事:

  1. 数据结构是否选对。
  2. 缓存异常场景是否有兜底方案。
  3. Redis 与数据库之间是否能接受最终一致性。
  4. 是否避免了慢命令、BigKey、HotKey 和集中过期。

理解这些核心机制后,Redis 就不只是一个缓存组件,而是高并发系统中非常重要的性能与稳定性基础设施。