簡介
資料建模的某些方面會變得主觀。事實上,可以說資料建模的大多數方面都是主觀的,儘管它有非常數學的基礎。任何關於如何表示資訊的問題都有許多答案,而且並不總是清楚哪一個最適用於特定上下文。在深入研究實現細節之前,你應該儘可能地理解該上下文和需求。這需要時間,也需要他人的時間,以及大量的問題。研究和訪談對於資料庫設計來說,與使用者體驗設計一樣重要:不完整、過於詳細、侵入性或結構不良的資料至少會給使用者和主題帶來痛苦,也會給系統的維護者帶來痛苦!即使你被告知執行需求,你也很可能是第一個全面思考問題及其可能後果的人。
資料建模問題的癥結在於定義有用的實體,並識別它們在連線網路或圖中是否以及如何相互關聯。這些實體代表物理物件或無形概念的類別——以及介於兩者之間的許多事物,例如位置、貨運或分配。但是,佔據這個符號空間的表示不受物理定律的約束,並且指示物和被指示物之間不一定存在嚴格的一對一對應關係。單個實體可能代表你正在處理的許多概念共享的一個方面,甚至是一個完整的子系統;或者,看似單一的概念,分解成多個後可能更有用或更易於管理。
任何實體本身所能表達的意義都有限。祖魯諺語 “人透過其他人成為人” 對於抽象表示也並非不正確。資料模型很少代表孤立事實的單一類別;它們更常代表系統,即使是少陣列件的關係和互動,對於系統的準確和有用表示來說,也與元件本身一樣重要。
主觀性使得抽象地回答這些問題成為不可能。這是一個需要深入思考和透過積極詢問與你收集的資料有利害關係的人來進行調查的練習。即便如此,你最初得出的答案也充其量只是暫時的;極少有資料模型在釋出前能保持不變。
什麼足夠重要可以被視為實體?
“我們真正關心哪些類別?”這是一個顯而易見的問題,以至於即使你即將將假設固化下來,也可能忘記提出。無論你使用哪種資料庫,定義系統的**主體**和**客體**都是首先要考慮的事情。這也與資料儲存的選擇有關:例如,如果你關心大量的儀器讀數或其他簡單事實(你讀取的頻率遠高於更新的頻率),這應該引導你選擇像 Cassandra 或 HBase 這樣的列式資料庫。
關係型資料庫很複雜(complicated)。在談論系統時,複雜(complicated)必須與複雜(complex)區分開來。複雜(complex)系統包括神經網路、經濟、語言、生命本身:每個元件都基於有限的上下文資訊與許多其他元件互動,整個系統與環境互動,歷史和行為隨之產生。
同時,複雜(complicated)系統只是由許多獨立的部分組成,每個部分都像時鐘或引擎一樣有節奏地行動並被作用。在資料庫中編碼歷史並定義行為是可能的,但這嚴格來說是一個自上而下的練習;資料模型中任何看似**湧現**的特性,都不過是一個確定性的 bug。資料模型的元素會相互作用,但總是以相同的方式與相同的其他元素作用。資料模型中的某些元素對於資料庫表示來說,就像紗門對於潛艇一樣,充其量只會增加一個機械故障點。
實體之間的哪些連線很重要?
設計資料模型時,如果多個使用者群及其資料儲存在相同的表中,有兩種方式。我們稍後會回到多租戶的更廣泛問題,但在共享模式模型中有一個特定的選擇,它體現了建模中的一些主要考量:每個記錄都應該包含一個 tenant_id 值,還是隻包含那些與租戶有直接、有意義連線的記錄?
從概念上講,只要強制執行外部索引鍵約束,識別某一行屬於哪個租戶並不困難。人們可以沿著關係從一個表 JOIN 到另一個表,回到 tenants 表,然後任何包含你正在尋找的 tenant_id 值的連線產品都屬於該租戶。然而,隨著模式的進一步演變,管理 SQL 可能會變得相當糟糕,並且隨著系統中資料量的增加,效能可能會成為一個問題。這並不意味著在更遠的表中管理額外的 tenant_id 列就自動值得,但要做出這個合理的決定也不需要太多。
實體和關係構成了一個有向的節點和邊圖。這裡的“有向”意味著邊是單向連線。一個帶有 tenant_id 值的記錄,既暗示了 tenants 表的存在,也暗示了其中特定記錄的存在(如果設定了正確的外部索引鍵約束,則是保證而非暗示!);然而,一個來自 tenants 本身的記錄,卻不暗示任何其他資料可能屬於它。
每一個額外的節點或邊都會使圖變得更加複雜,這反過來意味著進一步維護和改進資料模型將需要更多的努力。然而,這種複雜性既是一個區域性問題,也是一個全域性問題。實體集可以以足夠重要的關係交織在一起,這對於管理和使用都具有挑戰性。這些子系統在一定程度上可以透過 PostgreSQL 中的檢視或表繼承等機制來遮蔽,其最終效果與面向物件軟體開發中的外觀模式類似。
組織單元如何互動?
模型的任何元素——無論是實體、關係,還是包含多個實體和關係的子圖——都可能代表你正在設計的組織的一部分,或是這些單元之間互動的焦點。庫存連線著製造與物流,或物流與銷售。費用記錄連線著財務和人力資源,以及人力資源與公司各部門。有些參與者可能主要透過少數邊緣實體與模型互動;如果他們對系統深層運作了解甚少,這可能會帶來挑戰,但也可能透過讓他們更多地瞭解和接觸其餘部分來改善組織的整體運作。
很少有觀察結果像康威定律那樣經久不衰:組織註定要在其系統設計中複製其溝通結構,資料庫也不例外。某些資料建模問題,無論是全部還是部分,實際上是溝通問題,而資料庫對此可能幫助甚微。
多少細節?
把前兩個問題反過來問也很有用:如果我們完全忽略這個可能的實體或那個關係,資料模型的使用者會錯過什麼?當然,你最初認為是重要的元素,大多數確實如此。但有些元素可能不像初看起來那麼關鍵,省略或簡化它們可以提高模型的整體功能和可維護性。
重要性問題有不止“是”和“否”的答案。你可能不需要將問題空間的某些方面作為獨立的實體來表示,但仍然關心像存在或數量這樣通用且更易於管理的事實。在製造業中,零件號是關於校準、工藝歷史、子元件、容量、公差等大量資訊的關鍵,不同的特性對不同型別的零件很重要。在倉儲中,零件號變成了 SKU(“庫存單位”),而關鍵細節要簡單得多:有多少、什麼顏色、多重、在哪裡?
某些資訊也很有用,但它們本身已經有內部結構,將其轉換為實體和關係並不方便。層級結構、超文字文件、物料清單,甚至資料庫中其他地方的實體關係子圖的臨時“工作副本”;這些或它們包含的資訊可以用關係型方式表示,但除非存在跨越外部和內部結構邊界的重要關係,否則將其拆開可能不值得。如果此類資訊是你的主要關注點,那麼專門的資料庫可能更適合,例如(用於層級文件的)CouchDB 或 MongoDB。如果它們是其他關係模型的例外情況,那麼 JSON 和 XML 等資料型別可以幫助避免將模型拆分到兩個或更多資料庫中。因為每個額外的資料儲存不僅增加了維護和協調的工作量,而且參照完整性——即關係所依賴的資訊不會憑空消失的保證——只在單個數據庫內部成立。
哪些子圖、聚合和統計資料有用?
查詢關係型資料庫需要時間。具體需要多少時間取決於多種因素:主機電腦的物理特性,如磁碟速度和 CPU 功率;所涉關係型資料庫管理系統 (RDBMS) 的操作能力,如快取和最佳化;最後是查詢本身的構成。你的資料模型必須適應前兩類因素所設定的限制,但其成功的主要決定因素之一將是它們如何很好地預見第三類問題。
跨越的關係數量是查詢組成中最重要的方面之一,無論是對於效能還是可維護性。高 JOIN 計數不一定意味著不可接受的效能,特別是如果被連線的實體很小或已良好索引,但即使在最好的情況下,每一次連線都會增加查詢編寫者的認知負擔。對於你、後端開發人員和其他資料庫使用者來說,組裝常用結果集應該儘可能簡單明瞭。
資料庫提供了一些工具來幫助實現這一點。檢視封裝了常用的子圖及其上的計算,這有助於向用戶和應用程式遮蔽複雜性。將你的二階表示集中在資料庫中,還可以確保有一個單一的規範表示,例如包含所有相關資訊的採購訂單,或倉庫的常見統計資料。普通檢視對效能沒有幫助,因為它們是儲存的查詢,會被整合到正在執行的語句中。然而,大多數關係型資料庫都支援“物化”檢視(MySQL 和 MariaDB 是顯著的例外)。這些檢視將資料持久化到磁碟,像表一樣,使得檢索速度快得多,但這些資料會變得過時,需要重新整理。
這裡最後的手段是**反正規化化**:將實體的資料或有用的聚合(如計數或總計)作為不同實體的屬性直接儲存。我們將在未來的章節中更深入地探討正規化,但毋庸置疑,這不是一個可以輕易採取的步驟。儘管如此,如果收集所需資料非常耗時,那麼放棄更高層次的正規化化可能值得付出額外的管理努力。如果你需要在這裡走向極端,是時候再次考慮列式資料庫了。
不應該儲存什麼?
你不必為沒有的資訊負責。整個行業都圍繞資料安全展開,但沒有萬無一失的安全措施。避免一類非常棘手的問題最簡單的方法就是不收集可能給你帶來此類問題的資料。請注意,這不僅限於資料庫;敏感資訊也可能出現在日誌、報告、原始碼管理和其他系統中。
毫無疑問,最具影響力的單一敏感資訊類別與**人**有關。關於人的資訊必然具有政治性,而對有人類參與的系統進行建模,既反映也強化了建模者的政治立場。誰進入系統或被輸入系統,以何種身份,哪些特徵被認為重要或不重要,對誰可見:這一切都是政治。我們才剛剛開始看到隨意或侵入性處理個人資料所帶來的影響。越來越多的人的資訊不斷地被灌輸到資料模型中,而這些模型往往沒有得到相應的精心開發。在不少情況下,它們甚至被明確設計出來以利用這些資訊。
個人身份資訊(PII)表明其他資料與誰相關。沒有 PII,一個隨機的醫療記錄就是真空中的一段歷史。某個地方的某個人,只知道一個數字,接受過這些兒童疫苗接種,在這裡出現肺炎,在那裡出現輕微皮膚癌,曾經有藥物依賴,得過幾次流感,還有躁鬱症。將該醫療記錄號與姓名和地址關聯起來,突然你——或任何能看到它的人——就對某個特定人的生活了解得更多,超出了他們可能希望或允許他人瞭解的範圍。對於資料所有者來說,收集超出我們成功保護能力的資料所帶來的後果,從短暫的不愉快對話到失業、身份盜竊、勒索和各種仇恨犯罪。
關於醫療記錄,許多國家都有嚴格的訪問和披露法律,這是有充分理由的。在大多數其他情況下,資訊並沒有受到如此嚴格的法律控制(如果有的話),這意味著你有責任弄清楚你需要如何嚴密地保護那些姓名和地址、出生日期、生物識別資訊、母親的孃家姓,以及護照或納稅人識別號等政府分配的身份值。PII 也可能在你意想不到的地方出現,就像 2004 年 AOL 釋出他們自認為匿名的搜尋日誌給公眾時所發生的那樣。更糟糕的是,即使是非識別性事實,在數量足夠多時也會變得具有識別性:有多少其他人與你擁有相同的居住城市、性別、職稱、手機型號和最喜歡的餐廳?這些事實組合還可能與哪些其他事物相關聯?
另一種敏感資訊允許你代表他人行事。社交媒體或其他網路憑據、銀行路由和賬戶資訊、信用卡號都基於信任交予你。在某些情況下,特別是涉及支付時,存在第三方服務可以為你分擔這種信任負擔。除非你有非常充分的理由親自承擔,否則請使用這些服務。
接下來會發生什麼?
資料模型並非一成不變。需求會演變;業務會轉型;外部資訊源會出現、變化和消失。這不會像應用程式開發那樣充滿活力,但確實會發生,包括大改動、小調整以及介於兩者之間的一切。仔細的設計將有助於保持向更慢、更小變化的方向平衡,但永遠無法保證在某個時候不需要對資料模型進行重大“手術”,甚至一些看似微小的變化也可能在部署中帶來意想不到的困難。例如,在 PostgreSQL 11 之前,新增一個帶有非空預設值的新列需要重寫表中的每個記錄——對於數百萬行來說這可不是什麼趣事!
現在比未來更重要。如果你的模型現在沒用,你很可能就沒有機會看到它以後變得有用。然而,自然或非自然選擇的力量總是在起作用,無法適應這些壓力的資料庫將會消亡。我們稍後將更詳細地討論未來規劃的可能性,但即使知道存在可能讓你陷入困境的死衚衕,也能幫助你避免這種命運。


