為什麼在 Redis 實現 Lua 指令碼事務?

oschina發表於2015-08-04

在剛過去的幾個月中,我一直在構思並嘗試在 redis 中實現 lua 指令碼的事務功能。沒有多少人理解我的想法,所以我將通過一些歷史為大家做下解釋。

MySQL 與 Postgres

在 1998-2003 年間,如果你想執行一個正規的資料庫驅動的網站/服務,但又沒有足夠的資金購買微軟或 Oracle 的資料庫,你可以選擇 MySQL 或 Postgres 。很多人都選擇了 MySQL,因為它速度較快——主要是因為 MyISAM 儲存引擎沒有提供事務功能以此來換取效能,但速度確實很快。另一些人轉向 Postgres,因為雖然在相同硬體上其效能明顯低於 MySQL,但 Postgres 不會丟失資料(說實話,MySQL 資料丟失的情況非常少見,但丟了可不是鬧著玩的)。

就這樣湊合著過了很久;MySQL 將其預設的儲存引擎從 MyISAM 過渡到了 InnoDB (其實很早就有了),這樣它的儲存引擎也得到了完整的事務支援和其他功能。與此同時,Postgres 也變快了,並新增了一個持續擴充套件的功能列表來使自己與眾不同。現在對於 MySQL 與 Postgres 的選擇只看個人的體驗與偏好,除了有時業務需要或領導決定使用其他選擇。

資料完整性

從很多方面來看,Redis 很像當初採用 InnoDB 前的 MySQL。而 Redis 採用了一種很合理的方式來保證資料完整性(複製,AOF 等),並且從 Redis2.6 開始引入的 Lua 指令碼在功能與易用性方面為 Redis 的成長提供了很大助力。

相對來說,Lua 指令碼與其他資料庫中的儲存過程很相似,但指令碼的執行有些許不同。在本文中最重要的一點就是一旦將指令碼寫入資料庫,它會一直執行直到以下任一種情況出現:

1. 完成所有工作,所有寫操作處理完成後指令碼會自動退出。

2. 指令碼執行時出錯並中途退出,所有以前執行的寫操作都已發生,但不會再有其他寫操作。

3. Redis 通過 SHUTDOWN NOSAVE 關閉時(不儲存)。

4. 你附加了偵錯程式來“使”指令碼完成 #1 與 #2 (或其他手段來保證不會丟失資料)。

對於使用資料庫開發軟體的人,我想你也認同只有情景 #1 是最理想的。情景 #2,#3,#4 都會導致資料異常(#2 與 #4)和/或資料丟失(#3 和 #4)。如果你很重視資料,你應該儘可能地阻止資料異常與丟失。這不是哲學,而是工作(This is not philosophy, this is doing your job)。但很遺憾目前的 Redis 也幫不了你多少。所以我決定改變這種情況。

實現 Lua 指令碼事務

我嘗試解決上面列表中的 #2,#3,#4 問題,最終像下面這樣:

  1. 指令碼完成所有的工作,處理完寫操作後正常退出
  2. 指令碼執行過程中遇到錯誤退出,不更改任何資料(所有寫操作都回滾)

無論有沒有寫入資料,都不會有資料丟失。這應該是所有的資料庫都希望做到的,我打算把這個加到 Redis 中,因為我們都希望 Redis有 這個功能。

目前的 pull request 只是一個概念性的證明。也就是說,為了避免資料丟失,你要麼 a) 顯式使用事務的變體執行指令碼,要麼 b) 強制所有 Lua 指令碼呼叫帶配置選項的事務語義。

還有很多的辦法使現在這個 patch 變得更好,我希望能得到 Salvatore (Redisw 作者)和其他社群的幫助。

相關文章