const Redis = require('ioredis');
const redis = new Redis({
host: 'localhost',
port: 6379,
});
class RedisMemoryAnalyzer {
constructor(redisClient, batchSize = 50) {
this.redis = redisClient;
this.batchSize = batchSize;
this.topKeys = [];
}
async getTopMemoryKeys(topN = 10) {
const stream = this.redis.scanStream({
match: '*',
count: this.batchSize
});
let processedCount = 0;
let totalKeys = 0;
await new Promise((resolve, reject) => {
stream.on('data', async (keys) => {
stream.pause();
if (keys.length > 0) {
await this.processKeyBatch(keys);
processedCount += keys.length;
totalKeys += keys.length;
console.log(`Processed ${processedCount} keys...`);
}
stream.resume();
});
stream.on('end', resolve);
stream.on('error', reject);
});
console.log(`Total keys processed: ${totalKeys}`);
return this.topKeys.slice(0, topN);
}
async processKeyBatch(keys) {
const batchPromises = keys.map(async (key) => {
try {
const usage = await this.redis.call('MEMORY', 'USAGE', key);
const keyType = await this.redis.type(key);
return {
key,
usage: parseInt(usage),
type: keyType
};
} catch (err) {
console.error(`Error processing key ${key}:`, err.message);
return null;
}
});
const results = (await Promise.all(batchPromises)).filter(Boolean);
this.topKeys = [...this.topKeys, ...results]
.sort((a, b) => b.usage - a.usage)
.slice(0, 10);
}
async disconnect() {
await this.redis.disconnect();
}
}
async function main() {
const analyzer = new RedisMemoryAnalyzer(redis, 50);
try {
const topKeys = await analyzer.getTopMemoryKeys(10);
console.log('\n=== Top 10 Memory Consuming Keys ===');
topKeys.forEach((item, index) => {
const sizeKB = item.usage / 1024;
const sizeMB = sizeKB / 1024;
console.log(`${index + 1}. ${item.key}`);
console.log(` Size: ${item.usage} bytes (${sizeKB.toFixed(2)} KB${sizeMB > 1 ? `, ${sizeMB.toFixed(2)} MB` : ''})`);
console.log(` Type: ${item.type}`);
console.log('---');
});
} finally {
await analyzer.disconnect();
}
}
main().catch(console.error);