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

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

問題起源

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

對於傳統的數據庫設計,當用戶修改個性簽名,會執行類似如下的 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,命令查詢職責分離) 結合使用,這對開發人員而言,學習成本更高。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章