QDAirPortBackend0122/scripts/redis_memory_diagnosis.sh
2026-01-22 13:19:47 +08:00

150 lines
4.8 KiB
Bash

#!/bin/bash
# Redis 内存使用诊断脚本
# 分析 Redis 内存占用情况并提供优化建议
echo "=========================================="
echo "Redis 内存使用诊断报告"
echo "=========================================="
echo "生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# 检查 Redis 是否运行
if ! redis-cli ping > /dev/null 2>&1; then
echo "错误: Redis 未运行或无法连接"
exit 1
fi
echo "1. 内存使用概览"
echo "----------------------------------------"
redis-cli INFO memory | grep -E "used_memory_human|used_memory_peak_human|maxmemory_human|mem_fragmentation_ratio|used_memory_rss_human"
echo ""
echo "2. 配置信息"
echo "----------------------------------------"
redis-cli CONFIG GET maxmemory | xargs printf "最大内存限制: %s = %s\n"
redis-cli CONFIG GET maxmemory-policy | xargs printf "淘汰策略: %s = %s\n"
echo ""
echo "3. 数据库统计"
echo "----------------------------------------"
printf "Key 总数: "
redis-cli DBSIZE
echo ""
echo "4. 各类型 Key 数量分布"
echo "----------------------------------------"
echo "注意: 此操作可能耗时较长..."
# 统计不同前缀的 key 数量
declare -A key_counts
while IFS= read -r key; do
prefix=$(echo "$key" | cut -d':' -f1)
((key_counts[$prefix]++))
done < <(redis-cli --scan)
echo "前缀统计:"
for prefix in "${!key_counts[@]}"; do
printf " %-30s %d 个\n" "$prefix:" "${key_counts[$prefix]}"
done | sort -k2 -rn | head -20
echo ""
echo "5. 最大的 Key (Top 10)"
echo "----------------------------------------"
redis-cli --bigkeys 2>/dev/null | grep -A 20 "Biggest"
echo ""
echo "6. 没有过期时间的 Key 统计"
echo "----------------------------------------"
no_ttl_count=0
total_keys=$(redis-cli DBSIZE | awk '{print $1}')
# 采样检查 (最多检查1000个key)
sample_size=1000
checked=0
no_ttl_samples=""
while IFS= read -r key && [ $checked -lt $sample_size ]; do
ttl=$(redis-cli TTL "$key")
if [ "$ttl" = "-1" ]; then
((no_ttl_count++))
if [ $no_ttl_count -le 5 ]; then
no_ttl_samples="$no_ttl_samples\n $key"
fi
fi
((checked++))
done < <(redis-cli --scan)
if [ $checked -ge $total_keys ]; then
echo "检查了全部 $total_keys 个 key"
echo "没有过期时间的 key: $no_ttl_count 个 ($(awk "BEGIN {printf \"%.1f\", $no_ttl_count/$total_keys*100}")%)"
else
echo "采样检查了 $checked 个 key (总数: $total_keys)"
echo "采样中没有过期时间的 key: $no_ttl_count 个 ($(awk "BEGIN {printf \"%.1f\", $no_ttl_count/$checked*100}")%)"
fi
if [ $no_ttl_count -gt 0 ]; then
echo -e "示例:$no_ttl_samples"
fi
echo ""
echo "7. 内存使用率评估"
echo "----------------------------------------"
used_memory=$(redis-cli INFO memory | grep "used_memory:" | cut -d':' -f2 | tr -d '\r')
max_memory=$(redis-cli CONFIG GET maxmemory | tail -1 | tr -d '\r')
if [ "$max_memory" = "0" ]; then
echo "⚠️ 警告: 未设置最大内存限制!"
echo " 建议: 执行 ./configure_redis_memory.sh 设置内存限制"
else
usage_percent=$(awk "BEGIN {printf \"%.1f\", $used_memory/$max_memory*100}")
echo "内存使用率: $usage_percent%"
if (( $(echo "$usage_percent > 90" | bc -l) )); then
echo "🔴 严重: 内存使用率超过 90%!"
echo " 建议: 立即清理缓存或增加内存限制"
elif (( $(echo "$usage_percent > 80" | bc -l) )); then
echo "🟡 警告: 内存使用率超过 80%"
echo " 建议: 清理不必要的缓存"
elif (( $(echo "$usage_percent > 70" | bc -l) )); then
echo "🟢 正常: 内存使用率在合理范围内"
else
echo "🟢 良好: 内存使用率较低"
fi
fi
echo ""
echo "8. 优化建议"
echo "----------------------------------------"
# 检查是否设置了最大内存
if [ "$max_memory" = "0" ]; then
echo "✓ 设置 Redis 最大内存限制"
echo " 命令: redis-cli CONFIG SET maxmemory 512mb"
fi
# 检查是否有很多没有过期时间的 key
if [ $no_ttl_count -gt $(($checked / 2)) ]; then
echo "✓ 大量 key 没有过期时间,建议设置 TTL"
echo " 自动修复: 应用中的 RedisCacheCleanupTask 会自动处理"
fi
# 检查淘汰策略
eviction_policy=$(redis-cli CONFIG GET maxmemory-policy | tail -1 | tr -d '\r')
if [ "$eviction_policy" = "noeviction" ]; then
echo "✓ 当前淘汰策略为 noeviction,建议改为 volatile-lru"
echo " 命令: redis-cli CONFIG SET maxmemory-policy volatile-lru"
fi
echo ""
echo "=========================================="
echo "诊断完成!"
echo "=========================================="
echo ""
echo "快速操作:"
echo "1. 配置 Redis 内存: ./configure_redis_memory.sh"
echo "2. 清理 Redis 数据: redis-cli FLUSHDB (谨慎!)"
echo "3. 重启应用以应用新的缓存策略"