图解
Redis的事务采用标记来实现的,如下所示
public class Test {
private static final int SHA_DOU_BU_SHI = 0;
private static final int FEI_WU = 1 << 1;
private static final int LA_JI = 1 << 2;
private static final int LE_SE = 1 << 3;
public static void main(String[] args) {
fnm();
}
public static void fnm() {
int flags = 0;
flags |= LA_JI;
flags |= FEI_WU;
flags |= LE_SE;
if ((flags & FEI_WU) == FEI_WU) {
System.out.println("废物标签🏷️");
}
if ((flags & LA_JI) == LA_JI) {
System.out.println("垃圾标签🏷️");
}
if ((flags & LE_SE) == LE_SE) {
System.out.println("LE_SE标签🏷️");
}
if ((flags & SHA_DOU_BU_SHI) == 1) {
System.out.println("啥都不是标签🏷️");
}
}
}
- Multi进入事务为client打上
CLIENT_MULTI: This client is in a MULTI context
- watch的key发生了变更,为client打上
CLIENT_DIRTY_CAS: Watched keys modified. EXEC will fail.
- queue失败打上
CLIENT_DIRTY_EXEC: EXEC will fail for errors while queueing
事务
事务的特性: ACID
- A: 原子性(像单个命令一样,要么执行要么不执行,执行失败也是执行)
- C:一致性(数据从一个状态变成另外一个状态,Redis执行成功就会修改状态,失败就不会更新状态)
- I : 隔离型(Redis是单线程执行command,没有竞争条件,本书就是隔离的)
- D: 持久性 (有AOF和RDB保证)
由于Redis并未实现回滚,所以在一个事务中执行完成的命令不会因为后边的错误而回滚,从这个角度可以说是没有实现ACID中的一致性
Redis does not support rollbacks of transactions since supporting rollbacks would have a significant impact on the simplicity and performance of Redis.
使用
Redis事务使用 `Multi` 命令进入一个事务,虽然客户端提交的多个命令都会进入 `队列`,调用 `Exec` 命令的时候执行所有提交的 `Command`,调用 `Discard`则会清空事务队列,也就是 `multiState->commands`这部分,然后退出事务。
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
事务中的错误
Redis事务运行期间,可能出现两种类型的错误
- 执行的命令进入队列失败,例如参数错误,例如服务器内存限制导致导致命令不能进入队列。
- 命令执行失败,例如字符串的key用了集合的命令
执行事务
Redis持久化和事务
持久化得分开讨论
- 持久化选项没开,GG
- 用的RDB,这个不会,因为持久化函数执行的时候是直接持久化的dbNum,而事务的state是挂载在client上的。从另外一个角度来说,RDB是快照,后边exec命令可能丢失,重放失败(aof同样存在这个问题,需要使用工具check)
- aof,执行aofrewrite的时候同rdb,只是将dbNum里边的数据转成redis protocol的形式,只有在执行的时候exec命令的时候写入到aof缓冲区。首先构建一个multi命令写入,然后每次执行命令的时候构建一个写入aof,最后补充一个exec命令。
Redis脚本和事务
Redis脚本也是事务性质的,所有能够在Redis 事务中完成的,同样也可以使用脚本,一般来说,使用脚本比事务更加简单,也更快.