天天看點

為什麼在 Redis 實作 Lua 腳本事務?

在剛過去的幾個月中,我一直在構思并嘗試在 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 腳本與其他資料庫中的存儲過程很相似,但腳本的執行有些許不同。在本文中最重要的一點就是一旦将腳本寫入資料庫,它會一直執行直到以下任一種情況出現:

完成所有工作,所有寫操作處理完成後腳本會自動退出。

腳本運作時出錯并中途退出,所有以前執行的寫操作都已發生,但不會再有其他寫操作。

redis 通過 shutdown nosave 關閉時(不儲存)。

你附加了調試器來“使”腳本完成 #1 與 #2 (或其他手段來保證不會丢失資料)。

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

實作 lua 腳本事務

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

腳本完成所有的工作,處理完寫操作後正常退出

腳本執行過程中遇到錯誤退出,不更改任何資料(所有寫操作都復原)

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

目前的 pull request 隻是一個概念性的證明。也就是說,為了避免資料丢失,你要麼 a) 顯式使用事務的變體運作腳本,要麼 b) 強制所有 lua 腳本調用帶配置選項的事務語義。