Skip to content

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"}
END

gets 命令

功能:获取一个或多个键的值和 CAS 标识符

格式

gets key1 [key2 ...]

响应

VALUE key flags bytes cas_unique
value
...
END

3. 删除命令

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
...
END

flush_all 命令

功能:清空所有缓存数据

格式

flush_all [delay] [noreply]

delay:可选参数,延迟清空的秒数

响应

  • OK:清空成功

6. 其他命令

version 命令

功能:获取服务器版本信息

格式

version

响应

VERSION version_string

verbosity 命令

功能:设置服务器日志级别

格式

verbosity level [noreply]

响应

  • OK:设置成功

协议流程详解

1. 连接建立

  1. 客户端与 Memcached 服务器建立 TCP 连接
  2. 服务器接受连接,分配工作线程处理请求

2. 命令执行流程

以 set 命令为例,协议执行流程如下:

  1. 客户端发送 set 命令:set user:1 0 3600 12\r\n{"id":1,"name":"test"}\r\n
  2. 服务器解析命令,验证参数
  3. 服务器存储数据到内存
  4. 服务器返回响应:STORED\r\n

以 get 命令为例,协议执行流程如下:

  1. 客户端发送 get 命令:get user:1\r\n
  2. 服务器解析命令,查找键
  3. 如果键存在,服务器返回数据:VALUE user:1 0 12\r\n{"id":1,"name":"test"}\r\nEND\r\n
  4. 如果键不存在,服务器返回:END\r\n

3. 连接关闭

  1. 客户端发送关闭连接请求
  2. 服务器关闭连接,释放资源

数据格式规范

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:123product: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 机制的步骤如下:

  1. 使用 gets 命令获取键的值和 CAS 标识符
  2. 修改值
  3. 使用 cas 命令设置新值,并带上之前获取的 CAS 标识符
  4. 如果 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 参数,减少响应处理
  • 优化键和值的大小,减少网络传输量
  • 选择高效的序列化格式
  • 合理设置过期时间,减少数据过期清理开销