Redis 基础
- 开源、基于内存、支持持久化的高性能 Key-Value 数据库
- 单线程处理命令(6.x 之后引入 I/O 多线程,但核心命令执行仍是单线程)
- 典型 QPS:10w~50w+
- 缓存(热点数据)
- 排行榜(zset)
- 分布式锁(set nx ex)
- 限流(incr + expire / Lua)
- 会话管理(token/session)
- 消息队列(Stream/List)
- 计数器(incr)
- 地理位置(GEO)
适用于:计数器、缓存、分布式锁
常用命令:GET/SET/INCR/INCRBY/SETEX/MSET
最佳实践:
- 计数用 INCR,不用 GET+SET(避免并发问题)
- 使用 SET key value NX EX 10 实现分布式锁(RedLock 不推荐)
适用于:对象存储(例如用户信息)
最佳实践:
- 字段不要太多(>5000 会影响性能)
- 避免大 Key
- 可使用 HINCRBY 保存数值属性
适用于:消息队列、时间顺序数据
常用命令:LPUSH / RPOP / BLPOP
最佳实践:
- 不要做大列表,超过 50 万就危险
- 如果要做队列推荐使用 Stream
适用于:去重、标签、共同好友等运算
最佳实践:
- 大集合运算(如 SINTER)非常耗时,会造成阻塞
- 尽量用 SCARD 判断大小,避免 SMEMBERS 拉全量
适用于:排行榜、延迟队列
最佳实践:
- ZREMRANGEBYSCORE 用于清理过期任务
- 延迟队列:ZADD + ZRANGEBYSCORE + Lua
适用于:独立访客 UV、去重计数
误差:0.81% 左右
内存消耗固定:12KB
适用于:签到、活跃用户统计、是否在线等
节省内存,但不适合存储大量稀疏数据
特性:
- 有消费者组
- 持久化消息队列
- 支持 ACK
比 List 更适合消息系统。
优点:恢复快、占用空间小
缺点:可能丢失最近一次快照后的数据
最佳实践:
- 使用
save ""禁用相对持久化规则 - 使用
save 900 1 300 10 60 10000
优点:几乎不丢数据
缺点:文件越来越大
推荐模式:appendfsync everysec
AOF 重写(rewrite)最佳实践:
- 启用 auto-aof-rewrite-percentage 100
- 启用 auto-aof-rewrite-min-size 64mb
maxmemory-policy 推荐使用:
- volatile-lru(只淘汰有 ttl 的 key)
- allkeys-lru(作为缓存使用时)
不要使用:
- noeviction(容易导致直接报错)
危险的大 Key 场景:
- 一个 Hash 中几十万字段
- 巨大 List、Set、ZSet
危害:
- 删除大 Key 时可能阻塞 Redis
- 网络传输放大
解决方案:
- 用 SCAN 避免 KEYS
- 分拆大 key(Hash 分片)
- UNLINK 异步删除
生产常用架构:
- Redis Cluster(3主3从)
- Sentinel(主从)
原因:
- 使用 KEYS、HGETALL、SMEMBERS 等全量命令
- Lua 脚本耗时太久
- 大 key
解决:
- KEYS 改 SCAN
- 大 key 分片
- 使用 slowlog get 分析慢查询
- 使用 redis-cli –bigkeys 检查大 key
原因:
- Key value 太大
- 没设置过期
- 没启动淘汰策略
解决:
- 给缓存数据都加 TTL
- 设置 maxmemory 和 maxmemory-policy
原因:
- 复制阻塞
- 网络带宽不足
- Master 写入太多
解决:
- 使用 cluster 分片减轻压力
- 限流写流量
- 开启 repl-backlog-size
推荐使用 SET key value NX PX 30000
注意:
- value 必须是唯一值,用来比对锁是不是自己的
- 释放锁时必须使用 Lua 脚本保证原子性
示例 Lua 脚本:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
经典:
INCR key
EXPIRE key 60
更精准
高性能且精准
在 redis.conf 设置:
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command KEYS ""
rename-command SHUTDOWN ""