審計系統的一劑良方——事件溯源

精緻碼農發表於2020-11-30

大多數系統在資料庫存的都是系統的狀態資料,比如一個使用者表可能會存使用者的姓名、頭像、個性簽名等資訊。只存狀態資料的傳統模式會有什麼問題呢?

問題起源

假設你公司做了一個系統,並正式上線了。經過一週的推廣,老闆問你要一些使用者的行為資料。老闆想知道所有使用者平均修改個性簽名的次數。

對於傳統的資料庫設計,當使用者修改個性簽名,會執行類似如下的 SQL 語句:

UPDATE Users SET Sign='Talk is cheap, show me the code.' WHERE Id=123

問題是目前資料庫沒有記錄使用者修改密碼次數的欄位。於是,為了更快的實現老闆的需求,你需要給資料庫的使用者表增加這樣一個欄位。使用者每次修改個性簽名的時候,這個欄位值加一。

這樣雖然很好的解決了問題,但如果這種需求越來越多,比如老闆又問你要所有使用者平均修改密碼的次數,就會有一些明顯的問題:

  • 應對這種需求需要修改程式碼,重新測試和釋出。
  • 需要修改資料庫,會使得資料庫設計越來越複雜。
  • 增加的欄位依然是狀態資料,無法反映某時間段使用者的行為。比如無法獲取某使用者上次修改密碼的時間、每年修改了多少次密碼等資訊。

上面使用者的個性簽名、密碼修改次數的例子算是簡單的需求,增加欄位、修改程式碼還可以應付。但需求稍微變換一下,複雜一點,比如要分析某商品在使用者購物車中變化的情況,如什麼時間點新增這個商品到購物車的使用者最多、當使用者從購物車移除該商品的同時購物車中有哪些競品等。這種需求帶來的修改,會使資料庫設計和系統變得無比複雜,產生的工作量也是巨大的。

可見,隨著系統不斷擴大,業務需求越來越多樣化,這種資料庫儲存狀態資料的傳統模式就會越發捉襟見肘了。

面對這種“痛”,事件溯源可能是一劑良方。

一劑良方

事件溯源(ES,Event Sourcing),字面上理解就是使任何對資料的修改都可追溯。

事件溯源是一種設計模式。相對於傳統的在資料庫中儲存系統的狀態,事件溯源在資料庫中儲存的是系統發生的事件。

舉個例子,當使用者在系統中註冊後,一個UserCreated的事件就被儲存了。然後,當使用者修改了密碼,一個UserChangedPassword的事件就被儲存了。對於這個使用者而言,通過事件溯源設計模式,系統可以知道該使用者的一舉一動。比如按照時間線,某個使用者的事件可能是這樣的:

這樣對於前文使用者平均修改個性簽名次數的需求,就可以輕鬆應對了。只需要查詢事件為UserChangedPassword的數量再除以使用者總數即可。

事件溯源在現實世界中更接近人們的思維習慣,比如當有人問你今天過得怎麼樣時,你不會告訴他們今天你的體重是多少、吃了幾頓飯(狀態資料),而會告訴他們發生了什麼有趣的事情(事件)。所以對系統來說,也更容易建模。

適用範圍

事件溯源適用於資料分析,可以生成各種維度的報表,幫助你更深入地瞭解資料;可以提供審計日誌,幫助你準確地知道系統是如何從一個狀態變成另一個狀態的。比如你在銀行存了一千萬,隔了一年後發現賬戶餘額只有一百塊。事件溯源就可以告訴你,你是如何一步一步從一個千萬富翁變成窮光蛋的。

聽上去不錯,但事件溯源也不是包治百病的萬能藥。

事件溯源會給系統增加額外的複雜性。往往用傳統方式可以簡單快速完成的增、刪、改功能,使用事件溯源就會多繞幾步。它通常需要與 CQRS (Command Query Responsibility Segregation,命令查詢職責分離) 結合使用,這對開發人員而言,學習成本更高。

如果是不需要審計日誌的小規模系統,使用事件溯源就會得不償失。如果團隊中沒有在這方面有足夠的經驗的開發者,也不要輕易在生產環境中使用它。沒有精鋼鑽,不攬瓷器活。

相關文章