Redis从零讲解
一、Redis 是什么(核心定位)
- Redis 是一个开源的内存数据结构存储(in-memory data structure store),既可做缓存,也可做消息队列、计数器、会话存储等。
- 支持丰富的数据类型(String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream、Geospatial),并提供原子操作、Lua 脚本、事务、发布/订阅等特性。
- 性能极高:单节点可承载几十万到数百万 ops/s(视硬件与命令而定),主要因为数据在内存中操作且实现简单高效。
二、核心数据结构与典型命令(重要操作复杂度)
- String:最基本,支持 GET/SET/INCR/DECR/INCRBY/APPEND;常用于缓存、计数器、短期令牌。
- Hash:键下的键值对集合,适合存储对象(用户信息),命令 HGET/HSET/HMGET/HDEL。节省内存(小字段合并)。
- List:双端链表,支持 LPUSH/RPUSH, LPOP/RPOP, BRPOP(阻塞),用于队列/任务调度。
- Set:无序集合,O(1) 添加/判断/移除,适用去重、标签集合。
- Sorted Set(ZSET):带分数的有序集合,支持排行榜、延迟队列(score=timestamp)等。常用命令 ZADD/ZRANGE/ZPOPMIN。
- Bitmap:位图计数,用于用户活跃、签到、统计等(位运算非常高效)。
- HyperLogLog:基数估算(基数去重计数)内存小,常用于 UV 估算。
- Stream:类似日志/消息流,支持 XADD、XREADGROUP,适合构建可靠消息队列/事件流。
- Geo:地理位置索引(GEOADD/GEORADIUS等)。
- 注意复杂度:Redis 文档为每个命令标注时间复杂度(如 O(1)、O(N)),设计时必须避免大量 O(N) 在大数据量上使用(比如 KEYS、SORT、SMEMBERS 全量操作)。
三、一致性与原子性
- 单个命令原子:Redis 保证单个命令在执行时是原子的(不会被打断)。
- 多个命令原子:可用 MULTI/EXEC 进行“事务”批量提交,但事务中命令仍会逐条执行且不回滚(失败后不会回滚前面命令)。
- Lua 脚本:通过 EVAL 可以把一组操作写成一个原子脚本,适合对库存扣减等需要原子性逻辑的场景(秒杀/令牌发放典型用法)。Lua 在单实例(或单个分片)内原子,全局性能好。
四、持久化(RDB 与 AOF)
- RDB(快照):按配置间隔生成快照文件(dump.rdb),对恢复速度快、空间压缩好,但存在数据丢失窗口(最近一次快照以来的数据丢失)。写操作会 fork 子进程来生成快照,可能造成内存峰值。
- AOF(追加日志):把写命令追加到 appendonly.aof,可配置 fsync 策略(everysec/always/no),默认 everysec 比较折中,恢复数据更完整但 AOF 文件更大,需要重写(BGREWRITEAOF)。
- 两者可同时开启:RDB 用于快速恢复,AOF 用于更完整数据。生产中根据 RTO/RPO 选择策略(低数据丢失要求用 AOF)。
- 恢复:优先用 AOF(若同时存在),AOF 文件损坏可通过 aof-rewrite 修复。
- 注意:持久化会影响性能,配置要平衡(fsync 策略、snapshot 周期等)。
五、复制与高可用
- 主从复制(master-replica):异步复制,主写从读,读扩展与备份。Replica 会通过 PSYNC 完成全量/部分同步。异步复制有数据延迟,主节点崩溃后可能丢失一小段写。
- Sentinel:用于自动故障转移(主从切换)和服务发现。Sentinel 监控节点健康并可触发选举。注意监控阈值与 split-brain 风险。
- Redis Cluster:原生分片方案,自动分片与故障转移。数据分片基于 16384 个 hash slot。Client 需支持 cluster 模式或使用���理。Cluster 的 Lua 脚本/事务不能跨 slot(keys 必须在同 slot,可用 hash tag {id} 强制同槽)。
- 高可用部署建议:使用 Cluster + replication + AOF,或使用 Sentinel + replication(单主单集群)。
六、扩展性(Scale)
- 水平扩展:Redis Cluster 对写读进行分片扩展,但跨分片操作受限(用 hash tag 解决局部相关键)。
- 纵向扩展:增加内存与 CPU,提升单实例能力。
- 读扩展:主写、从读,适合读多写少场景;要处理读延迟与复制延迟。
- 缓存穿透/击穿/雪崩策略:使用缓存空值、二级缓存、请求排队、互斥锁(分布式锁)等策略。
七、内存管理与驱逐策略
- Redis 存全量在内存,必须规划内存使用:设置 maxmemory 限制,配置 eviction policy。常见策略:noeviction、allkeys-lru、volatile-lru、allkeys-random、volatile-ttl 等。
- 推荐:对缓存场景启用 maxmemory + 适合的 eviction(一般 allkeys-lru 或 volatile-lru),并结合合适 TTL。
- 内存碎片:关注 used_memory_vs_used_memory_rss(mem_fragmentation_ratio),内存碎片会造成 RSS 与实际占用差异。建议使用 jemalloc 并监控 fragmentation。
- 小技巧:尽量避免单键过大(比如非常长的 list/hash/zset),使用适当的数据建模(hash 用于较多小字段合并存储)。
八、性能优化与高吞吐实践
- 使用 pipelining 批量发送命令减少 RTT。
- 使用连接池(JedisPool、Lettuce 的共享连接/异步模型)避免频繁建连。Lettuce 在高并发下更推荐(Netty、线程安全)。
- 避免阻塞命令(BLPOP、BRPOP 在阻塞数量大时影响线程资源)或确保消费者足够。Stream + consumer group 更适合高并发消息消费。
- 使用 Lua 脚本做原子操作(库存扣减 + 去重 + 入队)。
- 尽量避免 KEYS *、SMEMBERS/SDIFF 大集合操作,改用 scan + 分页/分片处理。
- 对于 sorted set / zrange 均衡使用分页,避免一次性读取大区间。
- 将热 key 分散(避免单点热点),增大并发吞吐能力。
- 使用 Redis Cluster 分散负载但注意 hash tag 与跨槽限制。
- 客户端层面的限流、降级、缓存预热与冷启动保护(缓存穿透)是必须的。
九、常见使用场景与设计模式
- 缓存:使用 TTL、缓存击穿保护(互斥锁、互斥缓存、布隆过滤器)和请求层限流。
- Session 存储:快速读写,session 过期交由 Redis TTL 管理。
- 分布式锁:经典 Redlock 或单实例 set key NX PX ms(set key value NX PX)。注意 Redlock 争议与使用限制。
- 计数器/限流:INCR/INCRBY 或 Leaky Bucket/Token Bucket 模型(Lua 脚本)用于限流。
- 排行榜:使用 sorted set。
- 延迟队列:sorted set score=timestamp,定时 pop 指定 score 条目。或使用 Stream + consumer group + pending entries 管理。
- 消息队列:List(RPUSH + BLPOP)或 Stream(XADD + XREADGROUP)。Stream 更可靠且能管理 ACK、Pending。
- 签到/活跃统计:Bitmap、HyperLogLog、Bitfield。
- 地理位置查询:GEOADD/GEORADIUS。
- 分布式会话共享、短期令牌、验证码/OTP 存储等。
十、安全与配置
- 认证:requirepass / ACL(Redis 6+ 支持更细粒度权限)。生产必须开启 ACL 并限制命令。
- 网络:绑定内网 IP(bind),不要暴露到公网;使用防火墙或 VPC。
- TLS:Redis 6 支持 TLS,建议生产开启传输加密。
- 禁用危险命令:rename-command CONFIG "" 或禁用 FLUSHALL、DEBUG 等。
- 监控与审计:记录慢查询、连接数、客户端 IP 等。
十一、运维常见问题与排查方法
- 快照/持久化导致卡顿:RDB fork 或 AOF rewrite 都会触发 IO 与 CPU 峰值,需监控并合理配置内存/IO。
- 内存飙升:检查大 key、内存碎片、客户端内存(client output buffer)。使用 redis-cli --bigkeys 或 redis-memory-for-key 工具调查。
- 哨兵/主从切换问题:确保 sentinel 配置合理、通知机制和客户端重连策略。
- Cluster slot imbalance:数据迁移需在线 reshard,避免热点节点过载。
- 慢日志:通过 SLOWLOG 获取慢命令,找出影响性能的命令。
- 连接过多:配置 maxclients,使用连接池与合理超时,避免连接泄露。
十二、监控指标(重点)
- 内存:used_memory、used_memory_rss、mem_fragmentation_ratio、used_memory_peak
- 操作:instantaneous_ops_per_sec、total_commands_processed、expired_keys、evicted_keys
- 连接:connected_clients、blocked_clients、maxclients、rejected_connections
- 持久化:rdb_last_bgsave_status、aof_rewrite_in_progress、rdb_bgsave_in_progress
- 复制/延迟:role、master_repl_offset、slave0_repl_offset、master_link_status、replication_lag(自定义测量)
- 网络:instantaneous_input_kbps、instantaneous_output_kbps
- slowlog 长度与详细条目
十三、备份、恢复与迁移
- 备份:拷贝 RDB/AOF 文件并保证一致性(在低峰时或使用复制 slave 再备份)。
- 恢复:把 RDB/AOF 放在目标目录,启动 Redis。AOF 可通过 redis-check-aof 修复。
- 在线迁移工具:redis-shake、redis-cli --cluster 或 Redis 的 MIGRATE/CLUSTER MEET/RESHARD 工具。
- 升级策略:先在从库升级,切换后再升级主库;避免 downtime。
十四、Redis 模块生态(增强功能)
- RedisJSON:JSON 文档存储与查询。
- RediSearch:全文检索与索引。
- RedisGraph:图数据处理。
- RedisBloom:布隆过滤器、Cuckoo Filter、TopK 等概率数据结构。
- RedisTimeSeries:时间序列处理。
使用模块时要注意 Cluster 与模块兼容性/部署要求。
十五、常见坑与实践建议总结
- 千万别把持久化当作数据唯一来源:Redis 可作为缓存或快速状态存储,重要业务数据需在数据库持久化(或用 AOF + 主从 + 对账)。
- 避免 KEYS *、LRANGE 大范围、SMEMBERS 等 O(N) 操作在大数据量上执行。
- 使用 Redis Cluster 时注意键的 hash slot,使用 hash tag 强制同槽。
- 持久化配置(RDB/AOF)对可用性和性能有影响,要做压测与权衡。
- 监控内存与慢日志,生产上使用专门监控(Prometheus + redis_exporter、Grafana)。
- 在高并发场景(如秒杀)推荐:入口用 Redis Lua 做原子预扣 + 写队列,消费者异步写 DB 并做幂等与补偿。
- 客户端选型:Lettuce(Netty、线程安全、推荐)或 Jedis(传统、连接池),避免不当连接管理(泄露)。
十六、简单示例:用 Lua 做原子库存扣减(秒杀)
Lua 脚本:
KEYS[1]=stockKey; KEYS[2]=userSetKey; KEYS[3]=queueKey
ARGV[1]=userId; ARGV[2]=payload
if redis.call('sismember', KEYS[2], ARGV[1]) == 1 then return {0,'duplicate'} end
local stock = tonumber(redis.call('get', KEYS[1]) or '0')
if stock <= 0 then return {-1,'soldout'} end
redis.call('decr', KEYS[1])
redis.call('sadd', KEYS[2], ARGV[1])
redis.call('rpush', KEYS[3], ARGV[2])
return {1,'success'}Java 调用:jedis.eval(script, keys, args);脚本在同一 shard(cluster)时可原子执行。