redis——事務

Redis 事務可以一次執行多個命令, 并且帶有以下三個重要的保證:

  • 批量操作在發送 EXEC 命令前被放入隊列緩存。
  • 收到 EXEC 命令后進入事務執行,事務中任意命令執行失敗,其余的命令依然被執行。
  • 在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。

一個事務從開始到執行會經歷以下三個階段:

  • 開始事務。
  • 命令入隊。
  • 執行事務。

以下是一個事務的例子, 它先以?MULTI?開始一個事務, 然后將多個命令入隊到事務中, 最后由?EXEC?命令觸發事務, 一并執行事務中的所有命令:

redis 127.0.0.1:6379> MULTI
OKredis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUEDredis 127.0.0.1:6379> GET book-name
QUEUEDredis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUEDredis 127.0.0.1:6379> SMEMBERS tag
QUEUEDredis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"2) "C++"3) "Programming"

詳細介紹:

事務開始

MULTI?命令的執行標志著事務的開始:

redis> MULTI
OK

MULTI?命令可以將執行該命令的客戶端從非事務狀態切換至事務狀態, 這一切換是通過在客戶端狀態的?flags?屬性中打開?REDIS_MULTI?標識來完成的,?MULTI?命令的實現可以用以下偽代碼來表示:

def MULTI():# 打開事務標識client.flags |= REDIS_MULTI# 返回 OK 回復replyOK()

命令入隊

當一個客戶端處于非事務狀態時, 這個客戶端發送的命令會立即被服務器執行:

redis> SET "name" "Practical Common Lisp"
OKredis> GET "name"
"Practical Common Lisp"redis> SET "author" "Peter Seibel"
OKredis> GET "author"
"Peter Seibel"

與此不同的是, 當一個客戶端切換到事務狀態之后, 服務器會根據這個客戶端發來的不同命令執行不同的操作:

  • 如果客戶端發送的命令為?EXEC?、?DISCARD?、?WATCH?、?MULTI?四個命令的其中一個, 那么服務器立即執行這個命令。
  • 與此相反, 如果客戶端發送的命令是?EXEC?、?DISCARD?、?WATCH?、?MULTI?四個命令以外的其他命令, 那么服務器并不立即執行這個命令, 而是將這個命令放入一個事務隊列里面, 然后向客戶端返回?QUEUED?回復。

事務隊列

每個 Redis 客戶端都有自己的事務狀態, 這個事務狀態保存在客戶端狀態的?mstate?屬性里面:

typedef struct redisClient {// ...// 事務狀態multiState mstate;      /* MULTI/EXEC state */// ...} redisClient;

事務狀態包含一個事務隊列, 以及一個已入隊命令的計數器 (也可以說是事務隊列的長度):

typedef struct multiState {// 事務隊列,FIFO 順序multiCmd *commands;// 已入隊命令計數int count;} multiState;

事務隊列是一個?multiCmd?類型的數組, 數組中的每個?multiCmd?結構都保存了一個已入隊命令的相關信息, 包括指向命令實現函數的指針, 命令的參數, 以及參數的數量:

typedef struct multiCmd {// 參數robj **argv;// 參數數量int argc;// 命令指針struct redisCommand *cmd;} multiCmd;

事務隊列以先進先出(FIFO)的方式保存入隊的命令: 較先入隊的命令會被放到數組的前面, 而較后入隊的命令則會被放到數組的后面。

舉個例子, 如果客戶端執行以下命令:

redis> MULTI
OKredis> SET "name" "Practical Common Lisp"
QUEUEDredis> GET "name"
QUEUEDredis> SET "author" "Peter Seibel"
QUEUEDredis> GET "author"
QUEUED

那么服務器將為客戶端創建事務狀態:

  • 最先入隊的?SET?命令被放在了事務隊列的索引?0?位置上。
  • 第二入隊的?GET?命令被放在了事務隊列的索引?1?位置上。
  • 第三入隊的另一個?SET?命令被放在了事務隊列的索引?2?位置上。
  • 最后入隊的另一個?GET?命令被放在了事務隊列的索引?3?位置上。

執行事務

當一個處于事務狀態的客戶端向服務器發送?EXEC?命令時, 這個?EXEC?命令將立即被服務器執行: 服務器會遍歷這個客戶端的事務隊列, 執行隊列中保存的所有命令, 最后將執行命令所得的結果全部返回給客戶端。

EXEC?命令的實現原理可以用以下偽代碼來描述:

def EXEC():# 創建空白的回復隊列reply_queue = []# 遍歷事務隊列中的每個項# 讀取命令的參數,參數的個數,以及要執行的命令for argv, argc, cmd in client.mstate.commands:# 執行命令,并取得命令的返回值reply = execute_command(cmd, argv, argc)# 將返回值追加到回復隊列末尾reply_queue.append(reply)# 移除 REDIS_MULTI 標識,讓客戶端回到非事務狀態client.flags &= ~REDIS_MULTI# 清空客戶端的事務狀態,包括:# 1)清零入隊命令計數器# 2)釋放事務隊列client.mstate.count = 0release_transaction_queue(client.mstate.commands)# 將事務的執行結果返回給客戶端send_reply_to_client(client, reply_queue)

WATCH命令的實現

WATCH命令是一個樂觀鎖,它可以在EXEC命令執行之前,監視任意數量的數據庫鍵,并在EXEC執行后,檢查被監視的鍵是否至少有一個被修改,如果是,服務器拒絕執行事務,并向客戶端返回代表事務執行失敗的回復。

/* Redis database representation. There are multiple databases identified* by integers from 0 (the default database) up to the max configured* database. The database number is the 'id' field in the structure. */
typedef struct redisDb {dict *dict;                 /* The keyspace for this DB 數據庫鍵空間,保存數據庫中所有的鍵值對*/dict *expires;              /* Timeout of keys with a timeout set 保存過期時間*/dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP) */dict *ready_keys;           /* Blocked keys that received a PUSH 已經準備好數據的阻塞狀態的key*/dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS 事物模塊,用于保存被WATCH命令所監控的鍵*/// 當內存不足時,Redis會根據LRU算法回收一部分鍵所占的空間,而該eviction_pool是一個長為16數組,保存可能被回收的鍵// eviction_pool中所有鍵按照idle空轉時間,從小到大排序,每次回收空轉時間最長的鍵struct evictionPoolEntry *eviction_pool;    /* Eviction pool of keys */// 數據庫IDint id;                     /* Database ID */// 鍵的平均過期時間long long avg_ttl;          /* Average TTL, just for stats */
} redisDb;

在每個代表數據庫的 server.h/redisDb?結構類型中, 都保存了一個?watched_keys?字典, 字典的鍵是這個數據庫被監視的鍵, 而字典的值則是一個鏈表, 鏈表中保存了所有監視這個鍵的客戶端。比如說,以下字典就展示了一個?watched_keys?字典的例子:

每個key后掛著監視自己的客戶端。

監控的觸發

在任何對數據庫鍵空間(key space)進行修改的命令成功執行之后 (比如?FLUSHDB?、?SET?、?DEL?、?LPUSH?、?SADD?、?ZREM?,諸如此類),?multi.c/touchWatchedKey?函數都會被調用 (修改命令會調用signalModifiedKey()函數來處理數據庫中的鍵被修改的情況,該函數直接調用touchWatchedKey()函數)—— 它檢查數據庫的?watched_keys?字典, 看是否有客戶端在監視已經被命令修改的鍵, 如果有的話, 程序將所有監視這個/這些被修改鍵的客戶端的?REDIS_DIRTY_CAS?選項打開:
?

/* "Touch" a key, so that if this key is being WATCHed by some client the* next EXEC will fail. */
// Touch 一個 key,如果該key正在被監視,那么客戶端會執行EXEC失敗 
void touchWatchedKey(redisDb *db, robj *key) {list *clients;listIter li;listNode *ln;// 字典為空,沒有任何鍵被監視if (dictSize(db->watched_keys) == 0) return;// 獲取所有監視這個鍵的客戶端 	clients = dictFetchValue(db->watched_keys, key);// 沒找到返回if (!clients) return;/* Mark all the clients watching this key as CLIENT_DIRTY_CAS *//* Check if we are already watching for this key */// 遍歷所有客戶端,打開他們的 REDIS_DIRTY_CAS 標識listRewind(clients,&li);while((ln = listNext(&li))) {client *c = listNodeValue(ln);// 設置CLIENT_DIRTY_CAS標識c->flags |= CLIENT_DIRTY_CAS;}
}

事務的ACID性質

?在傳統的關系式數據庫中,常常用?ACID 性質來檢驗事務功能的安全性。

redis事物總是具有前三個性質。

a)原子性atomicity:redis事務保證事務中的命令要么全部執行要不全部不執行。

但是redis不同于傳統關系型數據庫,不支持回滾,即使出現了錯誤,事務也會繼續執行下去。

因為redis作者認為,這種復雜的機制和redis追求的簡單高效不符。并且,redis事務錯誤通常是編程錯誤,只會出現在開發環境中,而不會出現在實際生產環境中,所以沒必要支持回滾。

b)一致性consistency:redis事務可以保證命令失敗的情況下得以回滾,數據能恢復到沒有執行之前的樣子,是保證一致性的,除非redis進程意外終結。

Redis 的一致性問題可以分為三部分來討論:入隊錯誤、執行錯誤、Redis 進程被終結。

入隊錯誤

在命令入隊的過程中,如果客戶端向服務器發送了錯誤的命令,比如命令的參數數量不對,等等, 那么服務器將向客戶端返回一個出錯信息, 并且將客戶端的事務狀態設為?REDIS_DIRTY_EXEC?。
因此,帶有不正確入隊命令的事務不會被執行,也不會影響數據庫的一致性。

執行錯誤

如果命令在事務執行的過程中發生錯誤,比如說,對一個不同類型的 key 執行了錯誤的操作, 那么 Redis 只會將錯誤包含在事務的結果中, 這不會引起事務中斷或整個失敗,不會影響已執行事務命令的結果,也不會影響后面要執行的事務命令, 所以它對事務的一致性也沒有影響。

Redis 進程被終結

如果 Redis 服務器進程在執行事務的過程中被其他進程終結,或者被管理員強制殺死,那么根據 Redis 所使用的持久化模式,可能有以下情況出現:

內存模式:如果 Redis 沒有采取任何持久化機制,那么重啟之后的數據庫總是空白的,所以數據總是一致的。

RDB 模式:在執行事務時,Redis 不會中斷事務去執行保存 RDB 的工作,只有在事務執行之后,保存 RDB 的工作才有可能開始。所以當 RDB 模式下的 Redis 服務器進程在事務中途被殺死時,事務內執行的命令,不管成功了多少,都不會被保存到 RDB 文件里。恢復數據庫需要使用現有的 RDB 文件,而這個 RDB 文件的數據保存的是最近一次的數據庫快照(snapshot),所以它的數據可能不是最新的,但只要 RDB 文件本身沒有因為其他問題而出錯,那么還原后的數據庫就是一致的。

AOF 模式:因為保存 AOF 文件的工作在后臺線程進行,所以即使是在事務執行的中途,保存 AOF 文件的工作也可以繼續進行,因此,根據事務語句是否被寫入并保存到 AOF 文件,有以下兩種情況發生:

1)如果事務語句未寫入到 AOF 文件,或 AOF 未被 SYNC 調用保存到磁盤,那么當進程被殺死之后,Redis 可以根據最近一次成功保存到磁盤的 AOF 文件來還原數據庫,只要 AOF 文件本身沒有因為其他問題而出錯,那么還原后的數據庫總是一致的,但其中的數據不一定是最新的。

2)如果事務的部分語句被寫入到 AOF 文件,并且 AOF 文件被成功保存,那么不完整的事務執行信息就會遺留在 AOF 文件里,當重啟 Redis 時,程序會檢測到 AOF 文件并不完整,Redis 會退出,并報告錯誤。需要使用 redis-check-aof 工具將部分成功的事務命令移除之后,才能再次啟動服務器。還原之后的數據總是一致的,而且數據也是最新的(直到事務執行之前為止)。
?

c)隔離性Isolation:redis事務是嚴格遵守隔離性的,原因是redis是單進程單線程模式,可以保證命令執行過程中不會被其他客戶端命令打斷。

因為redis使用單線程執行事務,并且保證不會中斷,所以肯定有隔離性。

d)持久性Durability:持久性是指:當一個事務執行完畢,結果已經保存在永久介質里,比如硬盤,所以即使服務器后來停機了,結果也不會丟失

redis事務是不保證持久性的,這是因為redis持久化策略中不管是RDB還是AOF都是異步執行的,不保證持久性是出于對性能的考慮。


重點提煉

  • 事務提供了一種將多個命令打包, 然后一次性、有序地執行的機制。
  • 多個命令會被入隊到事務隊列中, 然后按先進先出(FIFO)的順序執行。
  • 事務在執行過程中不會被中斷, 當事務隊列中的所有命令都被執行完畢之后, 事務才會結束。
  • 帶有?WATCH?命令的事務會將客戶端和被監視的鍵在數據庫的?watched_keys?字典中進行關聯, 當鍵被修改時, 程序會將所有監視被修改鍵的客戶端的?REDIS_DIRTY_CAS?標志打開。
  • 只有在客戶端的?REDIS_DIRTY_CAS?標志未被打開時, 服務器才會執行客戶端提交的事務, 否則的話, 服務器將拒絕執行客戶端提交的事務。
  • Redis 的事務總是保證 ACID 中的原子性、一致性和隔離性, 當服務器運行在 AOF 持久化模式下, 并且?appendfsync?選項的值為?always?時, 事務也具有耐久性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445304.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445304.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445304.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度學習(06)-- Network in Network(NIN)

文章目錄目錄1.NIN 結構2.MLP卷積3.全局均值池化4.總體網絡架構5.NIN補充5.1 廣義線性模型(GLM)的局限性5.2 CCCP層5.3 1*1卷積核作用(補充)6.手勢識別RGB圖像--NIN結構目錄 1.NIN 結構 2.MLP卷積 傳統CNN的局部感受野窗口的運算…

Pytorch(2)-tensor常用操作

tensor常用數學操作1. 隨機數1.1 torch.rand() - 均勻分布數字1.2 torch.randn() - 正態分布數字2. 求和2.1 torch.sum(data, dim)2.2 numpy.sum(data, axis)3. 求積3.1 點乘--對應位置相乘3.2 矩陣乘法4. 均值、方差4.1 torch tensor.mean() .std()4.2 numpy array.mean() .st…

Java編程規約(OOP)

1、【強制】避免通過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增加編譯器解析 成本,直接用類名來訪問即可。 2、【強制】所有的覆寫方法,必須加Override 注解。 說明:getObject()與 get0bject()的問題。一個是字母的…

深度學習(07)-- 經典CNN網絡結構(Inception (v1-v4))

文章目錄目錄1.Inception介紹1.1 Inception結構1.2 Inception V1(GoogleNet)1.3 Inception V2(Batch Norm)1.4 Inception V3(Factorization)1.5 Inception V4(ResNet)1.5 Inception v1~v4 總結1.6 Inception進階2.Inception實現目…

Python(13)-函數,lambda語句

函數1 函數定義2 函數調用3 函數注釋文檔4 函數參數4.1 參數列表,默認參數,任意參數4.1.1 無缺省值參數4.1.2(部分)缺省值參數4.1.3 數量不定形參數4.2 可變對象和不可變對象4.3 作用域4.3.1 globals()函數4.3.2 global 聲明變量為全局變量5 函數返回值5…

深度學習(08)-- Residual Network (ResNet)

文章目錄目錄1.殘差網絡基礎1.1基本概念1.2VGG19、ResNet34結構圖1.3 梯度彌散和網絡退化1.4 殘差塊變體1.5 ResNet模型變體1.6 Residual Network補充1.7 1*1卷積核(補充)2.殘差網絡介紹(何凱明)3.ResNet-50(Ng)3.1 非常深的神經網…

Python(14)-模塊

模塊Python標準庫,第三方庫都是一個個模塊,我們還可以編寫自己的模塊。模塊python程序架構的核心模塊,模塊是一個工具包。 每一個以.py為擴展名的源代碼文件都是一個模塊。 想要使用工具包中的工具,可以使用Import的方式導入。 …

redis——命令請求的執行過程

發送命令請求 當用戶在客戶端中鍵入一個命令請求時, 客戶端會將這個命令請求轉換成協議格式, 然后通過連接到服務器的套接字, 將協議格式的命令請求發送給服務器。 讀取命令請求 當客戶端與服務器之間的連接套接字因為客戶端的寫入而變得可…

深度學習(09)-- DenseNet

文章目錄目錄1.DenseNet網絡結構2.稠密連接及其優點3.代碼實現4.補充說明目錄 1.DenseNet網絡結構 2.稠密連接及其優點 每層以之前層的輸出為輸入,對于有L層的傳統網絡,一共有L個連接,對于DenseNet,則有L*(L1)/2。 這篇論文主要…

redis——緩存擊穿/穿透/雪崩

緩存穿透 一般的緩存系統,都是按照key去緩存查詢,如果不存在對應的value,就去后端系統查找(比如DB)。 一些惡意的請求會故意查詢不存在的key,請求量很大,就會對后端系統造成很大的壓力。這就叫做緩存穿透…

python(15)-window7配置iPython

前提:安裝了Pythonanaconda anaconda安裝參考:https://www.zhihu.com/question/58033789 在window系統下可以使用兩種方法來實現類似與于Linux終端命令運行程序的方法(推薦方式2): 1.cmd:自己沒有操作過,可以參考下面…

深度學習(10)-- Capsules Networks(CapsNet)

版權聲明&#xff1a;本文為博主原創文章&#xff0c;未經博主允許不得轉載。 https://blog.csdn.net/malele4th/article/details/79430464 </div><div id"content_views" class"markdown_views"><!-- flowchart 箭頭圖標 勿刪 --&g…

手把手maven的功能/安裝/使用/idea集成

看這篇文章不用著急安裝&#xff0c;跟著步驟一定會成功&#xff0c;要理解maven是什么&#xff0c;如何使用。 介紹 maven官網 對于一個小白來說&#xff0c;官網有用的信息就是這些 不管如何介紹maven&#xff0c;作為使用者來說&#xff0c;主要感覺兩個方面有幫助&#x…

python(16)-列表list,for循環

高級數據類型--列表1列表定義2列表中取值3列表的增&#xff0c;刪&#xff0c;查&#xff0c;改3.1修改指定位置的數據3.2確定指定元素的索引3.3增加操作3.4刪除操作3.5 元素是否存在與列表中 in3.6在指定索引位置插入元素4列表的數據統計5列表排序6列表的循環遍歷-for7多維度l…

深度學習(11)-- GAN

TensorFlow &#xff08;GAN&#xff09; 目錄 TensorFlow &#xff08;GAN&#xff09;目錄1、GAN1.1 常見神經網絡形式1.2 生成網絡1.3 新手畫家 & 新手鑒賞家1.4 GAN網絡1.5 例子 1、GAN 今天我們會來說說現在最流行的一種生成網絡, 叫做 GAN, 又稱生成對抗網絡, 也…

Python外(1)--try-expect

語法&#xff1a; try:正常情況下執行的代碼塊 expect 錯誤類型1:&#xff08;可選&#xff09;錯誤類型1對應的處理方案 expect 錯誤類型2:&#xff08;可選&#xff09;錯誤類型2對應的處理方案 expect:剩下的錯誤類型對應的處理方案 else:&#xff08;可選&#xff09;沒有…

redis——數據結構和對象的使用介紹

redis官網 微軟寫的windows下的redis 我們下載第一個 額案后基本一路默認就行了 安裝后&#xff0c;服務自動啟動&#xff0c;以后也不用自動啟動。 出現這個表示我們連接上了。 redis命令參考鏈接 String 字符串結構 struct sdshdr{//記錄buf數組中已使用字節的數量int …

Python模塊(1)-Argparse 簡易使用教程

argparse 簡易使用教程1.概況2. action3. argparse 使用demo3.1 argparse 實現加法器3.2 D-Model parser1.概況 argparse是Python中用于解析命令行參數的一個模塊&#xff0c;可以自動生成help和usage信息&#xff1b;當從終端輸入的參數無效時&#xff0c;模塊會輸出提示信息…

深度學習--Keras總結

Keras主要包括14個模塊&#xff0c;本文主要對Models、layers、Initializations、Activations、Objectives、Optimizers、Preprocessing、metrics共計8個模塊分別展開介紹&#xff0c;并通過一個簡單的Bp神經網絡說明各個模塊的作用。 1. Model 包&#xff1a;keras.models …

redis——NOSQL及redis概述

NoSql入門概述 單機Mysql的美好時代 瓶頸&#xff1a; 數據庫總大小一臺機器硬盤內存放不下數據的索引&#xff08;B tree&#xff09;一個機器的運行內存放不下訪問量&#xff08;讀寫混合&#xff09;一個實例不能承受Memcached&#xff08;緩存&#xff09; MySql 垂直拆…