Redis는 단일 스레드(single-threaded) 기반으로 동작한다!
레디스는 단일 스레드를 기반으로 동작한다.
즉, 한번에 하나의 요청만 가능하다. 이는 하나의 명령을 처리하는 동안 다른 명령은 대기 상태로 머무르게 된다는 것을 말한다.
Redis 명령어 처리 과정
이 부분에서 내부 처리 과정을 이해해보는 것도 좋을 것 같다.
Redis는 클라이언트의 요청을 처리할 때 두 가지 주요 단계를 거친다.
processInputBuffer 단계
Redis는 클라이언트로부터 들어오는 데이터를 입력 버퍼(input buffer)에 저장하고, 클라이언트가 보낸 패킷이 하나의 완전한 명령어로 완성될 때까지 이 버퍼에 쌓이게 된다.
processCommand 단계
패킷이 완전한 명령어로 완성되면, Redis는 processCommand를 호출하여 해당 명령을 실제로 실행한다.
즉, 명령이 완전히 실행될 때까지 패킷으로 대기하게 된다. 그런 이유로 이 단계에서 명령어의 시간 복잡도가 실행 시간에 직접적인 영향을 미치게 된다.
만약 시간 복잡도가 O(N)인 명령어를 실행하면 데이터 크기에 비례하여 처리 시간이 길어지고, 이로 인해 Redis가 다른 요청을 처리하지 못하는 차단(blocking) 현상이 발생하게 된다.
자주 접하게 되는 O(N) 관련 명령어
1. keys
특정 패턴에 맞는 모든 키를 검색한다. 전체 키를 스캔하기 때문에 O(N)의 시간 복잡도를 가진다.
데이터베이스 크기가 클수록 검색 시간이 길어지는 문제가 있다.
2. DEL
(대량 삭제 시)
여러 키를 한꺼번에 삭제할 때 데이터 크기에 따라 처리 시간이 길어진다.
3. FLUSHALL
/ FLUSHDB
Redis의 모든 데이터 또는 특정 DB의 데이터를 삭제한다.
대안 : 비차단적 명령어를 사용하자!
keys 대신 SCAN
사용
SCAN
명령어는 데이터베이스 전체를 한 번에 검색하지 않고, 일부씩 나누어 반환하는 비차단(non-blocking) 명령이다.
let cursor = '0';
do {
const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', 'prefix:*', 'COUNT', 10);
cursor = nextCursor;
} while (cursor !== '0');
전체 데이터를 한 번에 처리하지 않고, 포인터를 이용해 부분적으로 데이터를 나누어 반환한다.
이렇게 분할 작업을 수행하기 때문에 검색 도중에도 다른 명령어를 처리할 수 있다.
DEL 대신 UNLINK
사용
DEL 명령어는 키 삭제 작업이 완료될 때까지 블로킹한다.
반면에, UNLINK는 Redis 서버의 백그라운드 쓰레드에서 데이터를 삭제하기 때문에 비동기로 처리된다.
await redis.unlink(...keys)
'Backend > Redis' 카테고리의 다른 글
Redis 메모리 초과로 서버 다운된 경험과 해결 (2) | 2024.12.01 |
---|---|
Redis HINCRBY로 동시성 문제 해결: 실시간 베팅 (0) | 2024.11.19 |