Skip to content

Memcached 日志分析

日志配置

日志级别配置

命令行参数配置

bash
# 设置日志级别为 verbose
memcached -v

# 设置日志级别为 very verbose
memcached -vv

# 设置日志级别为 extremely verbose
memcached -vvv

# 将日志输出到文件
memcached -vv 2> /var/log/memcached.log

系统服务配置

CentOS/RHEL 系统

bash
# 编辑 systemd 配置文件
vi /etc/systemd/system/memcached.service

# 在 ExecStart 行添加日志参数
ExecStart=/usr/bin/memcached -u memcached -p 11211 -m 1024 -c 1024 -vv 2> /var/log/memcached.log

# 重新加载配置并重启
systemctl daemon-reload
systemctl restart memcached

Ubuntu/Debian 系统

bash
# 编辑配置文件
vi /etc/memcached.conf

# 添加日志配置
-vv
logfile /var/log/memcached.log

# 重启服务
systemctl restart memcached

日志轮换配置

使用 logrotate 进行日志轮换

bash
# 创建 logrotate 配置文件
vi /etc/logrotate.d/memcached

# 添加以下内容
/var/log/memcached.log {
    daily
    missingok
    rotate 7
    compress
    delaycompress
    notifempty
    create 644 memcached memcached
    postrotate
        systemctl reload memcached > /dev/null 2>&1 || true
    endscript
}

手动触发日志轮换

bash
logrotate -f /etc/logrotate.d/memcached

日志内容分析

基本日志格式

客户端连接日志

<42 server listening (auto-negotiate)
<42 server listening (udp)
<42 server listening (tcp)
<42 new connection: 127.0.0.1:54321

命令执行日志

<42 connection: 127.0.0.1:54321
<42 cmd: set key1 0 3600 5
<42 storing key1, flags 0, exptime 3600, bytes 5
>42 sending key key1
>42 END

<42 cmd: get key1
<42 retrieving key key1
>42 sending key key1
>42 END

错误日志

<42 bad data chunk
<42 bad command line format: invalid command
<42 out of memory writing item

日志字段解析

字段描述示例
日志级别日志的优先级,<42 表示一般信息<42
事件类型日志事件的类型cmd, connection, storing, retrieving
命令Memcached 命令set, get, delete, stats
键名操作的键名称key1
标志位键的标志位0
过期时间键的过期时间(秒)3600
字节数存储的数据大小5
客户端地址客户端的 IP 地址和端口127.0.0.1:54321

常见日志事件

连接事件

  • new connection:新客户端连接
  • connection closed:客户端连接关闭
  • accept4() failed:连接接受失败

命令事件

  • cmd: set:存储命令
  • cmd: get:获取命令
  • cmd: delete:删除命令
  • cmd: incr/cmd: decr:增减命令
  • cmd: flush_all:清空缓存命令

错误事件

  • bad data chunk:数据块格式错误
  • invalid command:无效命令
  • out of memory writing item:内存不足
  • error reading from client:读取客户端数据错误

日志分析工具

命令行工具

grep 分析

bash
# 统计不同命令的执行次数
grep -o "cmd: [a-z]*" /var/log/memcached.log | sort | uniq -c

# 查找错误日志
grep -i "error\|fail\|bad" /var/log/memcached.log

# 统计连接次数
grep -c "new connection" /var/log/memcached.log

# 查找特定键的操作
grep "key1" /var/log/memcached.log

awk 分析

bash
# 分析命令执行时间(需要开启详细日志)
awk '/cmd: / {cmd=$3; ts=systime()} /END/ {if(cmd) print cmd, systime()-ts; cmd=""}' /var/log/memcached.log

# 统计每个客户端的连接数
awk '/new connection: / {split($4, a, ":"); clients[a[1]]++} END {for(c in clients) print c, clients[c]}' /var/log/memcached.log | sort -nr -k2

sed 过滤

bash
# 只保留命令相关日志
sed -n '/cmd: /,/END/p' /var/log/memcached.log

# 过滤掉特定类型的日志
sed '/server listening/d' /var/log/memcached.log

日志分析脚本

Python 日志分析脚本

python
#!/usr/bin/env python3
import re
from collections import Counter, defaultdict

# 日志文件路径
LOG_FILE = '/var/log/memcached.log'

def analyze_memcached_log(log_file):
    # 初始化统计数据
    command_count = Counter()
    error_count = 0
    connection_count = 0
    client_connections = defaultdict(int)
    
    # 日志模式匹配
    cmd_pattern = re.compile(r'cmd: ([a-z]+)')
    error_pattern = re.compile(r'(error|fail|bad|invalid|out of memory)', re.IGNORECASE)
    connection_pattern = re.compile(r'new connection: ([0-9.]+):')
    
    # 读取日志文件
    with open(log_file, 'r') as f:
        for line in f:
            # 统计命令
            cmd_match = cmd_pattern.search(line)
            if cmd_match:
                command = cmd_match.group(1)
                command_count[command] += 1
            
            # 统计错误
            if error_pattern.search(line):
                error_count += 1
            
            # 统计连接
            conn_match = connection_pattern.search(line)
            if conn_match:
                client_ip = conn_match.group(1)
                connection_count += 1
                client_connections[client_ip] += 1
    
    # 输出分析结果
    print("=== Memcached 日志分析结果 ===")
    print(f"总连接数: {connection_count}")
    print(f"错误日志数: {error_count}")
    print("\n命令执行统计:")
    for cmd, count in command_count.most_common():
        print(f"  {cmd}: {count} 次")
    
    print("\n客户端连接统计 (前10):")
    for client_ip, count in sorted(client_connections.items(), key=lambda x: x[1], reverse=True)[:10]:
        print(f"  {client_ip}: {count} 次连接")

if __name__ == "__main__":
    analyze_memcached_log(LOG_FILE)

Shell 分析脚本

bash
#!/bin/bash
# Memcached 日志分析脚本

LOG_FILE="/var/log/memcached.log"

# 检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; then
    echo "错误:日志文件 $LOG_FILE 不存在"
    exit 1
fi

echo "=== Memcached 日志分析 ==="
echo "分析文件: $LOG_FILE"
echo "分析时间: $(date)"
echo "====================================="

# 1. 基本统计
echo "\n1. 基本统计"
echo "-------------------------------------"
lines=$(wc -l < "$LOG_FILE")
echo "总行数: $lines"

# 2. 命令统计
echo "\n2. 命令执行统计"
echo "-------------------------------------"
grep -o "cmd: [a-z]*" "$LOG_FILE" | sort | uniq -c | sort -nr

# 3. 错误统计
echo "\n3. 错误日志统计"
echo "-------------------------------------"
grep -i "error\|fail\|bad\|invalid\|out of memory" "$LOG_FILE" | sort | uniq -c | sort -nr

# 4. 连接统计
echo "\n4. 连接统计"
echo "-------------------------------------"
connections=$(grep -c "new connection" "$LOG_FILE")
echo "总连接数: $connections"

# 5. 客户端IP统计
echo "\n5. 客户端IP连接排行 (前10)"
echo "-------------------------------------"
grep "new connection: " "$LOG_FILE" | awk -F: '{print $4}' | sort | uniq -c | sort -nr | head -10

# 6. 最近10条错误日志
echo "\n6. 最近10条错误日志"
echo "-------------------------------------"
grep -i "error\|fail\|bad\|invalid\|out of memory" "$LOG_FILE" | tail -10

echo "\n====================================="
echo "分析完成"

集中式日志管理

ELK Stack 配置

  1. Filebeat 配置 (filebeat.yml):

    yaml
    filebeat.inputs:
    - type: log
      enabled: true
      paths:
        - /var/log/memcached.log
      fields:
        service: memcached
        type: memcached_log
      fields_under_root: true
    
    output.elasticsearch:
      hosts: ["localhost:9200"]
    
    setup.kibana:
      host: "localhost:5601"
  2. Logstash 过滤配置

    ruby
    input {
      beats {
        port => 5044
      }
    }
    
    filter {
      if [service] == "memcached" {
        grok {
          match => {
            "message" => "<%{NUMBER:log_level}> %{WORD:event_type}: %{WORD:command} %{DATA:key} %{NUMBER:flags} %{NUMBER:exptime} %{NUMBER:bytes}"
          }
          add_field => { "[@metadata][index_prefix]" => "memcached" }
        }
        
        date {
          match => ["timestamp", "ISO8601"]
          target => "@timestamp"
        }
      }
    }
    
    output {
      elasticsearch {
        hosts => ["localhost:9200"]
        index => "%{[@metadata][index_prefix]}-%{+YYYY.MM.dd}"
      }
    }
  3. Kibana 可视化

    • 创建 Memcached 日志索引模式
    • 设计仪表盘,包含:
      • 命令执行趋势图
      • 错误日志统计
      • 客户端连接分布
      • 内存使用情况

Graylog 配置

  • 使用 GELF 输入接收日志
  • 创建提取器解析 Memcached 日志格式
  • 设计仪表盘展示关键指标

日志分析最佳实践

监控关键指标

命令执行频率

  • 监控 set/get 命令比例,理想情况 get 命令应占多数
  • 高比例 set 命令可能意味着缓存命中率低

错误率

  • 定期检查错误日志,尤其是 out of memorybad data chunk
  • 建立错误率告警,当错误率超过阈值时触发告警

连接模式

  • 监控客户端连接模式,识别异常连接行为
  • 检查是否有大量短连接,可能导致性能问题

性能分析

慢命令检测

  • 在详细日志模式下,分析命令执行时间
  • 识别执行时间长的命令,优化相关业务逻辑

热点键识别

bash
# 识别访问频率高的键
grep -o "retrieving key \|storing key " /var/log/memcached.log | grep -o "key [a-zA-Z0-9_]*" | sort | uniq -c | sort -nr | head -10

缓存命中率分析

bash
# 简单计算命中率(需要详细日志)
gets=$(grep -c "cmd: get" /var/log/memcached.log)
hits=$(grep -c "sending key" /var/log/memcached.log)
misses=$((gets - hits))
if [ $gets -gt 0 ]; then
    hit_rate=$(echo "scale=2; $hits / $gets * 100" | bc)
    echo "命中率: $hit_rate% ($hits/$gets)"
    echo "未命中率: $(echo "scale=2; $misses / $gets * 100" | bc)% ($misses/$gets)"
fi

安全分析

异常连接检测

  • 监控来自异常IP的连接
  • 检测短时间内大量连接的IP(可能是DDoS攻击)

命令注入检测

  • 监控无效命令日志
  • 检测可能的命令注入尝试

敏感数据检测

  • 定期检查日志中是否包含敏感数据
  • 确保日志中不记录密码等敏感信息

常见问题(FAQ)

Q1: Memcached 日志级别有哪些?如何选择?

A1: Memcached 日志级别:

  • 默认:不输出详细日志
  • -v:基本日志,包含连接和错误信息
  • -vv:详细日志,包含命令执行信息
  • -vvv:非常详细日志,包含内部调试信息

选择建议

  • 生产环境:建议使用 -v-vv,平衡日志详细度和性能
  • 调试环境:可使用 -vvv 获取完整调试信息
  • 高并发环境:考虑使用较低日志级别或定期轮换

Q2: 如何降低 Memcached 日志对性能的影响?

A2: 优化建议:

  • 在高并发环境下,使用较低日志级别
  • 配置适当的日志轮换策略
  • 考虑使用二进制协议减少日志输出
  • 将日志存储在高速存储设备上
  • 定期清理旧日志

Q3: 如何从日志中分析缓存命中率?

A3: 分析方法:

bash
# 计算简单命中率
gets=$(grep -c "cmd: get" /var/log/memcached.log)
hits=$(grep -c "sending key" /var/log/memcached.log)
if [ $gets -gt 0 ]; then
    hit_rate=$(echo "scale=2; $hits / $gets * 100" | bc)
    echo "命中率: $hit_rate%"
fi

注意:此方法仅适用于开启详细日志(-vv)的情况,生产环境建议使用监控工具获取更准确的命中率。

Q4: 如何识别 Memcached 中的热点键?

A4: 识别方法:

bash
# 从日志中提取热点键
grep -o "retrieving key \|storing key " /var/log/memcached.log | grep -o "key [a-zA-Z0-9_]*" | sort | uniq -c | sort -nr | head -10

或使用第三方监控工具如 Prometheus + Grafana 进行更实时的热点键监控。

Q5: 日志中出现 "out of memory writing item" 如何处理?

A5: 处理步骤:

  1. 检查 Memcached 内存配置,考虑增加内存容量
  2. 分析缓存策略,是否有过多大对象或过期时间设置不合理
  3. 检查是否存在内存泄漏
  4. 考虑实施数据分片,分散负载
  5. 监控 evictions 指标,调整缓存淘汰策略

Q6: 如何配置集中式日志管理?

A6: 推荐方案:

  1. ELK Stack:Filebeat + Elasticsearch + Kibana
  2. Graylog:配合 Filebeat 或 Rsyslog
  3. Splunk:企业级日志管理解决方案
  4. Loki:轻量级日志聚合系统,配合 Promtail 和 Grafana

配置要点:

  • 确保日志格式统一
  • 配置适当的索引策略
  • 设计合理的仪表盘
  • 建立有效的告警规则