redis中KEYS和SCAN命令对比
根据官方文档所述,KEYS命令时间复杂度是O(N),耗费时间很少,在笔记本上扫描100w个key的数据库使用了40ms。不推荐在实际生产环境使用KEYS命令,在数据量比较大的情况下执行该命令会有很大的性能损耗,这个命令是用来调试和其他特殊用途,比如,改变key空间的布局。在正式应用代码里使用SCAN和sets代替KEYS。
KEYS就是将redis中所有的key与KEYS参数一一匹配,但是这个匹配过程对服务器性能有很大的损耗。而SCAN则是将所有key分页处理,每次处理的条数通过参数传入。处理后返回一个游标,下次再次请求的时候携带该游标。
下面对KEYS和SCAN的性能和使用方式进行对比。
测试环境:
server:redis_version:5.0.2 运行在docker容器里,宿主机配置 i7-6700K,IP为192.168.1.160。image: redis:alpine, cpu默认配置可以占满一个核,映射出来的端口为6380。
client: ubuntu16.04 编程语言为node.js v10.15.0,依赖库 redis: 2.8.0,uuid: 3.3.2。
首先我先在redis中插入100w条数据。代码如下:
const { v4 } = require('uuid');
const { createClient } = require('redis');
const client = createClient({
host: '192.168.1.160',
port: 6380,
});
const set = (key) => {
return new Promise((resolve, reject) => {
client.set(key, key, (err, reply) => {
if (err)
reject(err);
return resolve();
});
});
};
let uuidList = [];
const run = async () => {
const start = new Date().getTime();
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 10000; i++) {
uuidList.push(v4());
}
await Promise.all(uuidList.map(uuid => {
return set(uuid);
}));
uuidList = [];
}
console.log('duration', new Date().getTime() - start);
};
run();
接下来测试KEYS查询,代码如下:
const { createClient } = require('redis');
const client = createClient({
host: '192.168.1.160',
port: 6380,
});
const start = new Date().getTime();
client.keys('*ccccc*', (err, reply) => {
const duration = new Date().getTime() - start;
console.log('duration');
console.log(reply.sort());
});
搜索到的结果为:
[ '2aaf224c-a31f-4555-8269-ccccc4b68244',
'317c73a2-40ef-4084-a7fa-8c2ccccc8817',
'3ccccc6b-bd15-4cc1-b857-2eb2ee35c147',
'8b4ce8df-17d5-40f5-a44a-86971f9ccccc',
'a8de5fe2-181a-4628-80dc-eccccc613f9f',
'a981dea3-1d9b-4038-a2f6-7ccccc9e88e1',
'd8ccccc5-3c5b-4bf8-b73b-c6c45c8ce41f',
'f137ad17-5b21-43d1-a77c-2eae5cccccdd' ]
平均耗时为:310ms。
接下来测试SCAN查询,代码如下:
const { createClient } = require('redis');
const client = createClient({
host: '192.168.1.160',
port: 6380,
});
const scan = (cursor, keys, count) => {
return new Promise((reslove, reject) => {
client.scan(cursor, 'MATCH', keys, 'COUNT', count, (err, res) => {
if (err) {
reject(err);
}
const [cursor, matched] = res;
return reslove({ cursor, matched });
});
});
};
const run = async () => {
const result = [];
const start = new Date().getTime();
let cursor = 0;
const keys = '*ccccc*';
while (true) {
const { matched, cursor: newCursor } = await scan(cursor, keys, process.argv[2]);
result.push(...matched);
cursor = newCursor;
if (cursor === '0') {
console.log('duration', new Date().getTime() - start);
break;
}
}
console.log(result.sort());
};
run();
COUNT分页大小与平均耗时:
大小 | 耗时 |
---|---|
1k | 2790ms |
1w | 664ms |