Redis事务
# Redis事务
- Redis 事务 (opens new window):将一组命令放在同一个事务中进行处理。
- Redis的事务是通过
multi、exec、discard和watch
这四个命令来完成的。 - Redis的单个命令都是原子性的,所以Redis的事务性的对象是命令集合,即一次执行多个命令。
- Redis会将命令集合序列化并确保处于同一事务的命令集合连续(按顺序地执行)且不被打断的执行。
- Reids的事物是弱事物,并不支持回滚。
# 事物命令
详细指令看:help @transactions
- multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个命令队列。
- exec:执行MULTI后发出的所有命令(执行命令队列)
- discard:清除MULTI后发出的所有命令(清除命令队列)
- watch:观察给定的key以确定MULTI/EXEC块的执行
- unwatch:清除监视的key
示例:
# 执行过程成功
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 123
QUEUED
127.0.0.1:6379> hset user:1 name zhansan
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
# 执行失败
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> hset user:1 name wangwu
QUEUED
127.0.0.1:6379> exec # 在提交前,在另个客户端更改了k1的值
(nil)
127.0.0.1:6379> hget user:1 name
"zhansan"
# 事务机制
# 事务的执行
- 事务开始:在RedisClient中,有个flags属性,用来表示是否在事务中,flags=REDIS_MULTI
- 命令入队:RedisClient将命令存放在事务队列中(EXEC,DISCARD,WATCH,MULTI除外)
- 事务队列:multiCmd *commands 用于存放执行的命令
- 执行事务:RedisClient向服务器端发送exec命令,RedisServer会遍历事务队列,执行队列中的命令,最后将执 行的结果一次性返回给客户端。
如果某条命令在入队过程中发生错误,redisClient将flags置为REDIS_DIRTY_EXEC
,EXEC命令将会失败返回。
127.0.0.1:6379> hset user:1
(error) ERR wrong number of arguments for 'hset' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
RedisClient源码结构体定义:
typedef struct redisClient{
int flags // 状态
multiState mstate; // 事务状态
// .....
}redisClient;
// 事务状态
typedef struct multiState{
multiCmd *commands; // 事务队列,FIFO顺序 是一个数组,先入队的命令在前,后入队在后
int count; // 已入队命令数
}multiState;
// 事务队列
typedef struct multiCmd{
robj **argv; // 参数
int argc; // 参数数量
struct redisCommand *cmd; // 命令指针
}multiCmd;
# Watch的执行
watch命令用来监视数据库键,在发起执行事物的时候,如果这个键没有被更改,那么事物就会执行。
redisDb有一个watched_keys字典,key是某个被监视的数据的key,value是一个链表,记录了所有监视这个数 据的客户端。
当修改这个key数据后,监视这个数据的客户端的flags置为REDIS_DIRTY_CAS
。此时,RedisClient向服务器端发送exec命令,服务器判断RedisClient的flags,如果为REDIS_DIRTY_CAS
,则清空事务队列。
typedef struct redisDb{
// .....
dict *watched_keys; // 正在被WATCH命令监视的键
// .....
}redisDb;
# Redis的弱事务性
为什么说Redis是弱事物性?
事物中指令入队过程中,发现语法错误(flags=multi_dirty),会清空命令队列,而不是依次回滚。
127.0.0.1:6379> multi OK 127.0.0.1:6379> set k1 lisi QUEUED 127.0.0.1:6379> hset user:1 (error) ERR wrong number of arguments for 'hset' command # 语法不能是错误的 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors.
不支持回滚。官方对于不支持回滚的原因如下:
- 大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
- Redis为了性能方面就忽略了事务回滚。
上次更新: 5/30/2023, 11:42:20 PM