所謂事務(Transaction) ,是指作為單個邏輯工作單元執(zhí)行的一系列操作
ACID回顧
1)Atomicity(原子性):構成事務的的所有操作必須是一個邏輯單元,要么全部執(zhí)行,要么全部不執(zhí)行。
Redis:一個隊列中的命令 執(zhí)行或不執(zhí)行
2)Consistency(一致性):數據庫在事務執(zhí)行前后狀態(tài)都必須是穩(wěn)定的或者是一致的。
Redis: 集群中不能保證時時的一致性,只能是最終一致性
3)Isolation(隔離性):事務之間不會相互影響。
Redis: 命令是順序執(zhí)行的,在一個事務中,有可能被執(zhí)行其他客戶端的命令的
4)Durability(持久性):事務執(zhí)行成功后必須全部寫入磁盤。
Redis有持久化但不保證 數據的完整性
Redis事務
1)Redis的事務是通過multi、exec、discard和watch這四個命令來完成的。
2)Redis的單個命令都是原子性的,所以這里需要確保事務性的對象是命令集合。
3)Redis將命令集合序列化并確保處于同一事務的命令集合連續(xù)且不被打斷的執(zhí)行
4)Redis不支持回滾操作
事務命令
multi:用于標記事務塊的開始,Redis會將后續(xù)的命令逐個放入隊列中,然后使用exec原子化地執(zhí)行這個命令隊列
exec:執(zhí)行命令隊列
discard:清除命令隊列
watch:監(jiān)視key
unwatch:清除監(jiān)視key

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 222
QUEUED
127.0.0.1:6379> hset set1 name zhangfei
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s2 333
QUEUED
127.0.0.1:6379> hset set2 age 23
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> watch s1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 555
QUEUED
127.0.0.1:6379> exec # 此時在沒有exec之前,通過另一個命令窗口對監(jiān)控的s1字段進行修改
(nil)
127.0.0.1:6379> get s1
222
127.0.0.1:6379> unwatch
OK
事務機制
事務的執(zhí)行
- 事務開始
在RedisClient中,有屬性flags,用來表示是否在事務中
flags=REDIS_MULTI - 命令入隊
RedisClient將命令存放在事務隊列中
(EXEC,DISCARD,WATCH,MULTI除外) - 事務隊列
multiCmd *commands 用于存放命令 -
執(zhí)行事務
RedisClient向服務器端發(fā)送exec命令,RedisServer會遍歷事務隊列,執(zhí)行隊列中的命令,最后將執(zhí)行的結果一次性返回給客戶端。
如果某條命令在入隊過程中發(fā)生錯誤,redisClient將flags置為REDIS_DIRTY_EXEC,EXEC命令將會失敗返回。
image.png
typedef struct redisClient{
// flags
int flags //狀態(tài)
// 事務狀態(tài)
multiState mstate;
// .....
}redisClient;
// 事務狀態(tài)
typedef struct multiState{
// 事務隊列,FIFO順序
// 是一個數組,先入隊的命令在前,后入隊在后
multiCmd *commands;
// 已入隊命令數
int count;
}multiState;
// 事務隊列
typedef struct multiCmd{
// 參數
robj **argv;
// 參數數量
int argc;
// 命令指針
struct redisCommand *cmd;
}multiCmd;
Watch的執(zhí)行
使用WATCH命令監(jiān)視數據庫鍵
redisDb有一個watched_keys字典,key是某個被監(jiān)視的數據的key,值是一個鏈表.記錄了所有監(jiān)視這個數據的客戶端。
監(jiān)視機制的觸發(fā)
當修改數據后,監(jiān)視這個數據的客戶端的flags置為REDIS_DIRTY_CAS
事務執(zhí)行
RedisClient向服務器端發(fā)送exec命令,服務器判斷RedisClient的flags,如果為REDIS_DIRTY_CAS,則清空事務隊列。

typedef struct redisDb {
// .....
// 正在被WATCH命令監(jiān)視的鍵
dict *watched_keys;
// .....
}redisDb;
Redis的弱事務性
Redis語法錯誤
整個事務的命令在隊列里都清除
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sets m1 44
(error) ERR unknown command `sets`, with args beginning with: `m1`, `44`,
127.0.0.1:6379> set m2 55
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get m1
"22"
flags=REDIS_DIRTY_EXEC
Redis運行錯誤
在隊列里正確的命令可以執(zhí)行 (弱事務性)
弱事務性 :
1、在隊列里正確的命令可以執(zhí)行 (非原子操作)
2、不支持回滾
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set m1 55
QUEUED
127.0.0.1:6379> lpush m1 1 2 3 #不能是語法錯誤
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get m1
"55"
Redis不支持事務回滾(為什么呢)
1、大多數事務失敗是因為語法錯誤或者類型錯誤,這兩種錯誤,在開發(fā)階段都是可以預見的
2、Redis為了性能方面就忽略了事務回滾。 (回滾記錄歷史版本)
