redis中KEYS和SCAN命令對比

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章