查看 Redis 某个数据库的内存占用
查看 Redis 某个数据库的内存占用
·
1. 创建一个 db_memory_size.lua 脚本文件
local db = tonumber(ARGV[1]) or 0
local cursor = tonumber(ARGV[2]) or 0
local batch_size = 10000
-- 切换到指定 db
redis.call("SELECT", db)
local reply = redis.call("SCAN", cursor, "COUNT", batch_size)
local new_cursor = tonumber(reply[1])
local keys = reply[2]
local total_memory = 0
local processed_keys = #keys -- 使用本次扫描到的 key 数量
for _, key in ipairs(keys) do
local success, mem_usage = pcall(redis.call, "MEMORY", "USAGE", key)
if success then
total_memory = total_memory + mem_usage
end
end
-- 返回单行格式
if new_cursor == 0 then
return "COMPLETED|" .. new_cursor .. "|" .. total_memory .. "|" .. processed_keys
else
return "IN_PROGRESS|" .. new_cursor .. "|" .. total_memory .. "|" .. processed_keys
end
2. 创建 db_memory_size.sh 脚本文件
#!/bin/bash
# 默认参数
REDIS_CLI="/home/redis/redis-5.0.3/src/redis-cli"
HOST="localhost"
PORT="6379"
PASSWORD=""
DB="0"
# 显示用法
usage() {
echo "用法: $0 [-h host] [-p port] [-a password] [-d db] [-c redis_cli_path]"
echo "示例:"
echo " $0 -h 10.10.10.1 -p 20001 -a 123456 -d 2"
echo " $0 -h redis.example.com -p 6379 -d 1"
echo " $0 -h 10.10.10.1 -p 20001 -a 123456 -d 2 -c /usr/local/bin/redis-cli"
exit 1
}
# 解析命令行参数
while getopts "h:p:a:d:c:" opt; do
case $opt in
h) HOST="$OPTARG" ;;
p) PORT="$OPTARG" ;;
a) PASSWORD="$OPTARG" ;;
d) DB="$OPTARG" ;;
c) REDIS_CLI="$OPTARG" ;;
*) usage ;;
esac
done
# 检查redis-cli是否存在
if [ ! -x "$REDIS_CLI" ]; then
echo "错误: redis-cli 不存在或不可执行: $REDIS_CLI"
exit 1
fi
# 初始化变量
cursor=0
total_memory=0
total_keys=0
batch_count=0
echo "开始分析 Redis 内存使用情况..."
echo "主机: $HOST, 端口: $PORT, 数据库: $DB"
echo "=========================================="
# 构建redis-cli命令基础参数
REDIS_CMD="$REDIS_CLI -h $HOST -p $PORT"
if [ -n "$PASSWORD" ]; then
REDIS_CMD="$REDIS_CMD -a $PASSWORD"
fi
# 测试连接
test_result=$($REDIS_CMD -n $DB ping 2>/dev/null)
if [ "$test_result" != "PONG" ]; then
echo "错误: Redis连接失败,请检查参数"
echo "尝试的命令: $REDIS_CMD -n $DB ping"
exit 1
fi
# 先显示基本信息
key_count=$($REDIS_CMD -n $DB dbsize 2>/dev/null)
echo "数据库 $DB 总Key数量: $key_count"
if [ "$key_count" -eq 0 ]; then
echo "数据库 $DB 为空,无需分析"
exit 0
fi
while true; do
# 执行 Lua 脚本
if [ -n "$PASSWORD" ]; then
result=$($REDIS_CMD --eval db_memory_size.lua , $DB $cursor 2>/dev/null)
else
result=$($REDIS_CMD --eval db_memory_size.lua , $DB $cursor 2>/dev/null)
fi
# 检查是否得到有效结果
if [ -z "$result" ]; then
echo "未获取到结果,可能脚本执行失败,重试中..."
sleep 2
continue
fi
# 解析单行格式: STATUS|CURSOR|MEMORY|KEYS
IFS='|' read -r status cursor batch_memory batch_keys <<< "$result"
if [ -n "$status" ] && [ -n "$cursor" ] && [ -n "$batch_memory" ] && [ -n "$batch_keys" ]; then
total_memory=$((total_memory + batch_memory))
total_keys=$((total_keys + batch_keys))
batch_count=$((batch_count + 1))
# 显示进度(每10批显示一次)
if [ $((batch_count % 10)) -eq 0 ] || [ "$status" = "COMPLETED" ] || [ "$cursor" -eq 0 ]; then
progress=$(echo "scale=2; $total_keys * 100 / $key_count" | bc)
echo "批次: $batch_count, 进度: $progress%, 已处理: $total_keys/$key_count keys"
fi
# 检查是否完成
if [ "$status" = "COMPLETED" ] || [ "$cursor" -eq 0 ]; then
break
fi
else
echo "解析失败: $result,重试中..."
sleep 2
continue
fi
# 添加延迟避免服务器压力过大
sleep 0.2
done
echo "=========================================="
if [ "$total_keys" -gt 0 ]; then
total_memory_mb=$(echo "scale=2; $total_memory / (1024 * 1024)" | bc)
avg_size=$(echo "scale=2; $total_memory / $total_keys" | bc)
memory_human=$(echo "scale=2; $total_memory_mb / 1024" | bc)
echo "数据库 $DB 内存使用统计完成:"
echo "总Key数量: $total_keys"
echo "总内存使用: $total_memory_mb MB (${memory_human} GB)"
echo "平均每个Key: $(echo "scale=2; $avg_size / 1024" | bc) KB"
echo "处理批次: $batch_count"
echo "分析时间: $(date)"
else
echo "分析完成,但未处理任何key"
fi
3. 调用执行
$chmod 755 db_memory_size.sh
$./db_memory_size.sh -h 10.10.10.1 -p 20001 -a 123456 -d 1
开始分析 Redis 内存使用情况...
主机: 10.10.10.1, 端口: 20001, 数据库: 1
==========================================
数据库 1 总Key数量: 7657244
批次: 10, 进度: 1.28%, 已处理: 98718/7657244 keys
批次: 20, 进度: 2.57%, 已处理: 197402/7657244 keys
批次: 30, 进度: 3.86%, 已处理: 296088/7657244 keys
...
批次: 750, 进度: 96.62%, 已处理: 7399172/7657244 keys
批次: 760, 进度: 97.91%, 已处理: 7497815/7657244 keys
批次: 766, 进度: 98.66%, 已处理: 7555309/7657244 keys
==========================================
数据库 1 内存使用统计完成:
总Key数量: 7555309
总内存使用: 1127.85 MB (1.10 GB)
平均每个Key: .15 KB
处理批次: 766
分析时间: Tue Sep 16 09:51:12 CST 2025
$
执行脚本的输出有两个问题:
- 两次“总Key数量”不一样。
- 最后一次的进度没到 100%。
这不是循环最后一次没有输出的问题,而是确实扫描到的 key 数量(7,555,309)少于 dbsize 报告的 key 数量(7657244)。这是 Redis SCAN 命令的预期行为。
(1)SCAN 与 DBSIZE 的差异
- DBSIZE 命令返回的是当前时刻数据库中的 key 总数。
- SCAN 命令在遍历过程中,数据库可能发生变化(key 过期、新增、删除)。
(2)产生差异的具体原因
- key 过期:在 SCAN 遍历过程中,有些 key 可能过期被自动删除。
- 时间窗口:SCAN 遍历需要时间,在这期间数据库状态可能变化。
- SCAN 特性:SCAN 不保证返回所有 key,特别是在有大量 key 变动时。
脚本工作正常,输出结果也是正确的。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)