單線程的redis如何實現併發訪問?

在服務器端軟件中, 併發和並行性一般被認爲是不一樣的概念。在服務器中, 支持併發 i/o 意味着服務器可以經過執行與那些客戶端僅有一個計算單元對應的幾個流來爲多個客戶端提供服務。在這種狀況下, 並行性意味着服務器可以同時執行多個操做 (具備多個計算單元), 這是不一樣的。服務器

例如, 一個酒保能夠照顧幾個客戶, 而他只能準備一次飲料。這樣他就能夠不併行地提供併發性。網絡

單線程程序絕對能夠經過使用 i/o 複用機制和事件循環 (Redis用的是事件循環), 在 i/o 級別上提供併發性。併發

並行性具備成本: 在現代硬件上能夠找到多個套接字/多個內核, 線程之間的同步很是昂貴。另外一方面, 像 Redis 這樣的高效存儲引擎的瓶頸每每是網絡--CPU前面的陷阱。所以, 孤立的事件循環 (不須要同步) 被視爲構建高效、可伸縮的服務器的良好設計。線程

Redis 操做是原子的事實僅僅是單線程事件循環的結果。有趣的一點是, 原子性是以不額外的成本提供的 (它不須要同步)。用戶能夠利用它來實現樂觀鎖定和其餘模式, 而無需支付同步開銷。設計

若是 Redis 是單線程的, 那麼爲何須要鎖定機制呢?code

Redis 確實 (大可能是) 單線程, 但當多個客戶端嘗試在相鄰的時間接近時進行不一樣的事情時, 則須要鎖定。在 RiA 中討論的鎖定徹底是關於-確保只有一個客戶端/線程執行特定任務, 或者確保更新不會出錯。事件

這裏有一個例子, 爲何你須要鎖定, 儘管 Redis 有單線程性: 假設你有一個值在 Redis, 一個數字, 例如存儲在一個名爲 foo 的鍵值下。您的應用程序的代碼讀取該數字 (GET foo), 作一些事情 (ADD 1) 並將其寫回 (SET)。當您在單個線程中運行代碼時, 這就是它的外觀:同步

應用程序 Redis服務器端

App               Redis
 |---- GET foo ---->|
 |<------ 1 --------|
 |                  |
 | thinking...      |
 |                  |
 |--- SET foo 2 --->|
 |<----- OK --------|

如今讓咱們看看兩個應用程序客戶端嘗試這樣作時會發生什麼:軟件

App 1             Redis              App 2
 |---- GET foo ---->|                  |
 |<------ 1 --------|<--- GET foo -----|
 |                  |------- 1 ------->|
 | thinking...      |                  |
 |                  |       thinking...|
 |--- SET foo 2 --->|                  |
 |<----- OK --------|<--- SET foo 2 ---|
 |                  |------ OK ------->|

在這裏你能夠當即看到發生了什麼,由於沒有鎖定, 儘管服務器 (主要是) 單線程的-獲得的結果不是 3, foo 的值最終是2。當您添加更多的線程/客戶/應用程序時, 當多個做者嘗試修改數據而不進行協調 (即鎖定) 時, 事情會變得更加歡快而嚴重。

樂觀鎖定只是一種方法, Redis 經過監視機制提供內置。然而, 有時, 樂觀-儘管看上去容易且快樂-一般不是正確的解決方案, 因此你須要實施更好/先進/不一樣的機制, 以防止競態條件。能夠說, 這樣的鎖可能在 Redis 以外實現, 但若是您已經使用它, 那麼在其中管理您的鎖也是有意義的。

相關文章
相關標籤/搜索