<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                >[info] redis 事務 任何數據庫都要有一套自己的事務控制機制,redis事務是一次可以執行多個命令,它的本質是一組命令的 集合。 一個事務中所有的命令都會被序列化,在事務執行的過程中會按照順序執行隊列中的命令。其它客 戶端提交的命令請求會等到事務執行完畢再執行。 * 總的來說:redis事務就是一次性、順序性、排他性的執行一個隊列中的一系列命令。 ***** **與其他數據庫相比:** 1. redis事務是分為三個階段:開始事務、命令入隊、執行事務。 2. redis事務不具有隔離級別的概念:redis在發送exec命令之前,命令操作只是被放入到隊列緩存當中,并 不會被實際執行,因此也就不能類似關系型數據中,在事務內查詢已經變更的操作,事務外的客戶端更不 能查詢到事務內的數據。 3. redis事務是不保證原子性的:redis事務只保證在命令格式只有在都正確的情況下才會都執行,要不就都 不執行命令。但是事務的整體是不保證原子性的,且沒有回滾,當事務中任意一個命令執行失敗,其余的命令依然會執行。 **redis 事務執行過程:** ![](https://img.kancloud.cn/32/4c/324c385f257bb93ea784516c48b590db_1273x824.png) ![](https://img.kancloud.cn/6e/b9/6eb987507211c77e902d2ca3e8c9e03a_1018x304.png) ***** **事務命令入隊過程:** ![](https://img.kancloud.cn/a0/ff/a0ffbb04bf509ad46a5e3c47ae3e7bf9_1135x642.png) ***** **事務ACID:** ![](https://img.kancloud.cn/db/9d/db9d945cb5672c2e16357abfbed8bcb8_1021x663.png) 1. 持久性 事務的耐久性指的是,當一個事務執行完畢時,執行這個事務所得的結果巳經被保存到 永久性存儲介質 (比如硬盤)里面了, 即使服務器在事務執行完畢 之后停機, 執行事務所得的結果也不會丟失。Redis事 務的耐久性由服務器所使用持久化模式決定的: ``` (1) 當服務器在無持久化的內存模式下運作時,事務不具有耐久性。因為一旦服務器停機, 服務器所有的數據都將丟失。 (2) 當服務器在ROB持久化模式下運作時,事務同樣不具有耐久性。因為服務器只會在特定的保存條 件下才會執行BGSAVE命令,并且異步執行的BGSAVE命令不能保證事務的數據第一時間被保存到硬盤 上。 (3) 當服務器運行在AOF持久化模式下,并且appendfsync選項的值為always時,程序總會在執行命 令之后調用同步(sync)函數,將命令數據真正地保存到硬盤里。 ``` 2. 隔離性 事務的隔離性指的是,即使數據庫中有多個事務并發地執行,各個事務之間也不會互相 影響,并且在并發 狀態下執行的事務和串行執行的事務產生的結果完全相同。 因為Redis使用單線程的方式來執行事務(以 及事務隊列中的命令),并且服務器保證, 在執行事務期間不會對事務進行中斷,因此,Redis的事務總是 以串行的方式運行的,并且 事務也總是具有隔離性的。 3. 一致性 事務的一致性是指,如果數據庫執行前是一致的,那么在事務執行后,無論事務是否執行成功,數據庫也 應該是一致的。 >[] 入隊錯誤 如果一個事務在入隊命令的過程中,出現了命令不存在,或者命令的格式不正確等情況, 那么Redis將拒絕執行這個事務。因為服務器會拒絕執行人隊過程中出現錯誤的事務, 所以Redis事務的一致性不會被帶有入 隊錯誤的事務影響。 ``` 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set msg heelo QUEUED 127.0.0.1:6379(TX)> get msg QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) "heelo" 127.0.0.1:6379> ``` **Redis 2.6.5以前的入隊錯誤處理:Redis會忽略錯誤的命令,而正確的命令如上面的SET和GET仍然 會被執行。** >[] 執行錯誤 執行錯誤通常都是一些不能在入隊時被服務器發現的錯誤, 這些錯誤只會在命令實際執行時被觸發。即使 在事務的執行過程中發生了錯誤, 服務器也不會中斷事務的執行, 它會繼續執行事務中余下的其他命令, 并且已執行的命令(包括執行命令所產生的結果)不會被出錯的命令影響。 因為在事務執行的過程中, 出錯的命令會被服務器識別出來, 并進行相應的錯誤處理, 所以這些出錯命令不會對數據庫做任何修改, 也不會對事務的一致性產生任何影響。 ~~~ 127.0.0.1:6379> set msg hello OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> sadd fruit apple banana cherry QUEUED 127.0.0.1:6379(TX)> rpush msg bye redis QUEUED 127.0.0.1:6379(TX)> sadd alphabet a b c QUEUED 127.0.0.1:6379(TX)> exec 1) (integer) 3 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) (integer) 3 127.0.0.1:6379> ~~~ 還有一種情況是與Redis的持久化相關,這里暫時不做解釋,在之后的課程會進行補充。 ***** 4. 原子性 事務具有原子性指的是, 數據庫將事務中的多個操作當作一個整體來執行,服務器要么就執行事務中的所 有操作, 要么就一個操作也不執行。 對于Redis的事務功能來說,事務隊列中的命令要么就全部都執行, 要么就一個都不執行,因此, Redis的事務是具有原子性的。 下面是一個執行失敗的事務,這個事務因為 命令入隊出錯而被服務器拒絕執行: ***** Redis的事務和傳統的關系型數據庫事務的最大區別在于,Redis不支持事務回滾機制(rollback), 即使事務隊 列中的某個命令在執行期間出現了錯誤,整個事務也會繼續執行下去,直到將事務隊列中的所有命令都執 行完畢為止。 下面展示了即使RPUSH命令在執行期間出現了錯誤,事務的后續命令也會繼續執行下去, 并且之前執行的命令也不會有任何影響: ***** **Redis為什么不支持回滾:** 不支持事務回滾是因為這種復雜的功能和Redis追求簡單高效的設計主旨不相符, 并且Redis事務的執行時錯誤通常都是編程錯誤產生的, 這種錯誤通常只會出現在開發環境中, 而很少會在 實際的生產環境中出現。 ![](https://img.kancloud.cn/ce/4b/ce4bcc1096b8df9abfdcac685502667b_957x184.png) ***** >[info] redis 樂觀鎖 **事務WATCH命令監控:** Redis Watch 命令用于監視一個(或多個) key ,如果在事務執行之前這個(或這些) key 被其他命令所改動, 那么事務將被打斷。 ``` 開啟第一個客戶端 127.0.0.1:6379> watch name OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set name starsky QUEUED 127.0.0.1:6379> get name QUEUED 127.0.0.1:6379> 開啟第二個客戶端 127.0.0.1:6379> watch name OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set name harry QUEUED 127.0.0.1:6379> get name QUEUED 127.0.0.1:6379> 客戶端一,執行事務 127.0.0.1:6379> exec 1) OK 2) "starsky" 127.0.0.1:6379> 客戶端二,執行事務 127.0.0.1:6379> exec (nil) 127.0.0.1:6379> ``` ***** **樂觀鎖的實現方式:** ![](https://img.kancloud.cn/6c/c1/6cc1dbc1e698fc0f7522b8c0930e2556_1260x299.png) >[] MVCC方式實現 一般是在數據表中加上版本號字段 version,表示數據被修改的次數。當數據被修改時,這個字段值會加1。 ``` 舉個簡單的例子:假設帳戶信息表中有一個 version 字段,當前值為 1 ,而當前帳戶的余額( balance )為 100 。 +----+--------+---------+ | id | price | version | +----+--------+---------+ | 1 | 100.00 | 1 | +----+--------+---------+ 1 row in set (0.01 sec) 1.操作員 A 此時準備將其讀出( version=1 ),并從其帳戶余額中扣除 50( 100-50 ); mysql> select * from mvcc_version where id=1 and version=1; +----+--------+---------+ | id | price | version | +----+--------+---------+ | 1 | 100.00 | 1 | +----+--------+---------+ 1 row in set (0.01 sec) 2.操作員 A 操作的過程中,操作員 B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 20 ( 100-20 ); mysql> select * from mvcc_version where id=1 and version=1; +----+--------+---------+ | id | price | version | +----+--------+---------+ | 1 | 100.00 | 1 | +----+--------+---------+ 1 row in set (0.01 sec) 3.操作員 A 完成修改工作,將數據版本號加1( version=2 ),連同帳戶扣除后余額( balance=50 ),提交到數據庫完成更新; mysql> update mvcc_version set price=price-50,version=version+1 where id=1 and version=1; Query OK, 1 row affected (0.00 sec)//1條數據受到影響 Rows matched: 1 Changed: 1 Warnings: 0 4.操作員 B 完成了操作,也將版本號加1( version=2 )試圖向數據庫提交數據( balance=80 ),但此時比對數據庫記錄版本發現,操作員 B 提交的數據版本號為 2 ,數據庫記錄的當前版本 也為 2 ,不滿足 “提交版本必須大于記錄當前版本才能執行更新“ 的樂觀鎖策略。 mysql> update mvcc_version set price=price-50,version=version+1 where id=1 and version=1; Query OK, 0 rows affected (0.01 sec)//受到影響的條數為0 Rows matched: 0 Changed: 0 Warnings: 0 ``` 因此,操作員 B 的提交被駁回。這樣,就避免了操作員 B 用基于 version=1 的舊數據修改,最終造成覆蓋 操作員 A 操作結果的可能。 ***** >[] redis 事務 watch 監控 key(模擬秒殺) 事務一: ![](https://img.kancloud.cn/69/93/6993efe7a667afadee84a778235b909c_645x792.png) 事務二: ![](https://img.kancloud.cn/14/57/145747d1cda57a45bb0e7cd75a05383e_477x466.png) ***** >[info] stream 數據類型 Redis Stream 主要用于消息隊列(MQ,Message Queue),Redis 本身是有一個 Redis 發布訂閱 (pub/sub) 來實現消息隊列的功能,但它有個缺點就是消息無法持久化,如果出現網絡斷開、Redis 宕機等,消息就會 被丟棄。 ***** **關于消息隊列:** >[] 什么是消息隊列? 把數據放到消息隊列叫做:**生產者** 從消息隊列里邊取數據叫做:**消費者** ![](https://img.kancloud.cn/23/b3/23b364db4ee114b551fd10d2fc55abdd_1145x643.png) 我們知道隊列 Queue 是一種先進先出的數據結構,所以消費消息時也是按照順序來消費的。比如生產者發 送消息1,2,3...對于消費者就會按照1,2,3...的順序來消費。但是偶爾也會出現消息被消費的順序不對的情況, 比如某個消息消費失敗又或者一個 queue 多個consumer 也會導致消息被消費的順序不對,我們一定要保 證消息被消費的順序正確。 ***** >[info] 為什么使用消息隊列? 在不使用消息隊列服務器的時候,用戶的請求數據直接寫入數據庫,在高并發的情況下數據庫壓力劇增, 使得響應速度變慢。但是在使用消息隊列之后,用戶的請求數據發送給消息隊列之后立即 返回,再由消息 隊列的消費者進程從消息隊列中獲取數據,異步寫入數據庫。由于消息隊列服務器處理速度快于數據庫 (消息隊列也比數據庫有更好的伸縮性),因此響應速度得到大幅改善。 ***** 通過以上分析我們可以得出消息隊列具有很好的削峰作用的功能——即通過異步處理,將短時間高并發產 生的事務消息存儲在消息隊列中,從而削平高峰期的并發事務。 ***** 舉例:在電子商務一些秒殺、促銷活動中,合理使用消息隊列可以有效抵御促銷活動剛開始大量訂單涌入 對系統的沖擊。 因為用戶請求數據寫入消息隊列之后就立即返回給用戶了,但是請求數據在后續的業務校驗、寫數據庫等 操作中可能失敗。因此使用消息隊列進行異步處理之后,需要適當修改業務流程進行配合,比如用戶在提 交訂單之后,訂單數據寫入消息隊列,不能立即返回用戶訂單提交成功,需要在消息隊列的訂單消費者進 程真正處理完該訂單之后,甚至出庫后,再通過電子郵件或短信通知用戶訂單成功,以免交易糾紛。這就 類似我們平時手機訂火車票和電影票。 ***** >[] 缺點 1. 系統可用性降低: 系統可用性在某種程度上降低,為什么這樣說呢?在加入MQ之前,你不用考慮消 息丟失或者說MQ掛掉等等的情況,但是,引入MQ之后你就需要去考慮了! 2. 系統復雜性提高: 加入MQ之后,你需要保證消息沒有被重復消費、處理消息丟失的情況、保證消息 傳遞的順序性等等問題! 3. 一致性問題: 我上面講了消息隊列可以實現異步,消息隊列帶來的異步確實可以提高系統響應速度。 但是,萬一消息的真正消費者并沒有正確消費消息怎么辦?這樣就會導致數據不一致的情況了! ***** **stream數據類型:** Redis5.0中發布的Stream類型,也用來實現典型的 **消息隊列**。該Stream類型的出現,幾乎滿足了消息隊列具 備的全部內容,包括但不限于: * 消息ID的序列化生成 * 消息遍歷 * 消息的阻塞和非阻塞讀取 * 消息的分組消費 * 未完成消息的處理 * 消息隊列監控 >[] stream類型的使用 **xadd命令:** 命令用于在某個stream中追加消息 ``` 127.0.0.1:6379> xadd memberMessage * user starsky age 20 "1610349286147-0" 127.0.0.1:6379> xadd memberMessage * user will age 30 "1610349299077-0" 127.0.0.1:6379> ``` 格式: ``` XADD key ID field string [field string ...] 需要提供key,消息ID方案,消息內容,其中消息內容為key-value型數據。 ID,最常使用*,表示由Redis生成消息ID,這也是強烈建議的方案。 field string [field string], 就是當前消息內容,由1個或多個key-value構成。 ``` ***** **xlen命令:** 返回結果為stream數據類型的長度 ``` 127.0.0.1:6379> xlen memberMessage (integer) 2 127.0.0.1:6379> ``` ***** **xrange命令:** 獲取消息列表,會自動過濾已經刪除的消息 ``` # -表示最小值, +表示最大值 127.0.0.1:6379> xrange memberMessage - + 1) 1) "1610349286147-0" 2) 1) "user" 2) "starsky" 3) "age" 4) "20" 2) 1) "1610349299077-0" 2) 1) "user" 2) "will" 3) "age" 4) "30" ``` ***** **xread命令:** 我們可以在不定義消費組的情況下進行Stream消息的獨立消費,當Stream沒有新消息時,甚至可以阻塞等 待。Redis設計了一個單獨的消費指令xread,可以將Stream當成普通的消息隊列(list)來使用。使用xread時, 我們可以完全忽略消費組(Consumer Group)的存在,就好比Stream就是一個普通的列表(list)。 ``` 從Stream頭部讀取兩條消息 127.0.0.1:6379> xread count 1 streams memberMessage 0-0 1) 1) "memberMessage" 2) 1) 1) "1610349286147-0" 2) 1) "user" 2) "starsky" 3) "age" 4) "20" ``` ***** **xgroup create命令:** Stream通過xgroup create指令創建消費組(Consumer Group),需要傳遞起始消息ID參數用來初始 化last_delivered_id變量。 ``` 127.0.0.1:6379> xgroup create memberMessage starsky 0-0 # 表示從頭開始消費 OK 127.0.0.1:6379> xgroup create memberMessage will $ # $表示從尾部開始消費,只接受新消 息,當前Stream消息會全部忽略 OK 127.0.0.1:6379> ``` ***** **xinfo命令:** 獲取Stream信息 ``` 127.0.0.1:6379> xinfo stream memberMessage 1) "length" 2) (integer) 2 共2個消息 3) "radix-tree-keys" 4) (integer) 1 5) "radix-tree-nodes" 6) (integer) 2 7) "last-generated-id" 8) "1610349299077-0" 9) "groups" 10) (integer) 2 #兩個消費組 11) "first-entry" 第一個消息 12) 1) "1610349286147-0" 2) 1) "user" 2) "starsky" 3) "age" 4) "20" 13) "last-entry" 最后一個消息 14) 1) "1610349299077-0" 2) 1) "user" 2) "will" 3) "age" 4) "30" ``` ***** **xreadgroup group命令:** Stream提供了xreadgroup指令可以進行消費組的組內消費,需要提供消費組名稱、消費者名稱和起始消息ID。它同xread一樣,也可以阻塞等待新消息。讀到新消息后,對應的消息ID就會進入消費者的PEL(正在處 理的消息)結構里,客戶端處理完畢后使用xack指令通知服務器,本條消息已經處理完畢,該消息ID就會從 PEL中移除。 ``` # >號表示從當前消費組的last_delivered_id后面開始讀 # 每當消費者讀取一條消息,last_delivered_id變量就會前進 127.0.0.1:6379> xreadgroup GROUP starsky xk count 1 streams memberMessage > 1) 1) "memberMessage" 2) 1) 1) "1610349286147-0" 2) 1) "user" 2) "starsky" 3) "age" 4) "20" 127.0.0.1:6379> xreadgroup GROUP starsky xk count 1 streams memberMessage > 1) 1) "memberMessage" 2) 1) 1) "1610349299077-0" 2) 1) "user" 2) "will" 3) "age" 4) "30" 127.0.0.1:6379> ``` ***** **xack命令:** ``` 127.0.0.1:6379> xack memberMessage starsky 1610349286147-0 (integer) 1 127.0.0.1:6379> ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看