이 글은 Redis 트랜잭션에 대한 철저한 이해를 제공하고 Redis 트랜잭션의 두 가지 모드(트랜잭션 모드와 Lua 스크립트)를 비교하는 내용이 모든 사람에게 도움이 되기를 바랍니다.
정확하게 말하면 Redis 트랜잭션에는트랜잭션 모드와Lua 스크립트의 두 가지 모드가 포함됩니다.
먼저 결론에 대해 이야기해 보겠습니다.
Redis의 트랜잭션 모델에는 다음과 같은 특징이 있습니다.
1 트랜잭션 원칙
트랜잭션은 세 단계로 구성됩니다.
다음은 거래 예시입니다.
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.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 트랜잭션의 원자성에 대한 나의 이해는 다음과 같습니다.
즉,Redis 트랜잭션은 특정 조건에서만 특정 원자성을 가집니다.
2.2 격리
동시 시나리오에서 트랜잭션이 서로 간섭을 피할 수 있는지 여부.
EXEC 명령 실행 전과EXEC 명령 실행 후의 두 단계로 나누어 별도로 논의할 수 있습니다.
WATCH 메커니즘을 사용하여 낙관적 잠금 효과를 얻을 수 있습니다.
2.3 내구성
redis 거래의 내구성은 보장할 수 없습니다.
2.4 일관성
먼저 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 서비스가 중단됩니다. 여기서는 서비스 구성의 지속성 모드를 고려해야 합니다.
"데이터 집약적 애플리케이션 설계"
원자성, 격리성 및 내구성은 데이터베이스의 속성인 반면, 일관성(ACID 의미에서)은 애플리케이션의 속성에 의존할 수 있습니다. 일관성을 달성하기 위해서는 데이터베이스의 원자성 및 격리 속성이 필요하지만 이는 데이터베이스에만 달려 있는 것이 아닙니다. 따라서 문자 C는 실제로 ACID에 속하지 않습니다.
원자성, 격리 및 내구성은 데이터베이스의 속성인 반면 일관성( ACID 의미에서)는 애플리케이션의 속성입니다. 애플리케이션은 일관성을 달성하기 위해 데이터베이스의 원자성 및 격리 속성에 의존할 수 있지만 이것이 데이터베이스에만 의존하는 것은 아닙니다. 따라서 문자 C는 ACID에 속하지 않습니다. 우리가 어려움을 겪고 있는 일관성은 실제로
을 의미합니다. 현실 세계에서의 일관성은 업무의 궁극적인 목표입니다.
실제 세계에서 일관성을 유지하려면 다음 사항을 충족해야 합니다.
2.5 트랜잭션 기능우리는 일반적으로 Redis를 인메모리 데이터베이스라고 부릅니다. 기존 관계형 데이터베이스와 달리 더 높은 성능과 더 빠른 쓰기 속도를 제공하기 위해 몇 가지 작업이 설계 및 구현 수준에서 수행되었습니다. Balanced는 트랜잭션 ACID를 완전히 지원하지 않습니다.
Redis 트랜잭션에는 다음과 같은 특징이 있습니다.
격리를 보장합니다.3.1 소개
Lua 스크립트는 게임 분야에서 빛을 발했습니다. 잘 알려진 "Westward Journey II"와 "World of Warcraft"는 모두 Lua 스크립트를 광범위하게 사용합니다. Lua 스크립트는
Openresty및Kong과 같이 Java 백엔드 엔지니어가 접하게 되는 API 게이트웨이에서 볼 수 있습니다.Redis 버전 2.6.0부터 Redis에 내장된 Lua 인터프리터는 Redis에서 Lua 스크립트를 실행할 수 있습니다.
Lua 스크립트 사용의 이점:
네트워크 오버헤드를 줄입니다. 네트워크 대기 시간을 줄이기 위해 스크립트 형식으로 한 번에 여러 요청을 보냅니다.3 | |
---|---|
4 | WATCH key [key ...] 트랜잭션이 실행되기 전에 이 키(또는 이러한 키)가 다른 명령에 의해 변경되면 트랜잭션이 중단됩니다. |
5 | UNWATCH는 WATCH 명령에 의한 모든 키의 모니터링을 취소합니다. |
데이터베이스 격리는 여러 동시 트랜잭션이 동시에 데이터를 읽고, 쓰고, 수정할 수 있도록 하는 데이터베이스의 기능을 의미합니다. 격리는 교차로 인해 여러 트랜잭션이 동시에 실행되는 것을 방지할 수 있습니다. 실행으로 인해 데이터 불일치가 발생합니다.트랜잭션 격리는우선 명확해야 합니다. Redis에는 트랜잭션 개념이 없습니다. 격리 수준. 여기에서는 Redis의 격리에 대해 논의합니다.트랜잭션 실행을트랜잭션 원칙 섹션에서 트랜잭션이 실행되기 전에도 Redis 키를 수정할 수 있음을 확인했습니다. 이때Redis는 단일 스레드 실행 명령이기 때문에 EXEC 명령이 실행된 후 Redis는 명령 대기열의 모든 명령이 실행되는지 확인합니다. 이는 트랜잭션 격리를 보장합니다.데이터베이스의 지속성은 트랜잭션이 완료된 후 데이터 수정이 영구적이며 시스템이 실패하더라도 손실되지 않음을 의미합니다.Redis 데이터의 지속 여부는 Redis의 지속성 구성 모드에 따라 다릅니다.요약하자면,내가 검색한 정보에는 일관성의 개념이 항상 혼동되어 왔습니다. |
3.2 EVAL 命令
命令格式:
1 EVAL script numkeys key [key ...] arg [arg ...]
说明:
script
是第一个参数,为 Lua 5.1脚本;numkeys
指定后续参数有几个 key;key [key ...]
,是要操作的键,可以指定多个,在 Lua 脚本中通过KEYS[1]
,KEYS[2]
获取;arg [arg ...]
,参数,在 Lua 脚本中通过ARGV[1]
,ARGV[2]
获取。简单实例:
1 redis> eval "return ARGV[1]" 0 100 2 "100" 3 redis> eval "return {ARGV[1],ARGV[2]}" 0 100 101 4 1) "100" 5 2) "101" 6 redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second 7 1) "key1" 8 2) "key2" 9 3) "first" 10 4) "second"
下面演示下 Lua 如何调用 Redis 命令 ,通过redis.call()
来执行了 Redis 命令 。
1 redis> set mystring 'hello world' 2 OK 3 redis> get mystring 4 "hello world" 5 redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring 6 "hello world" 7 redis> EVAL "return redis.call('GET','mystring')" 0 8 "hello world"
3.3 EVALSHA 命令
使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。
思路是先将 Lua 脚本先缓存起来 , 返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。
EVALSHA 命令基本语法如下:
1 redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...]
实例如下:
1 redis> SCRIPT LOAD "return 'hello world'" 2 "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 3 redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 4 "hello world"
从定义上来说,Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。
因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。
不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。
Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。
Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。
不过在编写 Lua 脚本时,要注意如下两点:
更多编程相关知识,请访问:编程视频!!
명령 및 설명 | |
---|---|
EVAL 스크립트 numkeys key [key ...] arg [arg ...] Lua 실행 스크립트. | |
EVALSHA sha1 numkeys key [key ...] arg [arg ...] Lua 스크립트를 실행합니다. | |
SCRIPT EXISTS script [스크립트 ...] 지정된 스크립트가 캐시에 저장되었는지 확인하세요. | |
SCRIPT FLUSH 스크립트 캐시에서 모든 스크립트를 제거합니다. | |
SCRIPT KILL 현재 실행 중인 Lua 스크립트를 종료합니다. | |
SCRIPT LOAD 스크립트 스크립트 캐시에 스크립트 스크립트를 추가하지만 스크립트를 즉시 실행하지는 않습니다. |
위 내용은 Redis의 트랜잭션에 대해 이야기해 보겠습니다: 트랜잭션 모드, Lua 스크립트의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!