이 글은 Redis 트랜잭션에 대한 철저한 이해를 제공하고 Redis 트랜잭션의 두 가지 모드(트랜잭션 모드와 Lua 스크립트)를 비교하는 내용이 모든 사람에게 도움이 되기를 바랍니다.
정확하게 말하면 Redis 트랜잭션에는 트랜잭션 모드와 Lua 스크립트의 두 가지 모드가 포함됩니다.
먼저 결론에 대해 이야기해 보겠습니다.
Redis의 트랜잭션 모델에는 다음과 같은 특징이 있습니다.
격리를 보장합니다.
내구성을 보장할 수 없지만
일관성은 지원하지 않습니다. 개념에는 차이가 있습니다. 일관성의 핵심은 제약이라는 의미 하에서 Redis 트랜잭션은 일관성을 보장할 수 있다고 가정합니다.
그러나 Lua 스크립트에는 좀 더 실용적인 시나리오가 있습니다. 이는 또 다른 형태의 트랜잭션입니다. 어느 정도 원자성이 있지만 스크립트가 오류를 보고하면 트랜잭션이 롤백되지 않습니다. Lua 스크립트는 격리를 보장하고 완벽하게 지원할 수 있습니다.
다음 단계는 이전 단계의 결과에 따라 달라집니다
. [관련 권장 사항: Redis 비디오 튜토리얼]
Lua 스크립트 모드는 분산 잠금, 지연 대기열, 빨간 봉투 잡기 및 기타 시나리오 등 거의 모든 곳에 있습니다.
1 트랜잭션 원칙
REDIS 트랜잭션에는 다음 명령이 포함됩니다.
2
EXEC는 트랜잭션 블록 내의 모든 명령을 실행합니다.
3
DISCARD는 트랜잭션을 취소하고 트랜잭션 블록 내의 모든 명령 실행을 중단합니다.
4
WATCH key [key ...] 트랜잭션이 실행되기 전에 이 키(또는 이러한 키)가 다른 명령에 의해 변경되면 트랜잭션이 중단됩니다.
5
UNWATCH는 WATCH 명령에 의한 모든 키의 모니터링을 취소합니다.
트랜잭션은 세 단계로 구성됩니다.
트랜잭션이 열립니다. MULTI를 사용하면 이 명령은 명령을 실행하는 클라이언트가 비트랜잭션 상태에서 트랜잭션 상태로 전환되는 것을 표시합니다.
명령 대기열에 포함되면 MULTI가 트랜잭션을 연 후 클라이언트의 명령이 즉시 실행되지는 않지만 트랜잭션 대기열에 들어가게 됩니다.
트랜잭션을 실행하거나 폐기합니다. EXEC 명령이 수신되면 트랜잭션 큐에 있는 명령이 실행됩니다. DISCARD이면 트랜잭션이 삭제됩니다.
다음은 거래 예시입니다.
1
redis> MULTI
2
OK
3
redis> SET msg "hello world"
4
QUEUED
5
redis> GET msg
6
QUEUED
7
redis> EXEC
8
1) OK
9
1) hello world
로그인 후 복사
여기에 질문이 있나요? 트랜잭션을 시작할 때 Redis 키를 수정할 수 있나요?
트랜잭션이 EXEC 명령을 실행하기 전에도 Redis 키를 수정할 수 있습니다.
트랜잭션이 시작되기 전에 watch 명령을 사용하여 Redis 키를 모니터링할 수 있습니다. 트랜잭션이 실행되기 전에 키 값을 수정합니다. 트랜잭션이 실패하면 nil이 반환됩니다.
위의 예를 통해 watch 명령은 낙관적 잠금과 유사한 효과를 얻을 수 있습니다.
2 트랜잭션의 ACID
2.1 원자성
원자성은 트랜잭션의 모든 작업이 완전히 완료되거나 완료되지 않았음을 의미하며 일부 중간 링크로 끝나지 않습니다. 트랜잭션 실행 중 오류가 발생하면 트랜잭션이 실행되지 않았던 것처럼 트랜잭션이 시작되기 전 상태로 롤백됩니다.
첫 번째 예:
EXEC 명령을 실행하기 전에 구문 오류나 존재하지 않는 명령 등 클라이언트가 보낸 작업 명령이 잘못되었습니다.
1
redis> MULTI
2
OK
3
redis> SET msg "other msg"
4
QUEUED
5
redis> wrongcommand ### 故意写错误的命令
6
(error) ERR unknown command 'wrongcommand'
7
redis> EXEC
8
(error) EXECABORT Transaction discarded because of previous errors.
9
redis> GET msg
10
"hello world"
로그인 후 복사
이 예에서는 존재하지 않는 명령을 사용하여 대기열에 넣기가 실패하고 전체 트랜잭션이 실행되지 못하게 되었습니다.
두 번째 예:
트랜잭션 작업이 대기열에 추가되면 명령과 작업의 데이터 유형이 일치하지 않습니다. 대기열에 추가하는 것은 정상이지만 EXEC 명령 실행이 비정상입니다.
1
redis> MULTI
2
OK
3
redis> SET msg "other msg"
4
QUEUED
5
redis> SET mystring "I am a string"
6
QUEUED
7
redis> HMSET mystring name "test"
8
QUEUED
9
redis> SET msg "after"
10
QUEUED
11
redis> EXEC
12
1) OK
13
2) OK
14
3) (error) WRONGTYPE Operation against a key holding the wrong kind of value
15
4) OK
16
redis> GET msg
17
"after"
로그인 후 복사
이 예에서는 Redis가 EXEC 명령을 실행할 때 오류가 발생하더라도 Redis는 다른 명령의 실행을 종료하지 않으며 특정 명령이 실행되지 않아 트랜잭션이 롤백되지 않습니다.
요약하자면 Redis 트랜잭션의 원자성에 대한 나의 이해는 다음과 같습니다.
명령이 대기열에 포함될 때 오류가 보고되면 원자성을 보장하기 위해 트랜잭션 실행이 중단됩니다.
명령이 다음과 같은 경우에는 정상입니다. 대기열에 추가되고 EXEC 명령을 실행하면 오류가 보고됩니다. 이는 속성을 보장하지 않습니다.
즉, Redis 트랜잭션은 특정 조건에서만 특정 원자성을 가집니다.
2.2 격리
데이터베이스 격리는 여러 동시 트랜잭션이 동시에 데이터를 읽고, 쓰고, 수정할 수 있도록 하는 데이터베이스의 기능을 의미합니다. 격리는 교차로 인해 여러 트랜잭션이 동시에 실행되는 것을 방지할 수 있습니다. 실행으로 인해 데이터 불일치가 발생합니다. 트랜잭션 격리는
커밋되지 않은 읽기 커밋된 읽기 반복 읽기 직렬화 가능
우선 명확해야 합니다. Redis에는 트랜잭션 개념이 없습니다. 격리 수준. 여기에서는 Redis의 격리에 대해 논의합니다.
동시 시나리오에서 트랜잭션이 서로 간섭을 피할 수 있는지 여부.
트랜잭션 실행을
EXEC 명령 실행 전과 EXEC 명령 실행 후의 두 단계로 나누어 별도로 논의할 수 있습니다.
EXEC 명령을 실행하기 전트랜잭션 원칙 섹션에서 트랜잭션이 실행되기 전에도 Redis 키를 수정할 수 있음을 확인했습니다. 이때
WATCH 메커니즘을 사용하여 낙관적 잠금 효과를 얻을 수 있습니다.
EXEC 명령이 실행된 후Redis는 단일 스레드 실행 명령이기 때문에 EXEC 명령이 실행된 후 Redis는 명령 대기열의 모든 명령이 실행되는지 확인합니다. 이는 트랜잭션 격리를 보장합니다.
2.3 내구성
데이터베이스의 지속성은 트랜잭션이 완료된 후 데이터 수정이 영구적이며 시스템이 실패하더라도 손실되지 않음을 의미합니다. Redis 데이터의 지속 여부는 Redis의 지속성 구성 모드에 따라 다릅니다. RDB 또는 AOF가 구성되지 않으면 트랜잭션의 내구성을 보장할 수 없습니다. RDB 모드를 사용하면 트랜잭션이 실행된 후 다음 RDB 스냅샷이 실행되기 전에 인스턴스 충돌이 발생하는 경우 트랜잭션의 내구성이 보장됩니다. AOF 모드가 사용된다는 보장도 없습니다. AOF 모드의 세 가지 구성 옵션인 no 및 Everysec을 사용하면 데이터가 손실됩니다. 항상 트랜잭션의 내구성을 보장할 수 있지만 성능이 너무 낮기 때문에 일반적으로 프로덕션 환경에서는 권장되지 않습니다. 요약하자면,
redis 거래의 내구성은 보장할 수 없습니다.
2.4 일관성
내가 검색한 정보에는 일관성의 개념이 항상 혼동되어 왔습니다.
Wikipedia
먼저 Wikipedia의 일관성 정의를 살펴보겠습니다.
일관성은 데이터베이스 불변성을 유지하면서 트랜잭션이 데이터베이스를 하나의 유효한 상태에서 다른 유효한 상태로만 가져올 수 있음을 보장합니다. 데이터베이스에 기록된 모든 데이터는 유효해야 합니다. 제약 조건, 캐스케이드, 트리거 및 이들의 조합을 포함하여 정의된 모든 규칙에 따라 이는 불법 트랜잭션으로 인한 데이터베이스 손상을 방지하지만 트랜잭션이 올바른지 보장하지는 않습니다.
이 텍스트에서 일관성의 핵심은 "제약조건", "데이터베이스에 기록된 모든 데이터는 정의된 모든 규칙에 따라 유효해야 합니다"입니다.
제약조건을 이해하는 방법은 무엇입니까? 다음은 Zhihu 질문 데이터베이스의 내부 일관성과 외부 일관성을 이해하는 방법의 인용문과 Ant Financial의 OceanBase R&D 전문가인 Han Fusheng이 답변한 구절입니다.
"제약 조건"은 사용자가 말합니다. 데이터베이스에 데이터베이스를 연결하고 사용자는 데이터가 특정 제약 조건을 준수하도록 요구합니다. 데이터가 수정되면 데이터베이스는 데이터가 여전히 제약 조건을 충족하는지 확인합니다. 제약 조건이 더 이상 충족되지 않으면 수정 작업이 발생하지 않습니다.
관계형 데이터베이스에서 가장 일반적인 두 가지 제약 조건은 "고유 제약 조건"과 "무결성 제약 조건"입니다. 테이블에 정의된 기본 키와 고유 키는 모두 지정된 데이터 항목이 테이블 간에 정의되지 않도록 보장합니다. 무결성은 또한 서로 다른 테이블에서 동일한 속성의 일관성을 보장합니다.
"ACID의 일관성"은 사용하기 매우 쉬우므로 대부분의 사용자는 테이블을 디자인할 때 필요한 제약 조건을 의식적으로 추가하고 데이터베이스는 이를 엄격하게 구현합니다.
그래서 트랜잭션의 일관성은 사전 정의된 제약 조건과 관련이 있습니다. 제약 조건을 보장한다는 것은 일관성을 보장한다는 의미입니다.
다음 문장을 자세히 살펴보겠습니다. 이는 불법 거래로 인한 데이터베이스 손상을 방지하지만 거래의 정확성을 보장하지는 않습니다.
이 글을 쓴 후에도 여전히 모두가 약간 혼란스러울 수 있습니다. 고전적인 transfer 사례를 살펴보겠습니다.
Zhang San과 Li Si 계정의 초기 잔액은 모두 1,000위안이며 잔액 필드에 대한 제한이 없습니다. Zhang San은 Li Si에게 1,200위안을 이체했습니다. 장산의 잔액이 -200으로 업데이트되고, 리시의 잔액이 2200으로 업데이트됩니다.
애플리케이션 수준에서 볼 때 이 거래는 분명히 불법입니다. 실제 시나리오에서는 사용자 잔액이 0보다 작을 수 없지만 데이터베이스의 제약 조건을 완전히 따르기 때문에 데이터베이스 수준에서 이 거래는 여전히 일관성을 보장합니다.
Redis의 트랜잭션 일관성은 Redis 트랜잭션이 실행 중에 데이터베이스의 제약 조건을 준수하고 불법적이거나 유효하지 않은 오류 데이터를 포함하지 않음을 의미합니다.
세 가지 예외 시나리오를 각각 논의합니다.
EXEC 명령을 실행하기 전에 클라이언트가 보낸 작업 명령이 잘못되어 트랜잭션이 종료되고 데이터가 일관성을 유지합니다. 명령 및 작업 데이터 유형이 일치하지 않으면 잘못된 명령은 오류를 보고하지만 잘못된 명령으로 인해 트랜잭션이 종료되지 않고 계속 실행됩니다. 올바른 명령은 정상적으로 실행되고 잘못된 명령은 오류를 보고합니다. 이러한 관점에서 데이터는 일관성을 유지할 수도 있습니다.
트랜잭션 실행 중에 Redis 서비스가 중단됩니다. 여기서는 서비스 구성의 지속성 모드를 고려해야 합니다.
지속성 없는 메모리 모드: 서비스가 다시 시작된 후 데이터베이스가 데이터를 유지하지 않으므로 데이터가 일관성을 유지합니다. RDB/AOF 모드: 서비스가 다시 시작된 후 Redis는 RDB/AOF 파일을 통해 데이터를 복원하며 데이터베이스는 일관된 상태로 복원됩니다.
요약하자면,
일관성의 핵심이 제약이라는 의미 하에서 Redis 트랜잭션은 일관성을 보장할 수 있습니다
.
"데이터 집약적 애플리케이션 설계"
이 책은 분산 시스템을 시작하기 위한 마법의 책입니다. 트랜잭션 장에 ACID에 대한 설명이 있습니다.
원자성, 격리성 및 내구성은 데이터베이스의 속성인 반면, 일관성(ACID 의미에서)은 애플리케이션의 속성에 의존할 수 있습니다. 일관성을 달성하기 위해서는 데이터베이스의 원자성 및 격리 속성이 필요하지만 이는 데이터베이스에만 달려 있는 것이 아닙니다. 따라서 문자 C는 실제로 ACID에 속하지 않습니다.
원자성, 격리 및 내구성은 데이터베이스의 속성인 반면 일관성( ACID 의미에서)는 애플리케이션의 속성입니다. 애플리케이션은 일관성을 달성하기 위해 데이터베이스의 원자성 및 격리 속성에 의존할 수 있지만 이것이 데이터베이스에만 의존하는 것은 아닙니다. 따라서 문자 C는 ACID에 속하지 않습니다. 우리가 어려움을 겪고 있는 일관성은 실제로
실제 세계에 따른 일관성
을 의미합니다. 현실 세계에서의 일관성은 업무의 궁극적인 목표입니다.
실제 세계에서 일관성을 유지하려면 다음 사항을 충족해야 합니다.
원자성, 내구성 및 격리성을 보장할 수 없으면 트랜잭션의 일관성을 보장할 수 없습니다.
문자열 길이와 같은 데이터베이스 자체의 제약 조건은 열 제한이나 고유 제약 조건을 초과할 수 없습니다. 수준도 보호해야 합니다.
2.5 트랜잭션 기능우리는 일반적으로 Redis를 인메모리 데이터베이스라고 부릅니다. 기존 관계형 데이터베이스와 달리 더 높은 성능과 더 빠른 쓰기 속도를 제공하기 위해 몇 가지 작업이 설계 및 구현 수준에서 수행되었습니다. Balanced는 트랜잭션 ACID를 완전히 지원하지 않습니다.
Redis 트랜잭션에는 다음과 같은 특징이 있습니다.
격리를 보장합니다.
내구성을 보장할 수 없습니다.
일정 수준의 원자성을 갖지만 롤백을 지원하지 않습니다.
일관성을 가정하면 일관성의 개념이 다릅니다. 일관성은 제약 조건의 의미에 따라 Redis 트랜잭션이 일관성을 보장할 수 있다는 것입니다.
엔지니어링 관점에서 트랜잭션 작업의 각 단계가 이전 단계에서 반환된 결과에 의존해야 한다고 가정하면 낙관적 잠금은 감시를 통해 구현되어야 합니다.
3 Lua 스크립트
3.1 소개
Lua는 표준 C로 작성되었습니다. 코드는 간단하고 아름답으며 거의 모든 운영 체제와 플랫폼에서 컴파일하고 실행할 수 있습니다. Lua 스크립트는 C/C++ 코드로 쉽게 호출할 수 있고 C/C++ 함수를 차례로 호출할 수도 있어 Lua가 응용 프로그램에서 널리 사용됩니다.
Lua 스크립트는 게임 분야에서 빛을 발했습니다. 잘 알려진 "Westward Journey II"와 "World of Warcraft"는 모두 Lua 스크립트를 광범위하게 사용합니다. Lua 스크립트는
Openresty
및 Kong과 같이 Java 백엔드 엔지니어가 접하게 되는 API 게이트웨이에서 볼 수 있습니다. Redis 버전 2.6.0부터 Redis에 내장된 Lua 인터프리터는 Redis에서 Lua 스크립트를 실행할 수 있습니다.
Lua 스크립트 사용의 이점:
네트워크 오버헤드를 줄입니다. 네트워크 대기 시간을 줄이기 위해 스크립트 형식으로 한 번에 여러 요청을 보냅니다.
원자적 연산. Redis는 전체 스크립트를 전체적으로 실행하며 중간에 다른 명령이 삽입되지 않습니다.
재사용. 클라이언트가 보낸 스크립트는 Redis에 영구적으로 저장되며, 다른 클라이언트는 코드를 사용하지 않고도 이 스크립트를 재사용하여 동일한 논리를 완성할 수 있습니다.