原子性 原子性是資料庫的事務中的特性。在資料庫事務的情景下,原子性指的是:一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。 對於Redis而言,命令的原子性指的是:一個操作的不可以再分,操作要麼執行,要麼不執行。 Redis操作原子性的原因 Re ...
原子性
原子性是資料庫的事務中的特性。在資料庫事務的情景下,原子性指的是:一個事務(transaction)中的所有操作,要麼全部完成,要麼全部不完成,不會結束在中間某個環節。
對於Redis而言,命令的原子性指的是:一個操作的不可以再分,操作要麼執行,要麼不執行。
Redis操作原子性的原因
Redis的操作之所以是原子性的,是因為Redis是==單線程的==。
由於對操作系統相關的知識不是很熟悉,從上面這句話並不能真正理解Redis操作是原子性的原因,進一步查閱進程與線程的概念及其區別。
進程與線程
- 進程
電腦中已執行程式的實體。比如,一個啟動了的php-fpm,就是一個進程。 - 線程
操作系統能夠進行運算調度的最小單元。它被包含在進程之中,是進程的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程並行執行不同的任務。比如,mysql運行時,mysql啟動後,該mysql服務就是一個進程,而mysql的連接、查詢的操作,就是線程。
進程與線程的區別
- 資源(如打開文件):進程間的資源相互獨立,同一進程的各線程間共用資源。某進程的線程在其他進程不可見。
- 通信:進程間通信:消息傳遞、同步、共用記憶體、遠程過程調用、管道。線程間通信:直接讀寫進程數據段(需要進程同步和互斥手段的輔助,以保證數據的一致性)。
- 調度和切換:線程上下文切換比進程上下文切換要快得多。
線程,是操作系統最小的執行單元,在單線程程式中,任務一個一個地做,必須做完一個任務後,才會去做另一個任務。
Redis在併發中的表現
Redis的API是原子性的操作,那麼多個命令在併發中也是原子性的嗎?
看看下麵這段代碼:
$redis= newRedis();
$redis->connect('127.0.0.1',6379);
for($i= 0;$iget('val');
$num++;
$redis->set('val',$num);
usleep(10000);
}
用兩個終端執行上面的程式,發現val的結果是小於2000的值,那麼可以知道,在程式中執行多個Redis命令並非是原子性的,這也和普通資料庫的表現是一樣的。
如果想在上面的程式中實現原子性,可以將get和set改成單命令操作,==比如incr,或者使用Redis的事務,或者使用Redis+Lua==的方式實現。
原子性總結
綜上所述,對Redis來說,執行get、set以及eval等API,都是一個一個的任務,這些任務都會由Redis的線程去負責執行,任務要麼執行成功,要麼執行失敗,這就是Redis的命令是原子性的原因。
Redis本身提供的所有API都是原子操作,Redis中的事務其實是要保證批量操作的原子性。
事務
MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事務相關的命令。事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:
- 事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
- 事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。
事務的關鍵字
- EXEC 命令負責觸發並執行事務中的所有命令:
如果客戶端在使用 MULTI 開啟了一個事務之後,卻因為斷線而沒有成功執行 EXEC ,那麼事務中的所有命令都不會被執行。 - 另一方面,如果客戶端成功在開啟事務之後執行 EXEC ,那麼事務中的所有命令都會被執行。
當使用 AOF 方式做持久化的時候, Redis 會使用單個 write(2) 命令將事務寫入到磁碟中。
然而,如果 Redis 伺服器因為某些原因被管理員殺死,或者遇上某種硬體故障,那麼可能只有部分事務命令會被成功寫入到磁碟中。
如果 Redis 在重新啟動時發現 AOF 文件出了這樣的問題,那麼它會退出,並彙報一個錯誤。
使用redis-check-aof程式可以修複這一問題:它會移除 AOF 文件中不完整事務的信息,確保伺服器可以順利啟動。
從 2.2 版本開始,Redis 還可以通過樂觀鎖(optimistic lock)實現 CAS (check-and-set)操作,具體信息請參考文檔的後半部分。
事務的語句
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
為什麼 Redis 不支持回滾(roll back)
如果你有使用關係式資料庫的經驗, 那麼 “Redis 在事務失敗時不進行回滾,而是繼續執行餘下的命令”這種做法可能會讓你覺得有點奇怪。
以下是這種做法的優點:
- Redis 命令只會因為錯誤的語法而失敗(並且這些問題不能在入隊時發現),或是命令用在了錯誤類型的鍵上面:這也就是說,從實用性的角度來說,失敗的命令是由編程錯誤造成的,而這些錯誤應該在開發的過程中被髮現,而不應該出現在生產環境中。
- 因為不需要對回滾進行支持,所以 Redis 的內部可以保持簡單且快速。
有種觀點認為 Redis 處理事務的做法會產生 bug , 然而需要註意的是, 在通常情況下, 回滾並不能解決編程錯誤帶來的問題。 舉個例子, 如果你本來想通過 INCR 命令將鍵的值加上 1 , 卻不小心加上了 2 , 又或者對錯誤類型的鍵執行了 INCR , 回滾是沒有辦法處理這些情況的。