我們知道,java中的 i++ 或者 ++i 這些自增操作不具備原子性,因為自增操作在我們程式碼層面是一個指令,但是在 jvm 底層,它分為這兩個步驟:
若 jvm 在執行第 1 步的時候,其他執行緒先於這個執行緒改變了 i 的值,然後執行第二步後得出的值就不是我們希望的。
在 redis 中也有類似的自增操作,如我們之前學的 string 資料型別中的 incr 指令,它可以對一個 integer 型別的值加1。但是 incr 指令與 java 自增不一樣的是,它是一個原子操作,因為 incr 是呼叫作業系統底層的指令,它不會被其他執行緒打斷。所以 incr 指令具有原子性。
為什麼叫原子性?
原子性其實就是有類似原子的性質,我們知道原子中有質子和中子,他們構成了原子,不可分割,如果分割了,就不是一個原子了,是不是很好理解。整理redis的同時也整理了最近兩個月最新的面試題。需要的朋友可以點選:這個,點這個!!,備註:csdn。
事務就是指一系列操作,這些操作要麼同時成功,要麼同時失敗,它是一種原子操作。原子就是一個整體,不可分割,事務中的這些操作也是一樣。
假設有這樣一個場景:張三向李四轉賬 100 塊錢,最少需要經歷兩個步驟
在沒有事務的情況下,可能會導致 1 號操作執行成功,2 號操作執行失敗,那結果是張三的錢少了 100 塊,李四的錢卻沒增加,張三的 100 塊憑空消失了,這肯定會出問題,所以這兩部操作必須要放在事務中執行,要麼轉賬成功,要麼轉賬失敗,這就是事務的作用。
redis 是單執行緒的程式,每個指令都保證原子性,但是不保證多個指令的原子性。
當有多個使用者端連線同一個 redis 時,使用者端之間會競爭cpu資源,來爭奪 rdis 指令的執行權,所以如果一個 client 要執行一系列指令時,可能會被其他使用者端打斷。
要使多個指令也具有原子性,可以使用 redis 事務
redis 事務保證兩點:
如果 redis 開啟 AOF 模式,在執行 redis 事務之前,redis 會先把事務寫在磁碟中再執行命令。如果事務執行到一半,redis 程序突然就掛了,那麼就會導致事務中只有一部分指令執行成功,還有一部分指令還未執行。這種情況下,在 redis 下一次啟動時會檢測到錯誤,並退出 redis。使用 redis-check-aof 工具可以對 AOF 檔案進行修復,刪除掉磁碟中的未執行成功的事務,然後重新啟動載入 AOF 檔案,redis 就恢復到事務沒有執行建立的狀態,就可以正常啟動了
和 redis 事務有關的指令有:
下面開始介紹每個命令
使用 multi 指令來開啟事務
127.0.0.1:6379> set zhangsan 1000
OK
127.0.0.1:6379> set lisi 1000
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby zhangsan -100
QUEUED
127.0.0.1:6379> incrby lisi 100
QUEUED
multi 指令返回 ok,證明事務開啟成功。
開啟事務後,接下來執行每條正確的指令都返回 QUEUE,代表命令被放入等待佇列,但並沒有執行。
使用 exec 命令來執行事務
127.0.0.1:6379> set zhangsan 1000
OK
127.0.0.1:6379> set lisi 1000
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby zhangsan -100
QUEUED
127.0.0.1:6379> incrby lisi 100
QUEUED
127.0.0.1:6379> exec # 執行事務
1) (integer) 900
2) (integer) 1100
使用 exec 執行事務後,會按順序返回事務中每天指令的返回結果。
還整理了Java核心的知識點,大家也可以看看。需要的朋友可以點選:這個,點這個!!,備註:csdn。
如果事務建立成功,但你又不想執行了,使用 discard 命令來取消
127.0.0.1:6379> multi # 開啟事務
OK
127.0.0.1:6379> incrby zhangsan -100
QUEUED
127.0.0.1:6379> incrby lisi 100
QUEUED
127.0.0.1:6379> discard # 取消事務
OK
樂觀鎖,即認為所有時候都不會出現問題,它不會去加鎖,他會在修改資料的時候判斷這個資料是否被改動過,如果沒有被其他人改動過,就更新資料。
在 redis 中也有樂觀鎖的操作,來防止資料出現問題,現在有如下一個場景:
你想通過事務對 redis 中的 key/value 進行修改,在沒有執行 exec 命令之前,這時候另一個 client 修改了這個 key/value,這樣再執行 exec 後可能會出現問題。這種情況下用 redis 的樂觀鎖操作就可以很輕鬆解決
在 redis 中有一種 check and set(CAS)的樂觀鎖操作,相關命令如下
通過 watch 命令來監視這個 key/value,如果發現該 key/value 被修改,則呼叫 exec 時事務不會被執行
127.0.0.1:6379> watch zhangsan lisi # 監視 zhangsan 和 lisi
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby zhangsan -100
QUEUED
127.0.0.1:6379> incrby lisi 100
QUEUED
================ 這時候zhangsan的值被其他使用者端修改=======================
127.0.0.1:6379> exec # 事務執行失敗
(nil)
取消監視,與 watch 命令相對
在執行 exec 後,不管是否執行成功,watch 命令都會 unwatched
使用者端退出後,watch 命令也會 unwatched
127.0.0.1:6379> watch zhangsan lisi # 監視 zhangsan 和 lisi
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby zhangsan -100
QUEUED
127.0.0.1:6379> incrby lisi 100
QUEUED
127.0.0.1:6379> unwatch # 取消監視
OK
在 redis 事務中,可能會發生兩種錯誤
1、編譯型錯誤
在 multi 後,exec 呼叫之前,由於輸入不正確的 redis 命令導致的錯誤
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sets zhangsan 22 # 事務中命令輸錯
(error) ERR unknown command `sets`, with args beginning with: `zhangsan`, `22`,
127.0.0.1:6379> set zhangsan 22
QUEUED
127.0.0.1:6379> exec # 事務執行不成功
(error) EXECABORT Transaction discarded because of previous errors.
在這種情況下事務不會執行成功
2、執行時錯誤
在 exec 呼叫之後,由於 redis 命令執行不成功導致的錯誤
127.0.0.1:6379> set test abc
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr test # 由於test中存的不是 integer,在exec時會出錯
QUEUED
127.0.0.1:6379> set info aaa
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
從執行結果看出,在 exec 時,即使其中有命令出錯,後面的命令不會被影響,依舊會被執行從這裡也可以看出,redis 不支援回滾
最後提供免費的Java架構學習資料,學習技術內容包含有:Spring,Dubbo,MyBatis, RPC, 原始碼分析,高並行、高效能、分散式,效能優化,微服務 高階架構開發等等。
點選:點這個!點這個!暗號:csdn。
還有Java核心知識點+全套架構師學習資料和視訊+一線大廠面試寶典+面試簡歷模板可以領取+阿里美團網易騰訊小米愛奇藝快手嗶哩嗶哩面試題+Spring原始碼合集+Java架構實戰電子書。