DBMS/Redis

Redis에서 트랜잭션 처리 방법

반응형

첫 번째 방법


Redis에서 트랜잭션을 위한 커맨드를 제공한다.

(MULTI/EXEC/DISCARD/WATCH)

  • MUlTI: 트랜잭션 진입
  • EXEC: 커밋
  • DISCARD: 롤백

Watch의 경우 get & set과 같이 락이 필요한 경우, WATCH 명령을 통해 낙관적 락을 사용할 수 있음.

  • WATCH 사용 시, Race Condition이 발생하면, 트랜잭션이 실패해서 재처리가 필요함.

한계점

1. 해당 명령을 통해, 트랜잭션 내의 중간 명령의 결과를 받아서, 다음 명령에 대한 분기처리를 할 수 없다

  • 무슨 말 이냐면, 예를 들어서 다음과 같이 incr 명령의 결괏값에 따른 분기 처리 등을 할 수 없는 한계가 존재한다
# 수도 코드
if (incr, "key" > 1 ) {
   pexpire("key", 1000)
} 

2. Redis Transaction 커맨드의 경우 Redis Cluster Mode에서는 지원하지 않음.

  • 관련 내용: https://stackoverflow.com/a/42091605
  • Redis cluster는 16384개의 hash slot에 나눠서 데이터를 저장하는데, 대상 키들이 모두 같은 슬롯에 있는 경우에만 트랜잭션을 실행할 수 있고, 다중 슬롯에 키가 나눠져 있는 경우 트랜잭션을 실행할 수 없음

Redis Cluster에서 트랜잭션이 필요하거나, 트랜잭션 내의 중간 연산 결과에 따른 분기처리가 필요한 경우는 어떻게 처리할까?

두 번째 방법


EVAL: Lua script를 실행하는 연산 (readonly로 EVAL_RO 등 다른 연산도 존재함)

EVAL 명령을 통해 Lua scipts를 실행할 수 있기 때문에

if (redis.call('incr', KEYS[1]) > 100) then
   redis.call('pexpire', KEYS[1], ARGV[2]]);
   return 1;
else
   return 0;
end

이런 식의 중간 incr 연산의 결과에 따른 분기 처리도 가능하다.

EVAL 동작 방식이, 레디스 노드를 블로킹해서, 다른 연산이 처리되지 않게 한 후, 스크립트 내의 요청을 처리한다.

  • 레디스는 싱글 스레드이기 때문에, 무거운 쿼리들이 나가지 않게 주의해야 하는데.. (싱글 스레드이기 때문에, 한 시점의 하나의 요청만 처리가 가능하기 때문에, 쿼리 처리 시간이 길면 뒤의 요청이 밀릴 수 있음)
  • 특히나 Lua script을 실행하는 경우는 여러 쿼리들을 블로킹해서 실행하기 때문에, 특히나 더욱 무거운 쿼리가 나가지 않게 주의해야 한다.


한계점

  • 타입 체크도 안되고 기타 등등으로 High Level 입장에서 사용하기 불편하기 때문에, 꼭 필요한 곳에서만 사용하는 것이 좋음.
    • 예를 들어 키가 존재하지 않는 경우에만 set 하는 경우, setnx 연산을 사용할 수 있고 기타 등등을 사용할 수 있음.
    • 분산 락 구현을 위한 레디스 라이브러리들 내부를 보면 대다수 EVAL 명령을 통해 Lua script를 실행하는 방법으로 구현되어 있는 듯
  • EVAL 명령으로 MULTI 관련된 기능들은 왠만한건 구현할 수 있는데, WATCH 명령을 통한 낙관적 락 같은 기능은 구현이 힘들듯..
반응형

'DBMS > Redis' 카테고리의 다른 글

[Redis] Redis 운영 관리 정리  (0) 2021.10.26
[Redis] Redis 세미나 정리  (0) 2021.09.15