Redis는 애초에 'High performance'를 목적에 두고 만들어졌기 때문에, 성능 개선을 위한 작업들을 많이 진행해 왔습니다. Pipelining이 그 중 하나고, 이번에는 Redis Scripting이라고도 불리는 eval을 알아 보겠습니다.

eval [script] [numkeys] [key ...] [arg ...]

EVAL은 Redis 버전 2.6.0부터 내장된 lua 인터프리터를 사용하여 스크립트를 평가하는 데 사용됩니다. 필수적으로 eval [script] [numkeys] 커맨드가 들어가 있어야 합니다.

  • script : 문자열 형태의 lua 스크립트입니다.
  • numkeys : 키의 갯수입니다. 이는 뒤에 추가적으로 붙을 선택 인자들 중 몇 개가 key인지를 lua가 알 수 있도록 합니다.

선택 인자 [key ...][arg ...]는 각각 lua에서 사용할 수 있도록 KEYSARGV 배열에 바인딩됩니다. Lua는 배열의 인덱스를 1부터 센다(one-based numbering)는 것에 주의해야 합니다.

아래 두 개의 lua 함수를 사용하여, lua script에서 redis 명령을 호출할 수 있습니다.

  • redis.call()
  • redis.pcall()

redis.call()은 redis.pcall()과 유사하지만, redis 명령 수행 중 오류가 발생하면 lua 오류를 발생시킵니다. 반대로 redis.pcall()은 오류를 trap하고 오류를 나타내는 lua 테이블을 반환합니다. 아래는 eval과 lua script를 사용한 간단한 set 명령입니다.

127.0.0.1:6379> eval "return redis.call('set', 1, 123)" 0
OK

script는 "return redis.call('set', 1, 123)", numkeys는 0입니다. 실제로 전달되는 명령은 set 1 123이 됩니다. eval의 컨벤션 상, 스크립트가 사용하는 모든 키는 KEYS 배열을 사용하여 전달되어야 하므로, 아래처럼 변경해야 합니다.

127.0.0.1:6379> eval "return redis.call('set', KEYS[1], 123)" 1 1
OK

어떤 이점이 있나?

복잡도만 늘어난 것으로 보이지만, Lua 스크립트는 다음과 같은 장점을 가집니다.

  • Pipelining처럼, 여러 명령을 한 번의 request/response만으로 수행할 수 있습니다.
  • 원하는 함수를 redis에서 지원하고 있지 않더라도 lua 스크립트로 대체 가능합니다.(반환되는 값 count, 반환되는 value 모두 더하기 등)
  • 아래와 같이, 스크립트를 재활용할 수도 있습니다.

스크립트 재활용하기

Lua script를 script load를 통해 실행하면, redis의 script cache라는 곳에 캐시됩니다. 그리고 그 실행 결과로 SHA 해시가 반환됩니다. 이는 각 lua script의 식별자로 활용되어, evalsha 명령어로 실행 가능합니다.

127.0.0.1:6379> script load "return {KEYS[1], KEYS[2]}"
"ee9729934e089eab4ec897f3c113f45c6a426b9d"
127.0.0.1:6379> evalsha ee9729934e089eab4ec897f3c113f45c6a426b9d 2 1 2
1) "1"
2) "2"

lua script의 길이가 길다면, 매 실행마다 script의 모든 코드를 전달하여 실행하는 것보다 미리 캐시된 script를 sha 값을 이용해 실행하는 것이 성능상 이점을 얻을 수 있습니다.

script exists로 특정 sha에 해당하는 script가 존재하는지 조회할 수 있고, script flush를 통해 script cache에 저장된 모든 script를 삭제할 수 있습니다.

127.0.0.1:6379> script exists ee9729934e089eab4ec897f3c113f45c6a426b9d
1) (integer) 1
127.0.0.1:6379> script flush
OK
127.0.0.1:6379> script exists ee9729934e089eab4ec897f3c113f45c6a426b9d
1) (integer) 0

eval은 러닝 커브로서 lua를 배워야 한다는 점이 있지만, 성능을 개선하기 위해 매우 강력한 무기이므로 배워 두면 정말 좋을 것 같습니다.

'데이터베이스 > Redis' 카테고리의 다른 글

[Redis] Pipelining  (0) 2018.09.06
[Redis] String(기본 조작)  (0) 2018.09.06
[Redis] List의 고급 커맨드들  (0) 2018.09.06
[Redis] List  (0) 2018.09.06
[Redis] select  (0) 2018.09.06

+ Recent posts