第14期:計算封閉性導致臃腫的數據庫
許多大型用戶的數據庫(倉庫)在運行多年之后,都會積累出很多的數據表,嚴重者數以萬計。這些數據表年代久遠,有些已經忘記建設原因,甚至可能已不再有用,但因為很難確認而不敢刪除。這給運維工作帶來巨大的負擔。伴隨著這些表還有大量的存儲過程仍在不斷地向這些表更新數據,占用大量計算資源,經常要迫使數據庫擴容。
這些表是真地業務需要嗎需要嗎?業務會復雜到需要成千上萬的表才能描述嗎?
有過開發經驗的人都知道這不大可能,幾百個表就能描述相當復雜的業務了。這些眾多的表絕大多數都是所謂的中間表,并不是用來存儲基礎數據的。
一
那么,為什么會有中間表?中間表是用來做什么的?
一般來說,中間表會有內部和外部兩種來源。
內部產生的中間表大多是為數據呈現(報表或查詢)服務的。原始數據量很大時,直接基于原始數據計算匯總信息時的性能會很差,用戶體驗惡劣。這時,我們會先把一些匯總結果事先計算出,再基于這些中間結果產生報表,用戶體驗就會好很多。而這些中間數據就會以中間表的形式存儲。有時候是因為計算過程很復雜,在生成報表時臨時計算會使報表開發過于繁瑣,也會采用中間表事先計算好。這類中間表都會伴隨著存儲過程去定時更新數據,不僅占用存儲空間,還會消耗計算資源。而且,報表是業務穩定性比較差的業務,會經常修改和增加,隨之而生的中間表也會越來越多。
那么,為什么要把中間數據以數據庫表的形式存儲呢?這主要是為了獲得進一步的計算能力。數據呈現時,并不能簡單地把計算好的中間數據直接取出來呈現,而仍然需要做一輪簡單些的計算,比如根據參數進行過濾,有時還有再匯總的需求。而這些計算是數據庫比較適合實現的,如果把中間數據保存成文件,則將失去計算能力,所以程序員會習慣于使用中間表。
二
來源于外部的中間表又有兩種情況,一種是在ETL過程中產生的。ETL過程中常常會涉及到數據庫的數據,正常的ETL過程應當是E、T、L這三個步驟逐步進行,也就是先清洗轉換之后再加載進數據庫,***在數據庫中的只是合理的結果數據。但是,E(清洗)和T(轉換)這兩個步驟中會涉及到大量數據計算,而在數據庫外實施這些計算很不方便,所以實際情況就會是把涉及到的所有數據都先加載進來然后再進行清洗和轉換,ETL過程變成了ELT甚至LET。事先要加載的這些數據在關系數據庫中也必須以表的形式存儲,這就使數據庫中增加了許多并非最終需要的中間表。
另一種情況是多樣性數據源造成的,這也是為數據呈現(報表查詢)服務的。現代應用中的數據呈現經常會涉及數據庫外的數據,目前一般的做法是把庫外數據定時導入到數據庫中,然后就能和數據庫內的數據一起運算產生報表,否則很難實現數據庫內外的數據的混合運算。這當然也會讓數據庫中多了一些表,而且,有些互聯網上取過來的數據常常是多層的json或XML格式,在關系數據庫中還要建立多個關聯的表來存儲,會進一步加劇中間表過多的問題。
三
我們發現這幾種情況的中間表都有一個共同點:就是要利用數據庫的計算能力。數據庫外缺乏強有力的計算能力,而數據庫的計算能力又是封閉的(它不能計算數據庫外的數據),這樣,為了獲得數據庫的計算能力,我們就只能把許多數據先裝入數據庫,也就形成了中間表。
數據庫的存儲封閉性是有意義的,這樣可以確保庫內數據滿足一條規則的約束性,保證數據的正確合理性。但計算能力的封閉性卻沒有什么必要,對于計算而言,本來也沒有庫內庫外之分。但是數據庫的計算模型是建立在其存儲模型之上的,這就迫使其計算能力和存儲能力一起封閉了,為了獲得計算能力只能把數據庫搞臃腫。這不僅給管理造成麻煩,而且由于數據庫的存儲及計算資源都相對昂貴,僅僅是為了獲得計算能力就去擴容或部署新數據庫,在經濟上也不劃算。
四
計算封閉性導致臃腫的數據庫,而導致運維困難的還有數據庫的另兩個技術機制。
數據庫是一個共享的獨立進程,其計算能力在應用外部,而不從屬于某個應用。各個應用共享數據庫,都能訪問數據庫的資源。某個應用(模塊)中生成的中間表或存儲過程可能被另一個應用(模塊)調用,這就造成了應用(模塊)之間的耦合性,即使某個中間表的制造者已經下線不用,但因為可能被別的應用使用了而不能刪除。
數據庫的表還是一種線性組織。在條目數量不多時尚可,太多(幾千上萬時)就很難理解,人們一般會采用樹狀多層結構來組織管理眾多的條目。但關系數據庫并不支持這種方案(有個模式概念可理解為只能分兩層),這時候就要給表較長的命名來區別其分類,這一方面使用不便,另一方面對開發管理水平要求很高,在工作較急迫時常常顧不上規范,而隨便起個名字先把任務完成再說,時間長了,就會遺留大量的混亂中間表。
當然,根本問題還是在于計算封閉性。