2022/09/30 Update
最近重新翻出這篇文章,覺得當初撰寫時有些不夠明確與細節,特地再更新一下內容
寫這一篇是想到了當年對於時間的設計,還特地回頭翻了以前寫的文章 MongoDB 使用、設計時間欄位小心得。
又經過一年的磨練,對於之前的設計又有點想法想來聊聊。
起因
我們使用的語言是 .Net Core,設計的系統為跨國使用,因此時區問題是無法避免的。系統中一律使用 C# DateTimeOffset
,直接存進 MongoDB 會變成一個陣列(可以參考上面的連結),除了使用上稍微不方便、時區功能也是無用的儲存(因為系統端通常都是無時區,到了呈現端才會轉換),在查詢上也會稍微有差距。
接著嘗試了 C# DateTime
,進了 MongoDB 後就是 Date
格式,很好處理掉上面的問題,但另一個更致命的是,商業產品上對於毫秒的精確度很高,而 Date
最多只支援三位毫秒,完全不服使用情境。
最後選用了 C# 的 UtcTick
,這樣好處是儲存體都是 UTC 時間,對外溝通都一率使用 UTC,很符合我們的技術棧 gRPC (時間皆是用 timestamp)。很好的解決了當時的所有問題,反正客戶端在哪個時區,再進行轉換就好; 最大的不便莫過於可讀性了,但暫時沒有更好的解決方案。
1 | Timestamp 是 Unix 時間,`1970年1月1日00:00:00 UTC` 為時間原點 |
新需求新改變
使用一段時間後,發現我們有個小困境,有些使用者情境不需要如此精確的時間,例如可能只以小時或者天為單位,所幸儲存體不算貴,因此一種時間就有了三個欄位,分別都是 long 型別 (UTC Ticks):
1 | CreateTime |
這樣還是能解決我們大部分的商務需求。但某一天接到一個臨時性的支援需求,需要以某個時間欄位以天
為單位進行統計資料,這時我驚覺,這個我們做不到了!
以往使用時間型別,還能夠 TRUNCATE 格式在進行 GroupBy,純粹是 Tick 難度就變得非常高。未來若是遇上以週、雙週、月為單位呢?難道要存更多卻很少使用到的欄位?
反思設計
回頭看 MongoDB 提供哪些時間的儲存資料類別:
Data Type | PROS | CONS |
---|---|---|
Date | 精確度高 | 程式面需強制UTC |
Timestamp | 可無痛對外溝通 | 精確度不足 |
Long(Tick) | 1. 最精確 2.數值型別 | 1. 人類無法閱讀 2.無法在 MongoDB 做轉換 |
Array(DateTimeOffset) | 有時區概念 | 較慢又用不到 |
String(DateString) | 別鬧了 | 別鬧了 |
透過上表,先來看看選手們:
- Timestamp: 缺點就是精准度不夠啦!如果需求面沒有到毫秒,我應該是會考慮使用,且還是可以在 MongoDB 做轉換。
- Long(Tick): Tick 除了無法閱讀,在查問題時需搭配網頁或工具貼來貼去的轉換,致命性問題就是無法透過 MQL 調整精確度。
- Date: 提供高精確度,且可以在 MongoDB 進行轉換,無疑是最全面的資料型別了,至於效能部分,目前還不敢說與 Long 的差異,也許未來有機會再來測試。
- Array, String: 想都別想,真的。
結論
- 超高精確度需求=> Ticks
- 有毫秒需求=> 使用 Date
- 無毫秒需求=>
- 系統有使用 Timestamp => 用 Timestamp
- 系統無使用 Timestamp => 使用 Date
希望為後來設計新需求或是系統的人提供一點意見。