티스토리 뷰
1. Redis란?
Redis는 Key:Value 구조의 비정형 데이터를 저장하고 관리하는 비관계형 데이터베이스이다. 데이터를 디스크가 아닌 메모리에 저장하므로 인메모리 DB(In-Memory DB)라고 부른다.
2. 데이터베이스가 있는데, 왜 Redis를 사용할까?
데이터베이스가 있음에도 Redis를 사용하는 이유는 CPU가 계산에 필요한 데이터를 얻기 위한 속도 차이가 발생하기 때문이다.
데이터베이스는 물리 디스크에 직접 데이터를 쓰기 때문에 서버에 장애가 발생하더라도 데이터가 손실되지 않는다. 그러나 데이터가 필요할 때마다 매번 디스크에 접근하게 되면 사용자가 많아질수록 데이터베이스에 부하가 발생하게 된다. 따라서 사용자가 많을 때 데이터베이스의 과부하를 줄이기 위해서 Redis와 같은 캐시 서버를 도입할 수 있다. (RAM은 전기적 신호로 데이터를 처리하기 때문에 물리적 이동이 없어 속도가 빠르지만, 디스크는 기계적인 회전과 헤드의 이동이 발생하므로 데이터를 읽고 쓰는 시간이 상대적으로 느리다.)
캐시는 한 번 읽어온 데이터를 임의의 공간에 저장하여, 다음에 동일한 데이터가 필요할 때 디스크가 아닌 캐시 서버에 저장된 데이터를 반환한다. 즉, 매번 동일한 데이터에 접근할 때 디스크가 아닌 메모리에 저장된 데이터를 가져오는 것이다.
→ 위 사진을 보면 디스크(HDD)와 메모리(RAM) 간의 속도 차이가 매우 크다.
3. Redis의 특징
- Key:Value 구조이므로 쿼리를 사용하지 않는다.
- In-Memory 구조이므로 데이터를 디스크가 아닌 메모리에서 처리하여 속도가 빠르다. 그러나 메모리 기반이기 때문에 메모리 용량이 제한적이다.
- strings, sets, lists, hashes, sorted sets, hyperloglog, … 지원
- 싱글 스레드이면서 멀티 스레드이다. → 4번에서 확인한다.
4. Redis는 싱글 스레드이면서 멀티 스레드이다
Redis는 기본적으로 싱글 스레드 기반으로 동작하기 때문에 원자성(Atomicity)을 보장한다. 원자성을 보장하기 때문에 하나의 명령을 처리하는 동안 다른 명령이 끼어들 수 없어, 데이터 일관성을 쉽게 유지할 수 있다.
Redis 6.0부터 네트워크 I/O에 대해서 멀티 스레드를 사용한다. 사용자가 많은 시스템에서 네트워크 I/O는 시스템의 병목 현상을 일으킬 수 있다. 왜냐하면 동시에 많은 요청이 몰렸을 때 Redis가 네트워크 I/O 처리에서 시간을 많이 소모하기 때문이다. 이는 Redis 서버의 전체적인 성능 저하로 연결될 수 있다. 이를 해결하기 위해 Redis 6.0부터 네트워크 I/O에 한해서 멀티 스레드로 처리할 수 있는 기능을 도입했다.
5. 사용자 요청이 많을 때, 싱글 스레드로 좋은 성능을 낼 수 있을까?
Redis는 클라이언트의 요청을 싱글 스레드로 처리하지만, 비동기 I/O 방식을 사용하여 성능을 극대화한다. Redis는 이벤트 루프(Event Loop) 기반으로 동작하는데, 클라이언트의 요청이 들어오면 이를 Task Queue에 저장한다. 요청을 처리하는 동안 I/O 작업이 발생하면 Redis는 해당 I/O를 기다리지 않고 다른 클라이언트의 요청을 처리한다. I/O 작업이 완료되면 그 결과를 클라이언트에게 전달하는 방식으로 효율적으로 작업을 병렬 처리하는 효과를 낸다.
따라서 Redis는 싱글 스레드임에도 불구하고 비동기 처리 덕분에 높은 처리량과 빠른 응답 시간을 유지하며, 다수의 클라이언트 요청을 지연 없이 처리할 수 있다.
6. Redis 6.0에서 여러 개의 요청을 동시에 처리하는 방법
Redis 6.0 이전에는 싱글 스레드로 동작하면서 모든 작업을 하나의 스레드로 처리하면서 다음 문제가 발생했다.
- 네트워크 I/O 지연 : 클라이언로부터 데이터를 읽거나, 데이터를 보내는 과정인 네트워크 I/O에서 시간이 많이 걸리는 경우가 있다. 예를 들어, 클라이언트의 네트워크 문제로 인해 데이터 전달이 지연된다면 Redis는 해당 시간 동안 다른 작업을 할 수 없다.
- 비효율적인 리소스 사용 : 싱글 스레드로 네트워크 I/O와 명령어 처리를 해야 하므로, 실제 데이터 처리 시간보다 네트워크 I/O를 처리하는 데 시간을 더 사용하게 될 수도 있다. 만약 네트워크 I/O 처리 시간이 긴 경우 CPU는 네트워크 작업이 끝날 때까지 기다리면서 유휴 상태가 된다.
위 문제는 네트워크 I/O와 명령어 처리를 싱글 스레드가 담당하면서 발생하는 문제이다. Redis 6.0부터는 멀티 스레드를 도입하여 네트워크 I/O는 멀티 스레드로 처리하고, 명령어 처리는 싱글 스레드로 처리하도록 변경되었다.
- 여전히 원자성(Atomicity) 보장 : 네트워크 I/O 처리만 멀티 스레드가 할 뿐, 명령어 처리는 싱글 스레드가 하므로 원자성을 보장한다.
- 여러 클라이언트의 요청 처리 속도 향상 : 네트워크 I/O를 멀티 스레드가 처리하기 때문에, 여러 클라이언트의 요청을 동시에 처리하고 응답을 내려줄 수 있다.
결론은 싱글 스레드로 사용자의 요청을 처리하기 때문에 원자성을 보장하고, 멀티 스레드로 네트워크 통신을 처리하므로 I/O 병목 현상을 해결하면서 명령어 처리에 더 많은 시간을 할애할 수 있어 전체 성능이 향상된다.
7. Redis가 여러 사용자의 요청을 처리하는 과정
- 클라이언트의 요청 도착 (멀티 스레드)
- Client A, B, C가 동시에 Redis 서버로 요청을 보낸다.
- Redis는 이 세 요청을 멀티 스레드로 처리한다. 즉, 클라이언트로부터 요청 데이터를 읽어오는 과정이 병렬로 처리된다.
- 멀티 스레드 덕분에 클라이언트의 요청이 Redis 서버로 도착하는 시간을 줄일 수 있다.
- 이벤트 루프로 요청 전달 (싱글 스레드)
- 클라이언트의 요청은 이벤트 루프(Event Loop)로 전달된다.
- 이벤트 루프는 싱글 스레드로 동작하며, 들어온 클라이언트의 요청을 순서대로 Task Queue(작업 큐)에 저장한다.
- 이벤트 루프는 클라이언트의 요청이 단순히 처리 대기 상태로 들어가는 것일뿐, 실제 명령어가 실행된 것이 아니다.
- Task Queue에 저장된 명령어 처리 (싱글 스레드)
- Task Queue에 저장된 명령어들은 Redis의 싱글 스레드가 하나씩 순차적으로 처리한다.
- Redis는 모든 명령을 순차적으로 처리하기 때문에, 하나의 명령이 처리되는 동안 다른 명령은 대기하게 된다. 이를 통해 원자성(Atomicity)이 보장된다.
- 클라이언트에 응답 전송 (멀티 스레드)
- 사용자의 요청을 처리하고 Redis는 클라이언트에게 결과를 응답으로 전송한다.
- 네트워크 I/O 작업은 멀티 스레드로 처리되므로 Client A, B, C에게 동시에 응답을 보낼 수 있다.
- 여러 클라이언트가 동시에 응답을 받을 수 있으므로 응답 지연 시간이 최소화된다.