Memcached最佳实践 #

一、部署最佳实践 #

1.1 硬件规划 #

text
硬件规划建议:

1. 内存
   - 根据数据量规划
   - 预留20%余量
   - 考虑内存碎片

2. CPU
   - 多核CPU
   - 线程数与核心数匹配

3. 网络
   - 千兆网卡
   - 低延迟网络

4. 存储
   - 不需要持久化存储
   - 日志存储足够

示例配置:

小型应用:
- 内存:4GB
- CPU:2核
- 节点数:1

中型应用:
- 内存:16GB
- CPU:4核
- 节点数:3

大型应用:
- 内存:64GB
- CPU:8核
- 节点数:10+

1.2 网络规划 #

text
网络规划建议:

1. 内网部署
   - 只监听内网IP
   - 不暴露到公网

2. 专用网络
   - 使用独立网络
   - 减少网络干扰

3. 网络拓扑
   - 应用服务器靠近缓存服务器
   - 减少网络延迟

示例:
┌─────────────────────────────────────────┐
│            应用服务器集群                │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │  App1   │ │  App2   │ │  App3   │   │
│  └─────────┘ └─────────┘ └─────────┘   │
└─────────────────────────────────────────┘
                    │
            ┌───────┴───────┐
            │  专用缓存网络  │
            └───────┬───────┘
                    │
┌─────────────────────────────────────────┐
│          Memcached服务器集群             │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐   │
│  │  MC1    │ │  MC2    │ │  MC3    │   │
│  └─────────┘ └─────────┘ └─────────┘   │
└─────────────────────────────────────────┘

1.3 配置建议 #

bash
# 生产环境推荐配置

memcached -d -m 2048 -p 11211 -u memcache -c 4096 -t 8 \
  -f 1.15 -L -I 1m \
  -o lru_crawler,slab_reassign,slab_automove=1 \
  -l 192.168.1.100 \
  -v >> /var/log/memcached.log 2>&1

# 参数说明:
# -d            后台运行
# -m 2048       2GB内存
# -p 11211      端口
# -u memcache   运行用户
# -c 4096       最大连接数
# -t 8          工作线程数
# -f 1.15       增长因子
# -L            大内存页
# -I 1m         最大Item大小
# -o ...        高级选项
# -l 192.168.1.100  监听内网IP
# -v            详细日志

二、键设计最佳实践 #

2.1 键命名规范 #

text
键命名规范:

1. 使用命名空间
   格式:<namespace>:<type>:<id>

2. 使用层级结构
   格式:<namespace>:<type>:<id>:<field>

3. 保持简洁
   - 避免过长的键名
   - 但要保持可读性

示例:
✓ user:info:1001
✓ user:profile:1001
✓ product:detail:2001
✓ order:items:3001
✓ cache:db:users:1001

✗ user_information_cache_user_id_1001
✗ 1001
✗ user-data-1001

2.2 键长度建议 #

text
键长度建议:

1. 最大长度:250字节
2. 推荐长度:< 100字节
3. 平衡可读性和性能

示例:
# 不好的做法
key = "user_information_cache_user_id_1001_profile_data"
# 长度:48字节

# 好的做法
key = "u:1001:p"
# 长度:8字节

# 平衡做法
key = "user:1001:profile"
# 长度:17字节

2.3 键命名示例 #

python
# 用户相关
user:info:1001           # 用户基本信息
user:profile:1001        # 用户资料
user:settings:1001       # 用户设置
user:friends:1001        # 用户好友列表

# 商品相关
product:info:2001        # 商品信息
product:stock:2001       # 商品库存
product:reviews:2001     # 商品评论

# 订单相关
order:info:3001          # 订单信息
order:items:3001         # 订单商品
order:status:3001        # 订单状态

# 缓存相关
cache:db:users:1001      # 数据库缓存
cache:api:weather:bj     # API缓存
cache:page:home          # 页面缓存

# Session相关
session:web:abc123       # Web Session
session:api:xyz789       # API Session

# 计数器相关
counter:page:views:home  # 页面访问计数
counter:api:calls:weather # API调用计数
counter:user:likes:1001  # 用户点赞计数

三、数据存储最佳实践 #

3.1 数据大小优化 #

python
import json

# 不好的做法
data = {
    "user_id": 1001,
    "user_name": "John Doe",
    "user_email": "john@example.com",
    "user_description": "A very long description..." * 100
}

# 好的做法
data = {
    "id": 1001,
    "name": "John Doe",
    "email": "john@example.com"
}
# 长文本单独存储或压缩

# 压缩大数据
import gzip

def compress_data(data):
    json_str = json.dumps(data)
    return gzip.compress(json_str.encode())

def decompress_data(compressed):
    json_str = gzip.decompress(compressed).decode()
    return json.loads(json_str)

# 使用
large_data = {"description": "..." * 10000}
compressed = compress_data(large_data)
client.set("large:data", compressed, flags=3)

3.2 序列化选择 #

text
序列化格式选择:

1. JSON
   优点:可读性好,跨语言
   缺点:体积较大
   适用:配置数据,需要可读性

2. MessagePack
   优点:体积小,速度快
   缺点:需要额外库
   适用:性能要求高

3. Protobuf
   优点:体积最小
   缺点:需要定义schema
   适用:固定结构数据

4. Pickle(Python)
   优点:支持复杂对象
   缺点:仅Python,安全风险
   适用:Python内部使用

示例:
# JSON
data = {"id": 1001, "name": "John"}
json_str = json.dumps(data)  # 体积:32字节

# MessagePack
import msgpack
data = {"id": 1001, "name": "John"}
msgpack_data = msgpack.packb(data)  # 体积:18字节

3.3 过期时间设置 #

python
import random

# 根据数据特性设置过期时间

# 1. 静态数据:较长过期时间
client.set("config:app", config_data, expire=86400)  # 1天

# 2. 动态数据:较短过期时间
client.set("api:weather", weather_data, expire=1800)  # 30分钟

# 3. 临时数据:很短的过期时间
client.set("code:verify", code, expire=300)  # 5分钟

# 4. 避免缓存雪崩:添加随机值
def set_with_jitter(key, value, base_expire=3600, jitter=300):
    expire = base_expire + random.randint(0, jitter)
    client.set(key, value, expire=expire)

# 5. 热点数据:永不过期
client.set("hot:product:1001", product_data, expire=0)

四、缓存策略最佳实践 #

4.1 缓存模式选择 #

text
缓存模式选择:

1. Cache-Aside(旁路缓存)
   - 读:先查缓存,未命中查数据库,然后写入缓存
   - 写:先更新数据库,然后删除缓存
   - 适用:读多写少

2. Write-Through(写穿透)
   - 读:先查缓存,未命中查数据库,然后写入缓存
   - 写:先写缓存,然后写数据库
   - 适用:数据一致性要求高

3. Write-Behind(写回)
   - 读:先查缓存
   - 写:只写缓存,异步写数据库
   - 适用:写性能要求高

4. Write-Around(写绕过)
   - 读:先查缓存,未命中查数据库,然后写入缓存
   - 写:只写数据库,不写缓存
   - 适用:写后很少读

4.2 缓存更新策略 #

python
# Cache-Aside模式
def get_user(user_id):
    key = f"user:{user_id}"
    
    # 1. 查缓存
    user = client.get(key)
    if user is not None:
        return json.loads(user)
    
    # 2. 查数据库
    user = db.query_user(user_id)
    
    # 3. 写缓存
    if user is not None:
        client.set(key, json.dumps(user), expire=3600)
    
    return user

def update_user(user_id, user_data):
    # 1. 更新数据库
    db.update_user(user_id, user_data)
    
    # 2. 删除缓存
    key = f"user:{user_id}"
    client.delete(key)

# Write-Through模式
def update_user_write_through(user_id, user_data):
    key = f"user:{user_id}"
    
    # 1. 更新数据库
    db.update_user(user_id, user_data)
    
    # 2. 更新缓存
    client.set(key, json.dumps(user_data), expire=3600)

4.3 缓存穿透防护 #

python
def get_user_with_cache_protection(user_id):
    key = f"user:{user_id}"
    
    # 1. 查缓存
    user = client.get(key)
    
    if user is not None:
        if user == b"NULL":
            # 缓存的空值
            return None
        return json.loads(user)
    
    # 2. 查数据库
    user = db.query_user(user_id)
    
    # 3. 写缓存
    if user is not None:
        client.set(key, json.dumps(user), expire=3600)
    else:
        # 缓存空值,防止缓存穿透
        client.set(key, "NULL", expire=300)
    
    return user

4.4 缓存雪崩防护 #

python
import random

def set_with_random_expire(key, value, base_expire=3600, jitter=300):
    # 添加随机过期时间,避免同时过期
    expire = base_expire + random.randint(0, jitter)
    client.set(key, value, expire=expire)

# 批量设置时使用
for user in users:
    key = f"user:{user['id']}"
    set_with_random_expire(key, json.dumps(user))

4.5 缓存击穿防护 #

python
import threading
import time

def get_hot_user(user_id):
    key = f"user:{user_id}"
    lock_key = f"lock:{key}"
    
    # 1. 查缓存
    user = client.get(key)
    if user is not None:
        return json.loads(user)
    
    # 2. 获取锁
    if client.add(lock_key, "1", expire=10):
        try:
            # 3. 查数据库
            user = db.query_user(user_id)
            
            # 4. 写缓存
            if user is not None:
                client.set(key, json.dumps(user), expire=3600)
            
            return user
        finally:
            # 5. 释放锁
            client.delete(lock_key)
    else:
        # 等待并重试
        time.sleep(0.1)
        return get_hot_user(user_id)

五、监控最佳实践 #

5.1 关键监控指标 #

text
关键监控指标:

1. 性能指标
   - 命中率:> 90%
   - QPS:监控峰值
   - 延迟:< 1ms(本地)

2. 资源指标
   - 内存使用率:< 80%
   - 连接数:监控峰值
   - CPU使用率:监控峰值

3. 业务指标
   - 数据量:监控增长
   - 淘汰次数:监控增长
   - 错误率:监控异常

4. 集群指标
   - 节点状态:监控存活
   - 数据分布:监控均衡
   - 网络延迟:监控延迟

5.2 告警规则 #

text
告警规则:

1. 命中率告警
   - 命中率 < 80%:警告
   - 命中率 < 60%:严重

2. 内存使用告警
   - 内存使用率 > 80%:警告
   - 内存使用率 > 90%:严重

3. 连接数告警
   - 连接数使用率 > 80%:警告
   - 连接数使用率 > 90%:严重

4. 淘汰告警
   - 淘汰次数快速增长:警告

5. 节点故障告警
   - 节点不可用:严重

5.3 监控脚本 #

bash
#!/bin/bash
# memcached_monitor.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}')
bytes=$(echo "$stats" | grep "STAT bytes " | awk '{print $3}')
limit_maxbytes=$(echo "$stats" | grep "STAT limit_maxbytes" | awk '{print $3}')
curr_connections=$(echo "$stats" | grep "STAT curr_connections" | awk '{print $3}')
max_connections=$(echo "$stats" | grep "STAT max_connections" | awk '{print $3}')
evictions=$(echo "$stats" | grep "STAT evictions" | 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

mem_usage=$(echo "scale=2; $bytes * 100 / $limit_maxbytes" | bc)
conn_usage=$(echo "scale=2; $curr_connections * 100 / $max_connections" | bc)

# 检查告警
ALERTS=()

if [ $(echo "$hit_rate < 80" | bc) -eq 1 ]; then
    ALERTS+=("命中率低于80%: ${hit_rate}%")
fi

if [ $(echo "$mem_usage > 80" | bc) -eq 1 ]; then
    ALERTS+=("内存使用率超过80%: ${mem_usage}%")
fi

if [ $(echo "$conn_usage > 80" | bc) -eq 1 ]; then
    ALERTS+=("连接数使用率超过80%: ${conn_usage}%")
fi

# 输出结果
echo "Memcached监控报告"
echo "=================="
echo "命中率: ${hit_rate}%"
echo "内存使用率: ${mem_usage}%"
echo "连接数使用率: ${conn_usage}%"
echo "淘汰次数: $evictions"

if [ ${#ALERTS[@]} -gt 0 ]; then
    echo ""
    echo "告警信息:"
    for alert in "${ALERTS[@]}"; do
        echo "  - $alert"
    done
fi

六、安全最佳实践 #

6.1 网络安全 #

bash
# 1. 只监听内网IP
memcached -l 192.168.1.100

# 2. 使用防火墙限制访问
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 11211 -j ACCEPT
iptables -A INPUT -p tcp --dport 11211 -j DROP

# 3. 不要暴露到公网
# 错误示例:
# memcached -l 0.0.0.0  # 危险!

6.2 访问控制 #

bash
# 1. 使用SASL认证
memcached -S -d -m 64 -p 11211

# 2. 创建用户
echo "mech_list: plain" > /etc/sasl2/memcached.conf
saslpasswd2 -a memcached -c user1

# 3. 客户端认证
# Python示例
from pymemcache.client.base import Client

client = Client(
    ('localhost', 11211),
    username='user1',
    password='password'
)

6.3 数据安全 #

python
# 1. 敏感数据加密
from cryptography.fernet import Fernet

key = Fernet.generate_key()
cipher = Fernet(key)

def encrypt_data(data):
    return cipher.encrypt(data.encode())

def decrypt_data(encrypted):
    return cipher.decrypt(encrypted).decode()

# 使用
sensitive_data = "password123"
encrypted = encrypt_data(sensitive_data)
client.set("sensitive:key", encrypted)

# 2. 不存储敏感信息
# 不好的做法
user = {
    "id": 1001,
    "password": "hashed_password"  # 不要存储
}

# 好的做法
user = {
    "id": 1001,
    "name": "John"
}

七、故障处理最佳实践 #

7.1 故障预防 #

text
故障预防措施:

1. 监控预警
   - 实时监控关键指标
   - 设置合理的告警阈值

2. 容量规划
   - 预估数据增长
   - 提前扩容

3. 高可用架构
   - 多节点部署
   - 故障自动转移

4. 定期备份
   - Memcached不支持持久化
   - 数据源需要备份

7.2 故障恢复 #

python
# 故障恢复策略

class ResilientClient:
    def __init__(self, servers, max_retries=3, timeout=1):
        self.servers = servers
        self.max_retries = max_retries
        self.timeout = timeout
        self.client = self._create_client()
    
    def _create_client(self):
        from pymemcache.client.hash import HashClient
        return HashClient(
            self.servers,
            use_pooling=True,
            timeout=self.timeout,
            ignore_exc=True
        )
    
    def get(self, key, default=None):
        for i in range(self.max_retries):
            try:
                value = self.client.get(key)
                if value is not None:
                    return value
                return default
            except Exception as e:
                if i == self.max_retries - 1:
                    print(f"Failed to get {key}: {e}")
                    return default
                time.sleep(0.1)
    
    def set(self, key, value, expire=3600):
        for i in range(self.max_retries):
            try:
                self.client.set(key, value, expire=expire)
                return True
            except Exception as e:
                if i == self.max_retries - 1:
                    print(f"Failed to set {key}: {e}")
                    return False
                time.sleep(0.1)

八、总结 #

最佳实践要点:

方面 建议
部署 内网部署、多节点、合理配置
键设计 命名规范、简洁明了
数据存储 优化大小、合理序列化
缓存策略 选择合适模式、防护穿透/雪崩/击穿
监控 关键指标、告警规则
安全 网络安全、访问控制、数据加密
故障处理 预防为主、快速恢复

恭喜你完成了Memcached的学习!

最后更新:2026-03-27