1. 개요:
다른 많은 데이터베이스와 마찬가지로 NoSQL 데이터베이스인 Redis도 트랜잭션 메커니즘을 제공합니다. Redis에서는 MULTI/EXEC/DISCARD/WATCH 네 가지 명령이 트랜잭션 구현의 초석입니다. 관계형 데이터베이스 개발 경험이 있는 개발자에게는 이 개념이 낯설지 않다고 생각합니다. 그럼에도 불구하고 Redis에서 트랜잭션의 구현 특성을 간략하게 나열하겠습니다.
1) 트랜잭션에서는 모든 명령이 순차적으로 실행됩니다. 트랜잭션이 실행되는 동안 Redis는 다른 클라이언트 요청에 대한 서비스를 제공하지 않으므로 트랜잭션의 모든 명령이 원자적으로 실행되도록 합니다.
2) 관계형 데이터베이스의 트랜잭션과 비교할 때 Redis 트랜잭션에서는 명령 실행이 실패해도 후속 명령이 계속 실행됩니다.
3) 관계형 데이터베이스 개발 경험이 있는 사람이라면 "BEGIN TRANSACTION" 문으로 이해할 수 있는 MULTI 명령을 통해 트랜잭션을 시작할 수 있습니다. 이 명령문 이후에 실행되는 명령은 트랜잭션 내의 작업으로 간주됩니다. 마지막으로 EXEC/DISCARD 명령을 실행하여 트랜잭션 내의 모든 작업을 커밋/롤백할 수 있습니다. 이 두 Redis 명령은 관계형 데이터베이스의 COMMIT/ROLLBACK 문과 동일한 것으로 간주될 수 있습니다.
4) 트랜잭션이 시작되기 전에 클라이언트와 서버 사이에 통신 장애가 발생하고 네트워크 연결이 끊어지면 이후에 실행될 모든 명령문이 서버에서 실행되지 않습니다. 그러나 클라이언트가 EXEC 명령을 실행한 후 네트워크 중단 이벤트가 발생하면 트랜잭션의 모든 명령이 서버에 의해 실행됩니다.
5) Append-Only 모드를 사용하는 경우 Redis는 write 시스템 함수를 호출하여 이 호출에서 트랜잭션의 모든 쓰기 작업을 디스크에 씁니다. 그러나 쓰기 프로세스 중에 정전으로 인한 가동 중지 시간과 같은 시스템 충돌이 발생하는 경우 현재 데이터의 일부만 디스크에 기록될 수 있으며 데이터의 다른 부분은 손실됩니다. Redis 서버는 다시 시작할 때 일련의 필요한 일관성 검사를 수행합니다. 유사한 문제가 발견되면 즉시 종료되고 해당 오류 메시지가 표시됩니다. 이때 Redis 툴킷에 제공되는 redis-check-aof 도구를 최대한 활용해야 합니다. 이 도구는 데이터 불일치 오류를 찾고 작성된 데이터 중 일부를 롤백하는 데 도움이 될 수 있습니다. 복구 후 Redis 서버를 다시 시작할 수 있습니다.
2. 관련 명령어 목록:
命令原型 | 时间复杂度 | 命令描述 | 返回值 |
MULTI | 用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才会被原子的执行。 | 始终返回OK | |
EXEC | 执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,即非事务状态。如果在事务中执行了WATCH命令,那么只有当WATCH所监控的Keys没有被修改的前提下,EXEC命令才能执行事务队列中的所有命令,否则EXEC将放弃当前事务中的所有命令。 | 原子性的返回事务中各条命令的返回结果。如果在事务中使用了WATCH,一旦事务被放弃,EXEC将返回NULL-multi-bulk回复。 | |
DISCARD | 回滚事务队列中的所有命令,同时再将当前连接的状态恢复为正常状态,即非事务状态。如果WATCH命令被使用,该命令将UNWATCH所有的Keys。 | 始终返回OK | |
WATCHkey [key ...] | O(1) | 在MULTI命令执行之前,可以指定待监控的Keys,然而在执行EXEC之前,如果被监控的Keys发生修改,EXEC将放弃执行该事务队列中的所有命令。 | 始终返回OK。 |
UNWATCH | O(1) | 取消当前事务中指定监控的Keys,如果执行了EXEC或DISCARD命令,则无需再手工执行该命令了,因为在此之后,事务中所有被监控的Keys都将自动取消。 | 始终返回OK。 |
3. 명령 예:
1. 트랜잭션이 정상적으로 실행되었습니다:
#在Shell命令行下执行Redis的客户端工具。 /> redis-cli #在当前连接上启动一个新的事务。 redis 127.0.0.1:6379> multi OK #执行事务中的第一条命令,从该命令的返回结果可以看出,该命令并没有立即执行,而是存于事务的命令队列。 redis 127.0.0.1:6379> incr t1 QUEUED #又执行一个新的命令,从结果可以看出,该命令也被存于事务的命令队列。 redis 127.0.0.1:6379> incr t2 QUEUED #执行事务命令队列中的所有命令,从结果可以看出,队列中命令的结果得到返回。 redis 127.0.0.1:6379> exec 1) (integer) 1 2) (integer) 1
2. 트랜잭션에 실패한 명령이 있습니다:
#开启一个新的事务。 redis 127.0.0.1:6379> multi OK #设置键a的值为string类型的3。 redis 127.0.0.1:6379> set a 3 QUEUED #从键a所关联的值的头部弹出元素,由于该值是字符串类型,而lpop命令仅能用于List类型,因此在执行exec命令时,该命令将会失败。 redis 127.0.0.1:6379> lpop a QUEUED #再次设置键a的值为字符串4。 redis 127.0.0.1:6379> set a 4 QUEUED #获取键a的值,以便确认该值是否被事务中的第二个set命令设置成功。 redis 127.0.0.1:6379> get a QUEUED #从结果中可以看出,事务中的第二条命令lpop执行失败,而其后的set和get命令均执行成功,这一点是Redis的事务与关系型数据库中的事务之间最为重要的差别。 redis 127.0.0.1:6379> exec 1) OK 2) (error) ERR Operation against a key holding the wrong kind of value 3) OK 4) "4"
#为键t2设置一个事务执行前的值。 redis 127.0.0.1:6379> set t2 tt OK #开启一个事务。 redis 127.0.0.1:6379> multi OK #在事务内为该键设置一个新值。 redis 127.0.0.1:6379> set t2 ttnew QUEUED #放弃事务。 redis 127.0.0.1:6379> discard OK #查看键t2的值,从结果中可以看出该键的值仍为事务开始之前的值。 redis 127.0.0.1:6379> get t2 "tt"
4. WATCH 명령 및 CAS 기반 낙관적 잠금:
Redis 트랜잭션에서는 WATCH 명령을 사용하여 CAS(검사 및 설정) 기능을 제공할 수 있습니다. 트랜잭션이 실행되기 전에 WATCH 명령을 통해 여러 키를 모니터링한다고 가정합니다. WATCH 이후 키 값이 변경되면 EXEC 명령으로 실행된 트랜잭션이 중단되고 호출자에게 알리기 위해 Null 다중 대량 응답이 반환됩니다. 거래 실행에 실패했습니다. 예를 들어, Redis에서는 키 값의 원자적 증가를 완료하기 위한 incr 명령이 제공되지 않는다고 다시 가정합니다. 이 기능을 구현하려면 해당 코드를 직접 작성하면 됩니다. 의사 코드는 다음과 같습니다.
val = GET mykey val = val + 1 SET mykey $val
위 코드는 단일 연결의 경우에만 실행 결과가 올바른지 보장할 수 있습니다. 동일한 시간, 코드를 사용하면 다중 스레드 프로그램 경쟁 조건(Race Condition)에서 자주 발생하는 오류 시나리오가 발생합니다. 예를 들어 클라이언트 A와 B는 동시에 mykey의 원래 값을 읽습니다. 값이 10이라고 가정합니다. 그 후 두 클라이언트 모두 값에 1을 추가하고 이를 Redis 서버에 다시 설정합니다. 결과는 우리가 생각한 12가 아니라 11입니다. 유사한 문제를 해결하려면 WATCH 명령의 도움이 필요합니다. 다음 코드를 참조하세요.
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC
이전 코드와의 차이점은 새 코드에서는 키를 가져오기 전에 WATCH 명령을 통해 모니터링한다는 점입니다. mykey 값을 입력한 다음 트랜잭션에서 set 명령을 묶습니다. 이렇게 하면 각 연결에 대해 EXEC를 실행하기 전에 현재 연결에서 얻은 mykey 값이 연결된 다른 클라이언트에 의해 수정되는 경우 현재의 EXEC 명령이 실행되도록 효과적으로 보장할 수 있습니다. 연결이 실행되지 않습니다. 이런 방식으로 호출자는 반환 값을 판단한 후 val이 성공적으로 재설정되었는지 여부를 알 수 있습니다.
위 내용은 Redis Tutorial (8): Detail Transactions의 내용입니다. 더 많은 관련 내용은 PHP 중국어 홈페이지(www.php.cn)를 참고해주세요!