Redis 事物

5 阅读4分钟

事务执行步骤

  1. 客户端使用显式命令开启事务。MULTI
  2. 客户端把事务中要执行的命令发送给服务器(增删改查),这里客户端发送命令给服务端,但是redis并不会立即执行这令命令,而是暂存在消息队列中。
  3. 客户端向服务端发送提交事务的命令EXEC,让redis执行暂存在消息队列中的命令

原子性

原子性是事务操作的一个属性,redis是如何保证事务的原子性的?

第一种情况
  • 在执行EXEC命令前,客户端发送的命令本身就有错误,例如语法错误等等,在进入队列的时就会被Redis判断出来。
  • 在命令入队的时,Redis会报错并记录下这个错误。此时,还是可以继续提交命令的。
  • 然后执行EXEC命令之后,Redis会拒绝所有提交命令操作,返回事务失败的结果。
  • 保证原子性
第二种情况
  • 事务操作入队的时,命令和操作的数据类型不匹配,但是Redis实例并没有检测出错误。
  • 然后执行EXEC后,Reids实际执行这些事务操作的时候会报错,也就是执行队列中的命令时会报错
  • 虽然Redis会对错误命令报错,但是其他的正确命令还是会执行,不会像Mysql一样回滚
  • 这里无法保证事务的原子性
# A、B 为 string 类型
# 开启事务
MULTI

LPOP A 
# QUEUED

DECR B
# QUEUED

EXEC
# error WRONGTYPE Operation against a key holding the wrong kind of value
# B的执行DECR结果
  • 这里就无法保证事务的原子性。但是Redis提供另一个方案使用DISCARD放弃所有操作
  • DISCARD 命令是主动放弃事务执行操作,把暂存队列清空,达不到回滚的效果
> get A
10
> get B
10
> MULTI
OK
> DECR A
QUEUED
> LPOP B
QUEUED
> DISCARD
OK
> get A
10
> get B
10
  • 这里A和B都是string数据结构并且都是10
  • LPOP对B进行操作的时候显然是不合法不正确的命令
  • 但是A做了DECR操作,然后执行了DISCARD命令
  • 可以发现B的值没有任何改变
第三种情况

在执行EXEC命令时,Redis发生故障,导致事务执行失败。

  • 这种情况下如果开启了AOF日志时,只会有部分的事务操作记录到AOF日志中。可以使用redis-check-aof工具检测AOF日志文件,这个工具可以把未完成的事务从aof日志中移除。然后使用aof恢复实例后,事务操作不会被执行,保证原子性。如果没有开启aof那就没办法保证原子性了。

隔离性

事务的隔离性保证,会受到和事务一起执行的并发操作影响。而事务队列执行又可以分成命令入队和命令执行两个阶段。

  • 并发操作在EXEC命令之前,此时,隔离性的保证要使用到WATCH机制来实现,否则无法保证隔离性
  • 并发操作在EXEC命令之后,此时,隔离性无法保证
WATCH机制
  • 在执行事务前,监控多个或一个键的变化情况,当事务调用EXEC命令执行时,WATCH机制会先检测当前键是否被其他客户端修改,如果修改过就会放弃事务,避免破坏事务的隔离性。如果没有并发修改事务数据的操作,事务就能正常执行,隔离性也得到保证。
  • 在执行事务后,并发操作在EXEC命令之后被服务器端接收执行。Redis是单线程执行命令,Redis会先保证队列中的命令先执行完。这样并发操作不会破坏事务的隔离性

持久性

Reids的持久性主要依靠AOF和RDB去实现。
但是这两种持久化的方式并没有能完完全全保证数据库的持久性。无论是RDB也好或是AOF也罢,都会在一定程度上丢失数据。