redis eval 명령어에 대해서 알아봅시다.

REDIS 2023. 1. 5. 23:56

 저번시간에 redis의 incr에 대해 배웠습니다. 그러면서 ttl이 있는 counter를 구현해 보았는데요. 한 가지 문제점이 있었습니다. incr을 하고, ttl을 설정하는 것이 2개의 작업으로 쪼개졌었습니다.

 

 

 이 작업 하나 하나는 atomic 하겠지만, 두 개의 작업은 그렇지 않습니다. race condition이 발생했을 때, 문제가 발생할 수 있는 여지가 있었는데요. 문서에서 몇 가지 방법을 소개했었습니다. 그 중에 2.6 이상부터 가능한 LUA script와 eval 명령어를 이용하는 방법이 있습니다. 간단하게 소개해 드리도록 하겠습니다.

 


 먼저, eval은 LUA script, key 갯수, 키, argument들로 구성됩니다. 스크립트는 이 문서를 보시면 됩니다만, 간단한 예제 몇 개를 보면서 이해해 보겠습니다.

 

 먼저 eval "return ARGV[1]" 0 "hello" 라고 입력했더니, "hello"가 나옵니다. keynum에 0을 주었으니, "hello"가 1번째 인자가 되는데요. LUA 스크립트에서 ARGV[1]로 받았습니다. ARGV[1]은 "hello"를 의미해요. 우리는 단순히 return ARGV[1]을 스크립트로 입력했는데요. ARGV[1]이 hello였기 때문에 "hello"를 출력합니다.

 

 

 다음 예제입니다. 이번에는 로컬 변수를 설정했어요. local v=1은, v의 값을 1로 설정해요. 다음에 그냥 return v를 했어요. 이는 1을 돌려준다는 것을 의미해요. 따라서 eval "local v=1 return v" 0을 실행하면 1이 리턴됩니다.

 

 

 다음에는 if 문을 써 볼까요? if A then B end 구문입니다. A 조건을 만족하면, B 구문을 실행합니다. 스크립트 중간에 if v<0 then v=-v end가 있는데요. 이는, v가 0보다 작으면 v에 -1을 곱합니다. 절댓값을 구하는 것입니다.

 

 

 이제 while문을 써 봅시다. while은 while C do D end 꼴로 쓰게 됩니다. C라는 조건을 만족하면 계속 D를 실행합니다. eval에 나온 LUA 스크립트를 봅시다. 처음에 v가 0이고 i가 0인 상태입니다. i가 100보다 작으면, v에 i를 더하고, i를 하나 증가시켜요. 계속 v에 i를 더하다가 언제 빠지나요? i가 100보다 커지거나 같아졌을 때 빠져요. 즉, v는 0 + ... + 99를 더한 값이 나올 겁니다. 4950이 나오겠네요.

 

 

 다음에 if else if ... 를 쓰는 방법입니다. if A then B elseif C then D end 구문으로 사용하시면 됩니다. A 조건을 만족하면, B가 수행되고, A를 만족하지 않지만 C를 만족하면, D가 실행됩니다.

 

 

 위 예제에서는 i가 0이면, v에 0이 들어갑니다. 그렇지 않고 i가 1이면, v에 1이 들어갑니다. 그렇지 않으면 v가 -1이겠네요. i가 2였으므로, 결과는 -1이 됩니다.

 


 이제 테스트를 하나 해 봅시다. 1번 세션에서 INCR test를 20000000번 수행합니다.

 

 2번 세션에서 INCR test를 20000000번 수행하려고 하면 block이 됩니다. 왜냐하면, 스크립트가 실행되는 중이기 때문입니다. 이를 통해, 두 가지를 알 수 있습니다. eval script가 실행되는 동안 다른 세션에서 뭔가를 할 수가 없다. 최소한 같은 스크립트를 실행할 수 없었습니다. 고로, 매우 오래 걸리는 작업에 대해서 적합하지 않습니다.

 

 대신에, 여러 작업의 경우, 하나의 묶음처럼 처리할 수 있습니다. 예를 들어 incr 명령어와 expire 명령을 같이 수행한다던지 등. 정말 단순한 묶음들. 이는 eval이 실행되고 있는 동안 다른 무언가가 접근을 하지 못하기 때문입니다.

 

 get test를 해 볼까요? 20000000이 출력되었습니다. 고로, test에 대해서 incr을 수행하고, ttl을 설정하는 두 작업을 아래와 같이 하나로 묶어낼 수 있습니다.

 

 

 ttl test를 해 보면, 293이 나오는 것을 볼 수 있습니다. 그리고, eval에 의해 저 두 명령은 하나로 묶이게 된다는 것이 중요합니다.