Computer >> 컴퓨터 >  >> 프로그램 작성 >> Redis

Lua:Redis 사용자를 위한 가이드

Redis에 스크립팅 언어가 내장되어 있다는 소식을 들었지만 아직 사용해 보지 않으셨나요? 다음은 Redis 서버에서 Lua의 강력한 기능을 사용하기 위해 이해해야 하는 내용입니다.

안녕하세요, 루아!

첫 번째 Redis Lua 스크립트는 실제로 의미 있는 방식으로 Redis와 상호 작용하지 않고 값을 반환합니다.

local msg = "Hello, world!"
return msg

이것은 간단합니다. 첫 번째 줄은 ourmessage로 로컬 변수를 설정하고 두 번째 줄은 Redis 서버에서 클라이언트로 해당 값을 반환합니다. 이 파일을 로컬에 hello.lua로 저장합니다. 다음과 같이 실행하십시오:

redis-cli --eval hello.lua

연결 문제?

redis-cli 예제에서는 로컬에서 Redisserver를 실행하고 있다고 가정합니다. RedisGreen과 같은 원격 서버로 작업하는 경우 호스트 및 포트 정보를 지정해야 합니다. 연결 찾기 RedisGreen 대시보드의 버튼을 클릭하여 서버의 로그인 정보를 빠르게 복사합니다.

참조:127.0.0.1:6379에서 Redis에 연결할 수 없음:연결이 거부되었습니다.

이것을 실행하면 "Hello, world!"가 출력됩니다. EVAL의 첫 번째 인수 완전한 lua 스크립트입니다 — 여기에서는 cat를 사용하고 있습니다. 파일에서 스크립트를 읽는 명령. 두 번째 인수는 스크립트가 액세스할 Rediskey의 수입니다. 간단한 "Hello World" 스크립트는 키에 액세스하지 않으므로 0을 사용합니다. .

키 및 인수 액세스

URL 단축 프로그램을 구축한다고 가정해 보겠습니다. URL이 들어올 때마다 저장하고 나중에 URL에 액세스하는 데 사용할 수 있는 고유 번호를 반환하려고 합니다.

Lua 스크립트를 사용하여 INCR을 사용하여 Redis에서 고유 ID를 가져옵니다. 고유 ID로 키가 지정된 해시에 URL을 즉시 저장합니다.

local link_id = redis.call("INCR", KEYS[1])
redis.call("HSET", KEYS[2], link_id, ARGV[1])
return link_id

call()을 사용하여 여기에서 처음으로 Redis에 액세스합니다. function.call() 의 인수는 Redis에 보낼 명령입니다. 먼저 INCR <key> , 그런 다음 HSET <key> <field> <value> . 이 두 명령은 순차적으로 실행됩니다. 이 스크립트가 실행되는 동안 Redis는 다른 작업을 수행하지 않으며 매우 빠르게 실행됩니다.

두 개의 Lua 테이블, KEYS에 액세스하고 있습니다. 및 ARGV . 테이블은 연관 배열이며 데이터를 구조화하는 Lua의 유일한 메커니즘입니다. 우리의 목적을 위해 그것들을 당신이 가장 편한 언어로 된 배열과 동일하다고 생각할 수 있지만, 언어를 처음 접하는 사람들을 넘어뜨리는 다음 두 가지 Lua-isms에 유의하십시오.

  • 테이블은 1부터 시작합니다. 즉, 인덱싱은 1에서 시작합니다. 따라서 mytable의 첫 번째 요소는 mytable[1]입니다. , 두 번째는 mytable[2]입니다. 등

  • 테이블은 nil 값을 가질 수 없습니다. 작업이 [ 1, nil, 3, 4 ] 테이블을 생성하는 경우 , 결과는 대신 [ 1 ] — 테이블이잘림 첫 번째 nil 값에서.

이 스크립트를 호출할 때 KEYS 값도 함께 전달해야 합니다. 및 ARGV 테이블. 원시 Redis 프로토콜에서 명령은 다음과 같습니다.

EVAL $incrset.lua 2 links:counter links:url https://malcolmgladwellbookgenerator.com/

EVAL 호출 시 , 스크립트 뒤에 2를 제공합니다. KEYS의 수로 액세스한 다음 KEYS를 나열합니다. , 그리고 마지막으로 ARGV에 대한 값을 제공합니다. .

일반적으로 Redis Lua 스크립트로 앱을 빌드할 때 Redisclient 라이브러리가 키 수 지정을 처리합니다. 위의 코드 블록은 완전성을 위해 표시되지만 명령줄에서 이 작업을 수행하는 더 쉬운 방법은 다음과 같습니다.

redis-cli --eval incrset.lua links:counter links:urls , https://malcolmgladwellbookgenerator.com/

--eval 사용 시 위와 같이 쉼표로 KEYS[]를 구분합니다. ARGV[]에서 항목.

명확히 하기 위해 이번에는 KEYS를 사용하여 원래 스크립트를 다시 보여줍니다. 및 ARGV 확장:

local link_id = redis.call("INCR", "links:counter")
redis.call("HSET", "links:urls", link_id, "https://malcolmgladwellbookgenerator.com")
return link_id

Redis용 Lua 스크립트를 작성할 때 액세스되는 모든 키는 KEYS로만 액세스해야 합니다. 테이블. ARGV 테이블은 매개변수 전달에 사용됩니다. 여기에 저장하려는 URL의 값이 있습니다.

조건부 논리:인크렉스 및 힌크렉스

위의 예는 URL 단축 프로그램에 대한 링크를 저장하지만 URL에 액세스한 횟수도 추적해야 합니다. 이를 위해 Redis의 해시에 카운터를 보관합니다. 사용자가 링크 식별자와 함께 올 때 존재하는지 확인하고 존재하는 경우 카운터를 증가시킵니다.

if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
  return redis.call("HINCRBY", KEYS[1], ARGV[1], 1)
else
  return nil
end

누군가가 짧은 링크를 클릭할 때마다 이 스크립트를 실행하여 링크가 다시 공유되었는지 추적합니다. EVAL을 사용하여 스크립트를 호출합니다. links:visits 전달 단일 키와 이전 스크립트에서 단일 인수로 반환된 링크 식별자에 대한

스크립트는 해시 없이 거의 동일하게 보입니다. 다음은 표준 Redis 키가 있는 경우에만 증가시키는 스크립트입니다.

if redis.call("EXISTS",KEYS[1]) == 1 then
  return redis.call("INCR",KEYS[1])
else
  return nil
end

스크립트 로드 및 EVALSHA

Redis가 Lua 스크립트를 실행할 때 다른 어떤 것도 실행하지 않는다는 것을 기억하십시오. 최고의 스크립트는 필요한 최소한의 논리로 작은 원자 데이터 작업의 기존 Redis 어휘를 단순히 확장합니다. Lua 스크립트의 버그는 Redis 서버를 완전히 잠글 수 있습니다. 문제를 짧고 쉽게 디버그할 수 있도록 하는 것이 가장 좋습니다.

일반적으로 매우 짧지만 실행할 때마다 전체 Lua 스크립트를 지정할 필요가 없습니다. 실제 애플리케이션에서는 애플리케이션이 부팅될 때(또는 배포할 때) 각 Lua 스크립트를 Redis에 등록한 다음 나중에 고유한 SHA-1 식별자로 스크립트를 호출합니다.

redis-cli SCRIPT LOAD "return 'hello world'"
# "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"

redis-cli EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
# "hello world"

SCRIPT LOAD에 대한 명시적 호출 EVAL 이후 라이브 애플리케이션에서는 일반적으로 필요하지 않습니다. 전달된 스크립트를 암시적으로 로드합니다. 애플리케이션은 EVALSHA를 시도할 수 있습니다. 낙관적으로 EVAL로 대체 스크립트를 찾을 수 없는 경우에만.

Ruby 프로그래머라면 Ruby 앱용 Lua 스크립트의 로드 및 저장을 간소화하는 Shopify의 Wolverine을 살펴보십시오. PHP프로그래머의 경우 Predis는 Luascript가 일반 Redis 명령인 것처럼 호출되도록 추가하는 것을 지원합니다. Lua와의 상호 작용을 표준화하기 위해 이러한 도구나 다른 도구를 사용하는 경우 알려주세요. 다른 도구가 있는지 알고 싶습니다.

루아를 언제 사용해야 하나요?

Lua에 대한 Redis 지원은 WATCH와 다소 겹칩니다. /MULTI /EXEC 함께 실행되도록 작업을 그룹화하는 블록입니다. 그렇다면 어떻게 다른 하나를 사용하도록 선택합니까? MULTI의 각 작업 블록은 독립적이어야 하지만 Lua를 사용하면 이후 작업이 이전 작업의 결과에 따라 달라질 수 있습니다. Lua 스크립트를 사용하면 WATCH할 때 느린 클라이언트를 굶주릴 수 있는 경쟁 조건을 피할 수도 있습니다. 사용됩니다.

RedisGreen에서 본 것에서 Lua를 사용하는 대부분의 앱은 MULTI/EXEC도 사용하지만 그 반대의 경우도 마찬가지입니다. 대부분의 성공적인 Lua 스크립트는 작으며 앱에 필요하지만 Redisvocabulary의 일부가 아닌 단일 기능을 구현합니다.

도서관 방문

Redis Lua 인터프리터는 기본, 테이블, 문자열, 수학, 디버그, cjson 및 cmsgpack의 7개 라이브러리를 로드합니다. 첫 번째 몇 가지는 모든 언어에서 기대할 수 있는 기본 작업을 수행할 수 있는 표준 라이브러리입니다. 마지막 두 개는 Redis가 JSON과 MessagePack을 이해할 수 있게 해주었어요. 이것은 매우 유용한 기능인데 왜 더 자주 사용되지 않는지 궁금합니다.

공개 API가 있는 웹 앱에는 JSON이 곳곳에 있는 경향이 있습니다. 따라서 일반 Redis 키에 많은 JSON blob이 저장되어 있고 마치 해시로 저장한 것처럼 그 안에 있는 특정 값에 액세스하려고 할 수 있습니다. Redis JSON 지원을 사용하면 쉽습니다.

if redis.call("EXISTS", KEYS[1]) == 1 then
  local payload = redis.call("GET", KEYS[1])
  return cjson.decode(payload)[ARGV[1]]
else
  return nil
end

여기에서 키가 존재하는지 확인하고 없으면 빠르게 nil을 반환합니다. 그런 다음 Redis에서 JSON 값을 가져와 cjson.decode()로 구문 분석합니다. , 요청한 값을 반환합니다.

redis-cli set apple '{ "color": "red", "type": "fruit" }'
# OK

redis-cli --eval json-get.lua apple , type
# "fruit"

이 스크립트를 Redis 서버에 로드하면 Redis에 저장된 JSON 값을 해시인 것처럼 처리할 수 있습니다. 개체가 상당히 작은 경우 액세스할 때마다 값을 구문 분석해야 하지만 실제로는 매우 빠릅니다.

성능을 요구하는 시스템을 위한 내부 API로 작업하는 경우 더 작고 빠르기 때문에 JSON보다 MessagePack을 선택할 가능성이 높습니다. 다행스럽게도 Redis(대부분의 장소에서와 같이)에서 MessagePack은 거의 대체품입니다. JSON:

if redis.call("EXISTS", KEYS[1]) == 1 then
  local payload = redis.call("GET", KEYS[1])
  return cmsgpack.unpack(payload)[ARGV[1]]
else
  return nil
end

숫자 처리

Lua와 Redis는 서로 다른 유형 시스템을 가지고 있으므로 Redis-Lua 경계를 넘을 때 값이 어떻게 변경될 수 있는지 이해하는 것이 중요합니다. 숫자가 Lua에서 Redis 클라이언트로 다시 들어오면 정수가 됩니다. 소수점 이하의 모든 숫자는 삭제됩니다.

local indiana_pi = 3.2
return indiana_pi

이 스크립트를 실행하면 Redis는 정수 3을 반환합니다. 흥미로운 파이 조각을 잃게 됩니다. 간단해 보이지만 스크립트 중간에 Redis와 상호 작용하기 시작하면 상황이 조금 더 까다로워집니다. 예:

local indiana_pi = 3.2
redis.call("SET", "pi", indiana_pi)
return redis.call("GET", "pi")

여기서 결과 값은 문자열입니다. "3.2" 왜요? Redis에는 전용 숫자 유형이 없습니다. 처음 SET할 때 Redis는 값을 문자열로 저장하고 Lua가 처음에 값을 float로 생각했다는 사실에 대한 모든 기록을 잃습니다. 나중에 값을 가져오면 여전히 문자열입니다.

GET로 액세스되는 Redis의 값 /SET INCR과 같은 숫자 연산을 제외하고는 문자열로 간주해야 합니다. 및 DECR 그들에 대해 실행됩니다. 이러한 특수 숫자 연산은 실제로 정수 응답을 반환하지만(수학적 규칙에 따라 저장된 값을 조작함) Redis에 저장된 값의 "유형"은 여전히 ​​문자열 값입니다.

문제:요약

다음은 Redis에서 Lua로 작업할 때 볼 수 있는 가장 일반적인 오류입니다.

  • 테이블은 대부분의 인기 있는 언어와 달리 Lua에서 1 기반입니다. KEYS 테이블의 첫 번째 요소는 KEYS[1]입니다. , 두 번째는 KEYS[2]입니다. 등.

  • nil 값은 Lua에서 테이블을 종료합니다. 따라서 [ 1, 2, nil, 3 ] 자동으로 [1, 2]가 됩니다. . 테이블에 nil 값을 사용하지 마십시오.

  • redis.call redis.pcall 동안 예외 스타일의 Lua 오류가 발생합니다. 모든 오류를 자동으로 잡아내고 검사할 수 있는 astable을 반환합니다.

  • Lua 숫자는 Redis로 전송될 때 정수로 변환됩니다. 소수점 이하의 모든 것은 손실됩니다. 부동 소수점 숫자를 반환하기 전에 문자열로 변환하십시오.

  • KEYS의 Lua 스크립트에서 사용하는 모든 키를 지정해야 합니다. 그렇지 않으면 향후 Redis 버전에서 스크립트가 중단될 수 있습니다.

  • Lua 스크립트는 Redis의 다른 작업과 같습니다. 실행되는 동안 다른 작업은 실행되지 않습니다. 스크립트를 Redis 서버의 어휘를 확장하는 방법으로 생각하세요. 짧고 핵심적인 내용을 유지하세요.

추가 자료

Lua 및 Redis에 대한 훌륭한 리소스가 온라인에 많이 있습니다. 다음은 몇 가지 Iuse입니다.

  • EVAL 문서
  • RedisGreen의 Lua 스크립트 라이브러리
  • 루아 참조 설명서
  • 루아 튜토리얼 디렉토리