인생을 코딩하다.

[Redis] Redis Pipeline 본문

DataBase

[Redis] Redis Pipeline

Hyung1 2023. 10. 3. 00:53
728x90
반응형

Redis Pipeline에 설명하기에 앞서 No Pipeline에 관해 간략하 말씀드리겠습니다.

redis는 요청을 보낼 때 일반적으로 아래와 같이 수행됩니다.

  • 클라이언트는 서버에 쿼리를 보내고 일반적으로 차단 방식으로 소켓에서 서버 응답을 읽습니다.
  • 서버는 명령을 처리하고 클라이언트에 응답을 다시 보냅니다.

Redis는 클라이언트-서버 모델과 요청/응답 프로토콜을 사용하는 TCP 서버이며, 클라이언트가 서버에 명령을 보내고 응답을 받는 구조입니다.

위 과정은 클라이언트가 연속적으로 많은 요청을 보내야 하는 경우 RTT가 길어져 성능에 악영향을 미칠 수 있습니다. 예를 들어 RTT가 250밀리초인 경우, 초당 최대 4개의 요청만 처리할 수 있습니다. 만약 요청이 1000개라면 엄첨 느리겠죠?
 
위와 같이 RTT가 길어지는 상황을 개선하기 위해서는 어떤 방법이 존재할까요? 네트워크 연결을 최적화하고, 명령을 일괄 처리하는 파이프라인으로 전송하는 방법을 고려할 수 있습니다. 이러한 최적화는 Redis 성능을 향상시키는 데 도움이 됩니다.

* RTT ->  클라이언트가 명령을 보내고 서버가 응답을 반환하는 데 걸리는 시간, RTT는 네트워크 연결의 속도에 따라 다르며 인터넷을 통한 연결이 느릴수록 RTT가 길어짐

Redis Pipeline이란?

Redis Pipeline은 개별 명령에 대한 응답을 기다리지 않고 한 번에 여러 명령을 실행하여 성능을 향상시키는 기술입니다. 

Pipeline 동작 과정 

  1.   Redis 클라이언트와 서버 연결: 먼저 Redis 클라이언트가 Redis 서버에 연결합니다. 클라이언트와 서버 간의 TCP/IP 연결이 설정됩니다.
  2. 파이프라인 시작: 클라이언트는 파이프라인을 시작합니다. 이때, 여러 개의 Redis 명령을 파이프라인에 추가할 수 있습니다.
  3. 여러 명령 추가: 클라이언트는 파이프라인에 여러 개의 Redis 명령을 추가합니다. 이 명령들은 일련의 작업을 수행하는데 사용됩니다. 예를 들어, SET, GET, HSET, HGET 등 다양한 명령이 추가될 수 있습니다.
  4. 파이프라인 실행: 파이프라인에 추가된 명령들은 클라이언트가 EXECUTE 또는 SEND 명령을 호출하기 전까지 서버로 전송되지 않습니다.
  5. 서버로 명령 일괄 전송: 클라이언트가 EXECUTE 또는 SEND 명령을 호출하면, 파이프라인에 추가된 모든 명령이 일괄로 Redis 서버로 전송됩니다. 이때, 네트워크 오버헤드를 최소화하기 위해 모든 명령이 한 번에 전송됩니다.
  6. 서버에서 명령 처리: Redis 서버는 파이프라인에 추가된 모든 명령을 받아 처리합니다. 명령들은 순서대로 처리되며, 각 명령의 결과가 서버에서 생성됩니다.
  7. 클라이언트 응답 수신: Redis 서버는 명령을 순서대로 실행하고, 각 명령의 결과를 클라이언트에게 응답으로 보냅니다. 클라이언트는 이 응답을 받아 처리합니다.
  8. 파이프라인 결과 반환: 클라이언트가 파이프라인 실행 결과를 수신하면, 각 명령의 결과를 추출하여 사용자나 응용 프로그램에 반환합니다. 결과는 명령이 실행된 순서대로 반환됩니다.
  9. 파이프라인 종료: 파이프라인은 실행이 완료된 후 종료됩니다. 클라이언트는 필요한 경우 다시 파이프라인을 시작할 수 있습니다.

이러한 과정을 통해 Redis 파이프라인은 여러 명령을 일괄로 처리하여 네트워크 오버헤드를 줄이고 성능을 향상시킬 수 있습니다.
 
그림은 notion mermaid로 그렸고 mermaid code는 아래 더보기 참고 부탁드립니다.

더보기

graph TD;

subgraph "파이프라인 사용"
A[연결 설정]
B[파이프라인 시작]
C[여러 명령 추가]
D[파이프라인 실행]
E[클라이언트 응답 수신]
F[파이프라인 결과 반환]
G[파이프라인 종료]
end

subgraph "파이프라인 미사용"
H[연결 설정]
I[명령1 실행]
J[명령2 실행]
K[명령3 실행]
L[명령4 실행]
M[명령5 실행]
N[클라이언트 응답 수신]
end

A -->|연결 요청| B
B -->|파이프라인 시작| C
C -->|여러 명령 추가| C
C -->|명령1 실행| C
C -->|명령2 실행| C
C -->|명령3 실행| C
C -->|명령4 실행| C
C -->|명령5 실행| D
D -->|파이프라인 실행| E
E -->|클라이언트 응답| F
F -->|파이프라인 결과 반환| G
G -->|파이프라인 종료| N
H -->|연결 요청| I
I -->|명령1 실행| J
J -->|명령2 실행| K
K -->|명령3 실행| L
L -->|명령4 실행| M
M -->|명령5 실행| N
N -->|클라이언트 응답| N

어떤 상황에서 사용하나요?

  1. 성능 향상: Redis 파이프라인을 사용하면 여러 Redis 명령을 한 번에 보내서 네트워크 오버헤드를 줄일 수 있습니다. 이는 대량의 작업을 수행해야 할 때 특히 유용합니다.
  2. 원자성 보장: 파이프라인을 사용하면 여러 명령을 하나의 트랜잭션으로 처리할 수 있으므로 Redis 서버는 이러한 명령을 원자적으로 실행합니다. 이로써 여러 개의 명령이 중간에 실패하더라도 이전 상태로 롤백되지 않고 일괄 처리됩니다.
  3. 비동기 작업: Redis 파이프라인은 명령을 일괄로 보내기 때문에 클라이언트에서 서버로 대량의 작업을 비동기적으로 보낼 때 유용합니다. 이를 통해 작업을 병렬로 처리하고 응답을 기다리는 시간을 줄일 수 있습니다.
  4. 대량 데이터 처리: Redis 파이프라인은 대량의 데이터를 처리할 때 유용합니다. 예를 들어, 여러 개의 값을 한 번에 쓰거나 읽어야 하는 경우에 사용할 수 있습니다.
  5. 실행 속도 향상: 일련의 연관된 작업을 한 번에 실행하려면 파이프라인을 사용하여 작업의 전체 성능을 향상시킬 수 있습니다.
  6. 실시간 분석 및 통계: Redis는 실시간 데이터 분석 및 통계에 매우 적합한 데이터베이스입니다. 파이프라인을 사용하여 여러 명령을 조합하여 실시간으로 데이터를 분석하고 집계할 수 있습니다.

그러나 파이프라인을 사용할 때 주의할 점이 있습니다. 파이프라인에서 여러 개의 명령을 보낸 경우, 그 명령들은 동시에 실행되기 때문에 순서가 중요하다는 점을 고려해야 합니다.

아래는 간단한 예제 코드 입니다. 얼마나 성능 최적화가 가능한지 직접 실험해보세요ㅎㅎ

.. 생략

val result = redisTemplate.executePipelined(RedisCallback<Any> { connection ->
        val keySerializer: RedisSerializer<String> = StringRedisSerializer()
        val valueSerializer: RedisSerializer<String> = StringRedisSerializer()

        connection.openPipeline()
		
        val keyToValue = mapOf(
            "a" to "1",
            "b" to "2",
            "c" to "3"
        )

        keyValues.forEach { (key, value) ->
            connection.set(keySerializer.serialize(key), valueSerializer.serialize(value))
        }

        connection.closePipeline()
        null
    })

.. 생략

 

728x90
반응형
Comments