Memcached读取命令 #
一、读取命令概述 #
1.1 读取命令列表 #
text
Memcached提供以下读取命令:
get - 获取数据
gets - 获取数据和CAS ID
1.2 命令格式 #
text
get命令格式:
get <key>*\r\n
gets命令格式:
gets <key>*\r\n
参数说明:
key - 键名,可以指定多个
响应格式:
VALUE <key> <flags> <bytes> [<cas unique>]\r\n
<data block>\r\n
END\r\n
二、get命令 #
2.1 命令说明 #
text
get命令:
- 获取指定键的数据
- 可以同时获取多个键
- 键不存在时不返回数据
语法:
get <key>*\r\n
响应:
VALUE <key> <flags> <bytes>\r\n
<data block>\r\n
END\r\n
2.2 获取单个数据 #
bash
# 存储数据
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
STORED
# 获取数据
get user:1001
VALUE user:1001 0 45
{"id":1001,"name":"John","email":"john@example.com"}
END
# 获取不存在的数据
get user:9999
END
2.3 获取多个数据 #
bash
# 存储多个数据
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
STORED
set user:1002 0 3600 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
STORED
set user:1003 0 3600 45
{"id":1003,"name":"Bob","email":"bob@example.com"}
STORED
# 获取多个数据
get user:1001 user:1002 user:1003
VALUE user:1001 0 45
{"id":1001,"name":"John","email":"john@example.com"}
VALUE user:1002 0 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
VALUE user:1003 0 45
{"id":1003,"name":"Bob","email":"bob@example.com"}
END
# 部分键不存在
get user:1001 user:9999 user:1002
VALUE user:1001 0 45
{"id":1001,"name":"John","email":"john@example.com"}
VALUE user:1002 0 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
END
2.4 响应格式解析 #
text
响应格式:
VALUE <key> <flags> <bytes>\r\n
<data block>\r\n
END\r\n
字段说明:
key - 键名
flags - 标志位(存储时设置的值)
bytes - 数据字节数
data - 实际数据内容
示例解析:
VALUE user:1001 0 45
{"id":1001,"name":"John","email":"john@example.com"}
END
key = user:1001
flags = 0
bytes = 45
data = {"id":1001,"name":"John","email":"john@example.com"}
三、gets命令 #
3.1 命令说明 #
text
gets命令:
- 获取数据和CAS ID
- 用于实现乐观锁
- 可以同时获取多个键
语法:
gets <key>*\r\n
响应:
VALUE <key> <flags> <bytes> <cas unique>\r\n
<data block>\r\n
END\r\n
3.2 获取单个数据 #
bash
# 存储数据
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
STORED
# 获取数据和CAS ID
gets user:1001
VALUE user:1001 0 45 123
{"id":1001,"name":"John","email":"john@example.com"}
END
# CAS ID = 123
3.3 获取多个数据 #
bash
# 存储多个数据
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
STORED
set user:1002 0 3600 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
STORED
# 获取多个数据和CAS ID
gets user:1001 user:1002
VALUE user:1001 0 45 123
{"id":1001,"name":"John","email":"john@example.com"}
VALUE user:1002 0 45 124
{"id":1002,"name":"Jane","email":"jane@example.com"}
END
3.4 CAS ID的作用 #
text
CAS ID(Compare And Swap ID):
- 每次数据更新时递增
- 用于检测数据是否被修改
- 实现乐观锁机制
示例:
1. 客户端A获取数据,CAS ID = 100
2. 客户端B获取数据,CAS ID = 100
3. 客户端A更新数据,CAS ID变为101
4. 客户端B使用CAS ID = 100更新,失败(ID不匹配)
四、批量读取优化 #
4.1 批量读取优势 #
text
批量读取优势:
1. 减少网络往返
- 一次请求获取多个数据
- 降低延迟
2. 提高吞吐量
- 减少连接开销
- 提高效率
3. 简化代码
- 一次调用获取所有数据
- 减少代码复杂度
4.2 批量读取示例 #
bash
# 场景:获取用户列表
# 存储用户数据
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
STORED
set user:1002 0 3600 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
STORED
set user:1003 0 3600 45
{"id":1003,"name":"Bob","email":"bob@example.com"}
STORED
set user:1004 0 3600 45
{"id":1004,"name":"Alice","email":"alice@example.com"}
STORED
set user:1005 0 3600 45
{"id":1005,"name":"Charlie","email":"charlie@example.com"}
STORED
# 批量获取
get user:1001 user:1002 user:1003 user:1004 user:1005
VALUE user:1001 0 45
{"id":1001,"name":"John","email":"john@example.com"}
VALUE user:1002 0 45
{"id":1002,"name":"Jane","email":"jane@example.com"}
VALUE user:1003 0 45
{"id":1003,"name":"Bob","email":"bob@example.com"}
VALUE user:1004 0 45
{"id":1004,"name":"Alice","email":"alice@example.com"}
VALUE user:1005 0 45
{"id":1005,"name":"Charlie","email":"charlie@example.com"}
END
4.3 批量读取最佳实践 #
text
批量读取建议:
1. 控制批量大小
- 建议每次不超过100个键
- 避免响应过大
2. 处理部分命中
- 检查返回的数据
- 处理未命中的键
3. 使用Pipeline
- 客户端支持Pipeline
- 进一步提高性能
五、读取性能优化 #
5.1 使用UDP读取 #
text
UDP读取优势:
- 无连接开销
- 更快的响应
限制:
- 不保证可靠性
- 仅适合读取操作
启用UDP:
memcached -U 11211 -p 11211
5.2 客户端优化 #
text
客户端优化策略:
1. 连接池
- 复用连接
- 减少连接开销
2. Pipeline
- 批量发送命令
- 减少网络往返
3. 本地缓存
- 缓存热点数据
- 减少远程访问
4. 异步读取
- 并发读取多个键
- 提高吞吐量
5.3 读取示例代码 #
python
# Python示例
import pymemcache.client.base
# 创建客户端
client = pymemcache.client.base.Client(('localhost', 11211))
# 单个读取
user = client.get('user:1001')
# 批量读取
users = client.get_many(['user:1001', 'user:1002', 'user:1003'])
# 使用连接池
from pymemcache.client.base import PooledClient
client = PooledClient(('localhost', 11211), max_pool_size=10)
六、缓存穿透处理 #
6.1 缓存穿透问题 #
text
缓存穿透:
- 查询不存在的数据
- 每次都穿透到数据库
- 可能导致数据库压力过大
示例:
1. 查询 user:9999(不存在)
2. 缓存未命中
3. 查询数据库
4. 数据库也没有
5. 返回空
6. 下次查询继续穿透
6.2 解决方案 #
bash
# 方案1:缓存空值
set user:9999 0 300 0
# 空值过期时间较短
get user:9999
VALUE user:9999 0 0
END
# 方案2:使用特殊标记
set user:9991 0 300 4
NULL
STORED
get user:9991
VALUE user:9991 0 4
NULL
END
# 方案3:布隆过滤器(客户端实现)
# 先检查布隆过滤器,不存在直接返回
6.3 代码示例 #
python
def get_user(user_id):
key = f"user:{user_id}"
# 1. 查询缓存
data = client.get(key)
if data is not None:
if data == b"NULL":
# 缓存的空值
return None
return json.loads(data)
# 2. 查询数据库
user = db.query_user(user_id)
if user:
# 缓存数据
client.set(key, json.dumps(user), expire=3600)
else:
# 缓存空值
client.set(key, "NULL", expire=300)
return user
七、缓存雪崩处理 #
7.1 缓存雪崩问题 #
text
缓存雪崩:
- 大量缓存同时过期
- 大量请求同时穿透到数据库
- 数据库压力瞬间增大
示例:
1. 系统启动时加载大量缓存
2. 所有缓存设置相同的过期时间(1小时)
3. 1小时后所有缓存同时过期
4. 大量请求同时查询数据库
5. 数据库崩溃
7.2 解决方案 #
python
import random
def set_cache_with_jitter(key, value, base_expire=3600):
# 添加随机过期时间
jitter = random.randint(0, 300) # 0-5分钟随机值
expire = base_expire + jitter
client.set(key, value, expire=expire)
# 示例
for user in users:
key = f"user:{user.id}"
value = json.dumps(user)
set_cache_with_jitter(key, value)
7.3 缓存预热 #
bash
# 系统启动时预热缓存
# 1. 加载热点数据
set hot:product:1001 0 3600 128
{"id":1001,"name":"iPhone","price":999}
set hot:product:1002 0 3600 128
{"id":1002,"name":"iPad","price":799}
# 2. 设置不同的过期时间
set hot:product:1001 0 3600 128
set hot:product:1002 0 3700 128
set hot:product:1003 0 3800 128
八、缓存击穿处理 #
8.1 缓存击穿问题 #
text
缓存击穿:
- 热点数据过期
- 大量请求同时访问
- 数据库压力瞬间增大
示例:
1. 热点商品缓存过期
2. 大量用户同时访问该商品
3. 所有请求穿透到数据库
4. 数据库压力增大
8.2 解决方案 #
python
import threading
import time
# 方案1:互斥锁
def get_user_with_lock(user_id):
key = f"user:{user_id}"
lock_key = f"lock:{key}"
# 1. 查询缓存
data = client.get(key)
if data is not None:
return json.loads(data)
# 2. 获取锁
if client.add(lock_key, "1", expire=10):
try:
# 3. 查询数据库
user = db.query_user(user_id)
# 4. 缓存数据
if user:
client.set(key, json.dumps(user), expire=3600)
return user
finally:
# 5. 释放锁
client.delete(lock_key)
else:
# 等待并重试
time.sleep(0.1)
return get_user_with_lock(user_id)
# 方案2:永不过期
def get_user_never_expire(user_id):
key = f"user:{user_id}"
# 1. 查询缓存
data = client.get(key)
if data is not None:
value = json.loads(data)
# 检查逻辑过期时间
if value['expire_time'] > time.time():
return value['data']
# 2. 异步更新缓存
async_update_cache(user_id)
# 3. 返回旧数据或查询数据库
if data is not None:
return json.loads(data)['data']
return db.query_user(user_id)
九、读取统计 #
9.1 查看读取统计 #
bash
# 查看统计信息
stats
STAT cmd_get 10000 # get命令次数
STAT get_hits 8000 # 命中次数
STAT get_misses 2000 # 未命中次数
END
# 计算命中率
命中率 = get_hits / (get_hits + get_misses)
命中率 = 8000 / 10000 = 80%
9.2 监控脚本 #
bash
#!/bin/bash
# monitor_hit_rate.sh
HOST="127.0.0.1"
PORT="11211"
# 获取统计
stats=$(echo "stats" | nc $HOST $PORT)
# 解析数据
get_hits=$(echo "$stats" | grep "STAT get_hits" | awk '{print $3}')
get_misses=$(echo "$stats" | grep "STAT get_misses" | awk '{print $3}')
# 计算命中率
if [ $((get_hits + get_misses)) -gt 0 ]; then
hit_rate=$(echo "scale=2; $get_hits * 100 / ($get_hits + $get_misses)" | bc)
else
hit_rate=0
fi
echo "缓存命中率: ${hit_rate}%"
echo "命中次数: $get_hits"
echo "未命中次数: $get_misses"
十、最佳实践 #
10.1 读取优化建议 #
text
读取优化建议:
1. 使用批量读取
- 减少网络往返
- 提高性能
2. 合理设置过期时间
- 避免缓存雪崩
- 添加随机值
3. 处理缓存穿透
- 缓存空值
- 使用布隆过滤器
4. 处理缓存击穿
- 使用互斥锁
- 热点数据永不过期
5. 监控命中率
- 定期检查命中率
- 优化缓存策略
10.2 键命名建议 #
bash
# 好的键命名
get user:1001
get product:2001:detail
get order:3001:items
# 不好的键命名
get 1001
get user_data_1001
get user-data-1001
10.3 数据格式建议 #
bash
# JSON格式(推荐)
set user:1001 0 3600 45
{"id":1001,"name":"John","email":"john@example.com"}
# 序列化格式(紧凑)
set user:1001 0 3600 30
<serialized data>
# 压缩格式(大数据)
set large:data 0 3600 100
<compressed data>
十一、总结 #
读取命令要点:
| 命令 | 说明 | 用途 |
|---|---|---|
| get | 获取数据 | 简单读取 |
| gets | 获取数据和CAS ID | 乐观锁 |
最佳实践:
- 使用批量读取提高性能
- 处理缓存穿透、雪崩、击穿
- 监控缓存命中率
- 合理设置过期时间
下一步,让我们学习Memcached的删除与更新命令!
最后更新:2026-03-27