Appearance
性能优化
1. Pipeline
原理
批量执行命令,减少网络往返。
使用
python
# Redis-py
pipe = redis.pipeline()
pipe.get("key1")
pipe.get("key2")
pipe.get("key3")
result = pipe.execute()对比
| 方式 | N次操作耗时 |
|---|---|
| 普通 | N * RTT |
| Pipeline | 1 * RTT |
优点
| 优点 | 说明 |
|---|---|
| 减少RTT | N条命令只需要1次网络往返 |
| 提高吞吐量 | 批量执行,QPS大幅提升 |
| 简单易用 | 只需封装执行,不需要改服务 |
缺点
| 缺点 | 说明 |
|---|---|
| 不保证原子性 | 与MULTI/EXEC不同,命令逐条执行 |
| 内存占用 | 服务端需要缓存所有响应 |
| 错误处理复杂 | 单条命令失败不影响其他 |
| 无回滚 | 中间失败无法撤销 |
注意事项
1. 命令数量控制
python
# ❌ 太多命令在一次pipeline中
pipe = redis.pipeline()
for key in keys: # 10万条
pipe.get(key)
results = pipe.execute() # 内存爆炸!
# ✅ 分批执行
batch_size = 5000
for i in range(0, len(keys), batch_size):
pipe = redis.pipeline()
for key in keys[i:i+batch_size]:
pipe.get(key)
pipe.execute()2. 大key问题
python
# ❌ pipeline中包含大key
pipe.get("big_key_100mb") # 响应很大
# ✅ 先拆分检查
if redis.memory_usage(key) > 10000: # 太大就分开
# 单独处理3. 错误处理
python
# ✅ 正确处理错误
pipe = redis.pipeline()
pipe.set("k1", "v1")
pipe.get("k1")
pipe.incr("k2") # k2不是数字,会失败
results = pipe.execute()
for i, result in enumerate(results):
if isinstance(result, Exception):
print(f"命令{i}失败: {result}")4. 事务命令不能用Pipeline
python
# ❌ 这样没有原子性
pipe = redis.pipeline()
pipe.multi()
pipe.incr("key")
pipe.exec() # 返回的是结果列表,不是事务
# ✅ 用execute的transaction参数
pipe = redis.pipeline(transaction=True)
pipe.multi()
pipe.incr("key")
pipe.execute() # 真正的事务适用场景
| 场景 | 推荐 | 不推荐 |
|---|---|---|
| 批量读取 | ✅ GET批量 | - |
| 批量写入 | ✅ MSET | - |
| 计数器 | ✅ INCR批量 | - |
| 事务 | ❌ 用MULTI | 事务性操作 |
| 大key | ❌ | 包含大value |
2. Lua脚本
原子执行
lua
-- 一次网络往返执行多条命令
local m = redis.call('get', KEYS[1])
if m then
redis.call('incr', KEYS[1])
end
return m优点
- 减少网络往返
- 原子执行
- 可复用
3. 集群分片
客户端分片
python
# CRC16取模分配
slot = CRC16(key) % 16384代理分片
客户端 -> Proxy(Redis Cluster/Sentinel) -> Redis4. 其他优化
1. 连接池
python
pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=10)
r = redis.Redis(connection_pool=pool)2. 压缩keys
# 不用
user:1000000001:profile:name
# 用
u:1:p:n3. 选择数据结构
- String vs Hash:字段多时用Hash
- Set vs List:需要去重用Set
面试考点
Q: Pipeline vs Lua?
- Pipeline:简单批量
- Lua:复杂逻辑+原子
Q: 集群分片方式?
- 客户端分片:Codis
- 代理分片:Twemproxy
- 服务端分片:Redis Cluster
