外观
Memcached 文本协议
协议基本格式
1. 命令格式
Memcached 文本协议的命令格式为:
command key flags exptime bytes [noreply]
value- command:命令名称(如 get、set、delete 等)
- key:数据的键名,长度不超过 250 字节
- flags:用户定义的标志,可以存储额外信息,如数据类型
- exptime:过期时间,单位为秒
- 0:永不过期
- 小于 30 天:表示秒数
- 大于等于 30 天:表示 UNIX 时间戳
- bytes:值的字节数,不包括结尾的 \r\n
2. 响应格式
响应格式根据命令类型和执行结果而有所不同,主要包括:
- 单行响应:如 STORED、NOT_STORED、DELETED 等
- 多行响应:如 get 命令返回的数据
- 错误响应:如 ERROR、CLIENT_ERROR、SERVER_ERROR 等
核心命令详解
1. 存储命令
set 命令
功能:设置键值对,无论键是否存在
格式:
set key flags exptime bytes [noreply]
value响应:
- STORED:存储成功
- NOT_STORED:存储失败
示例:
set user:1 0 3600 12
{"id":1,"name":"test"}add 命令
功能:只有当键不存在时才设置键值对
格式:
add key flags exptime bytes [noreply]
value响应:
- STORED:存储成功
- NOT_STORED:键已存在,存储失败
replace 命令
功能:只有当键存在时才替换键值对
格式:
replace key flags exptime bytes [noreply]
value响应:
- STORED:替换成功
- NOT_STORED:键不存在,替换失败
append 命令
功能:在现有值的末尾追加数据
格式:
append key flags exptime bytes [noreply]
value响应:
- STORED:追加成功
- NOT_STORED:键不存在,追加失败
prepend 命令
功能:在现有值的开头添加数据
格式:
prepend key flags exptime bytes [noreply]
value响应:
- STORED:添加成功
- NOT_STORED:键不存在,添加失败
cas 命令
功能:使用 CAS(Check and Set)机制设置键值对,用于并发场景
格式:
cas key flags exptime bytes cas_unique [noreply]
value响应:
- STORED:存储成功
- EXISTS:CAS 值不匹配,存储失败
- NOT_FOUND:键不存在
2. 读取命令
get 命令
功能:获取一个或多个键的值
格式:
get key1 [key2 ...]响应:
VALUE key flags bytes
value
...
END示例:
get user:1 user:2
VALUE user:1 0 12
{"id":1,"name":"test"}
VALUE user:2 0 13
{"id":2,"name":"test2"}
ENDgets 命令
功能:获取一个或多个键的值和 CAS 标识符
格式:
gets key1 [key2 ...]响应:
VALUE key flags bytes cas_unique
value
...
END3. 删除命令
delete 命令
功能:删除指定键
格式:
delete key [noreply]响应:
- DELETED:删除成功
- NOT_FOUND:键不存在
4. 计数器命令
incr 命令
功能:递增键的值,键必须存储数字类型
格式:
incr key increment_value [noreply]响应:
- 递增后的值
- NOT_FOUND:键不存在
- CLIENT_ERROR:值不是数字类型
decr 命令
功能:递减键的值,键必须存储数字类型
格式:
decr key decrement_value [noreply]响应:
- 递减后的值
- NOT_FOUND:键不存在
- CLIENT_ERROR:值不是数字类型
5. 统计命令
stats 命令
功能:获取服务器统计信息
格式:
stats [argument]argument:可选参数,如 items、slabs、settings 等
响应:
STAT key value
...
ENDflush_all 命令
功能:清空所有缓存数据
格式:
flush_all [delay] [noreply]delay:可选参数,延迟清空的秒数
响应:
- OK:清空成功
6. 其他命令
version 命令
功能:获取服务器版本信息
格式:
version响应:
VERSION version_stringverbosity 命令
功能:设置服务器日志级别
格式:
verbosity level [noreply]响应:
- OK:设置成功
协议流程详解
1. 连接建立
- 客户端与 Memcached 服务器建立 TCP 连接
- 服务器接受连接,分配工作线程处理请求
2. 命令执行流程
以 set 命令为例,协议执行流程如下:
- 客户端发送 set 命令:
set user:1 0 3600 12\r\n{"id":1,"name":"test"}\r\n - 服务器解析命令,验证参数
- 服务器存储数据到内存
- 服务器返回响应:
STORED\r\n
以 get 命令为例,协议执行流程如下:
- 客户端发送 get 命令:
get user:1\r\n - 服务器解析命令,查找键
- 如果键存在,服务器返回数据:
VALUE user:1 0 12\r\n{"id":1,"name":"test"}\r\nEND\r\n - 如果键不存在,服务器返回:
END\r\n
3. 连接关闭
- 客户端发送关闭连接请求
- 服务器关闭连接,释放资源
数据格式规范
1. 键的规范
- 长度限制:键名长度不超过 250 字节
- 字符限制:可以包含字母、数字、下划线、连字符等,但不能包含空格、换行符等特殊字符
- 命名约定:建议使用前缀+冒号的格式(如
user:1),便于管理和统计
2. 值的规范
- 字节数限制:默认最大为 1MB,可以通过
-I参数修改 - 数据类型:可以存储任意二进制数据,但建议根据实际需求选择合适的数据格式
- 序列化:对于复杂数据,建议使用 JSON、MessagePack 等序列化格式
3. 过期时间规范
- 永不过期:设置为 0
- 相对时间:小于 30 天(60×60×24×30=2592000 秒),表示秒数
- 绝对时间:大于等于 30 天,表示 UNIX 时间戳
- 过期策略:采用惰性删除+定期删除的混合策略
客户端实现
1. 基本实现步骤
以 Python 为例,实现一个简单的 Memcached 文本协议客户端:
python
import socket
class MemcachedClient:
def __init__(self, host='localhost', port=11211):
self.host = host
self.port = port
self.socket = None
def connect(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((self.host, self.port))
def close(self):
if self.socket:
self.socket.close()
self.socket = None
def _send_command(self, command):
if not self.socket:
self.connect()
self.socket.sendall(command.encode('utf-8'))
def _recv_response(self):
data = b''
while True:
chunk = self.socket.recv(4096)
if not chunk:
break
data += chunk
if b'\r\nEND\r\n' in data:
break
if b'\r\nSTORED\r\n' in data:
break
if b'\r\nNOT_STORED\r\n' in data:
break
if b'\r\nDELETED\r\n' in data:
break
if b'\r\nNOT_FOUND\r\n' in data:
break
if b'\r\nOK\r\n' in data:
break
if b'\r\nVERSION' in data:
break
return data.decode('utf-8')
def set(self, key, value, exptime=0, flags=0):
command = f'set {key} {flags} {exptime} {len(value)}\r\n{value}\r\n'
self._send_command(command)
return self._recv_response()
def get(self, key):
command = f'get {key}\r\n'
self._send_command(command)
response = self._recv_response()
# 解析响应
lines = response.split('\r\n')
if len(lines) < 3 or not lines[0].startswith('VALUE'):
return None
# 提取值
value_line = lines[1]
return value_line
def delete(self, key):
command = f'delete {key}\r\n'
self._send_command(command)
return self._recv_response()
def stats(self, argument=''):
command = f'stats {argument}\r\n'.strip() + '\r\n'
self._send_command(command)
return self._recv_response()
def version(self):
command = f'version\r\n'
self._send_command(command)
return self._recv_response()
# 使用示例
if __name__ == '__main__':
client = MemcachedClient()
client.connect()
# set 命令
print(client.set('test_key', 'test_value', exptime=3600))
# get 命令
print(client.get('test_key'))
# stats 命令
print(client.stats())
# version 命令
print(client.version())
# delete 命令
print(client.delete('test_key'))
client.close()2. 连接池实现
为了提高性能,客户端通常会实现连接池,避免频繁创建和销毁连接:
python
import socket
import queue
import threading
class MemcachedConnectionPool:
def __init__(self, host='localhost', port=11211, max_connections=10):
self.host = host
self.port = port
self.max_connections = max_connections
self.pool = queue.Queue(maxsize=max_connections)
self.lock = threading.Lock()
# 初始化连接池
for _ in range(max_connections):
self.pool.put(self._create_connection())
def _create_connection(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.host, self.port))
return sock
def get_connection(self):
return self.pool.get()
def return_connection(self, conn):
try:
self.pool.put(conn)
except queue.Full:
conn.close()
def close(self):
while not self.pool.empty():
conn = self.pool.get()
conn.close()协议最佳实践
1. 键设计最佳实践
- 使用有意义的键名:便于管理和调试
- 使用前缀分类:如
user:123、product:456 - 限制键的长度:不超过 250 字节
- 避免使用特殊字符:如空格、换行符等
- 使用哈希值作为键:对于长键,可以使用哈希值缩短
2. 值设计最佳实践
- 选择合适的序列化格式:JSON、MessagePack 等
- 压缩大值:对于大值,考虑压缩后存储
- 限制值的大小:避免存储过大的值,影响性能
- 避免存储敏感数据:Memcached 以明文存储数据
3. 命令使用最佳实践
- 使用 noreply 参数:对于非关键操作,使用 noreply 参数提高性能
- 批量操作:使用批量 get 命令减少网络往返
- 合理设置过期时间:根据数据的时效性设置合适的过期时间
- 使用 CAS 机制:在并发场景下,使用 CAS 机制避免数据冲突
4. 性能优化最佳实践
- 使用连接池:避免频繁创建和销毁连接
- 减少网络往返:使用批量命令,减少请求次数
- 优化序列化:选择高效的序列化格式
- 合理设置超时时间:避免长时间等待响应
- 监控和调优:定期监控协议性能,进行调优
文本协议与二进制协议对比
| 特性 | 文本协议 | 二进制协议 |
|---|---|---|
| 可读性 | 人类可读,便于调试 | 不可读,调试困难 |
| 性能 | 较低,需要解析文本 | 较高,二进制格式解析更快 |
| 灵活性 | 较差,命令和响应格式固定 | 较好,支持扩展 |
| 客户端支持 | 广泛支持 | 部分客户端支持 |
| 错误处理 | 基于文本的错误信息 | 结构化错误码 |
| 数据安全性 | 明文传输 | 支持加密传输 |
常见问题(FAQ)
Q1: Memcached 文本协议的默认端口是什么?
A1: Memcached 文本协议的默认端口是 11211。
Q2: 文本协议支持哪些数据类型?
A2: 文本协议本身不区分数据类型,所有数据都以二进制形式存储。客户端需要负责数据的序列化和反序列化。
Q3: 键名的最大长度是多少?
A3: 键名的最大长度是 250 字节。超过这个长度的键会被服务器拒绝。
Q4: 值的最大大小是多少?
A4: 值的最大大小默认是 1MB,可以通过 -I 参数修改。例如,memcached -I 2m 将最大值大小设置为 2MB。
Q5: 如何设置永不过期的键?
A5: 将 exptime 参数设置为 0 即可,表示键永不过期。
Q6: noreply 参数有什么作用?
A6: noreply 参数表示客户端不需要服务器返回响应,这样可以减少网络往返,提高性能。适用于非关键操作,如某些 set 或 delete 命令。
Q7: 如何使用 CAS 机制?
A7: 使用 CAS 机制的步骤如下:
- 使用 gets 命令获取键的值和 CAS 标识符
- 修改值
- 使用 cas 命令设置新值,并带上之前获取的 CAS 标识符
- 如果 CAS 标识符匹配,服务器会更新值;否则,返回 EXISTS
Q8: 如何清空 Memcached 中的所有数据?
A8: 使用 flush_all 命令可以清空 Memcached 中的所有数据。可以通过添加延迟参数,如 flush_all 30,表示 30 秒后清空所有数据。
Q9: 如何获取 Memcached 服务器的统计信息?
A9: 使用 stats 命令可以获取 Memcached 服务器的统计信息。可以添加参数查看特定类型的统计信息,如:
stats items:查看项目统计stats slabs:查看内存分配统计stats settings:查看服务器配置
Q10: 文本协议和二进制协议可以同时使用吗?
A10: 是的,Memcached 服务器可以同时支持文本协议和二进制协议。但需要注意的是,两者使用相同的端口,服务器会根据客户端发送的数据自动识别协议类型。
Q11: 如何处理连接超时?
A11: 处理连接超时的方法包括:
- 在客户端设置合理的连接超时时间
- 实现连接池,避免频繁创建连接
- 监控服务器状态,及时发现和处理故障
- 实现重试机制,处理临时故障
Q12: 如何调试 Memcached 文本协议?
A12: 可以使用以下工具调试 Memcached 文本协议:
- telnet:直接连接 Memcached 服务器,发送命令并查看响应
- nc(netcat):类似 telnet,更轻量级
- Wireshark:捕获和分析网络数据包
- memcached-tool:Memcached 自带的监控工具
Q13: 文本协议支持加密吗?
A13: 文本协议本身不支持加密,但可以通过以下方式实现加密:
- 使用 TLS/SSL 加密传输层
- 在客户端和服务器之间使用 VPN 或 SSH 隧道
- 在应用层对数据进行加密后再存储
Q14: 如何处理大值存储?
A14: 处理大值存储的方法包括:
- 压缩数据后存储
- 拆分大值为多个小值,分别存储
- 考虑使用其他存储方案,如 Redis 或数据库
- 调整服务器的最大值大小限制
Q15: 如何提高文本协议的性能?
A15: 提高文本协议性能的方法包括:
- 使用连接池,避免频繁创建和销毁连接
- 使用批量命令,减少网络往返
- 使用 noreply 参数,减少响应处理
- 优化键和值的大小,减少网络传输量
- 选择高效的序列化格式
- 合理设置过期时间,减少数据过期清理开销
