對計算機領域中間層的理解
以前看《代碼大全2》時看到了如下一句經典臺詞:
“any problem in computer science can be sloved by another layer of indirecition”
“計算機科學領域的任何問題都可以通過增加一個中間層來解決”
當時只是覺得好就永遠記住了,幾年的編碼經驗,越來越感受到這句話的經典所在,下面來舉幾個例子說一下我對它的理解:
1.操作系統的緩存:
比較典型的用於闡述這句話的緩存實例,當屬內存(包括swap、buffer、內存的cache)和cpu的Cache(有三個等級),
操作系統中數據總體讀取流向:硬盤》內存》cpu。
swap:
當有應用需要讀寫磁盤數據時,由系統把相關數據從磁盤讀取到內存,如果物理內存不夠,則把內存中的部分數據導入到磁盤,從而把磁盤的部分空間當作虛擬內存(也就是swap)來使用。換句簡單的話說,swap就是當內存不夠時從硬盤中開辟的一塊空間。
buffer:
當一個應用程序在內存中修改過數據後,因為寫入磁盤速度相對較低,在有空閑內存的情況下,這些數據先存入內存的一塊空閑空間(即buffer),在以後某個時間再寫入磁盤,從而應用程序可以繼續後面的操作,而不必等待這些數據寫入磁盤的操作完成。簡單說,buffer 是為了提高內存和硬盤(或其他I/O設備)之間的數據交換的速度而從內存中開辟的一塊空間。
內存的cache: 從磁盤讀取到內存的數據在被相關應用程序讀取後,如果有剩余內存,則這部分數據會存入內存的一塊空閑空間(內存的cache),以備第2次讀取時,避免重新讀取磁盤。
cpu的Cache:
Cache:高速緩存,是位於CPU與主內存間的一種容量較小但速度很高的存儲器。由於CPU的速度遠高於主內存,CPU直接從內存中存取數據要等待一定時間周期,Cache中保存著CPU剛用過或循環使用的一部分數據,當CPU再次使用該部分數據時可從Cache中直接調用,這樣就減少了CPU的等待時間,提高了系統的效率。
又上可見,操作系統的緩存就是硬盤與內存之間,內存與硬盤之間的各種中間層,用於解決操作系統內部數據交互速度不一致的問題。
2.消息對列:
當系統中出現“生產“和“消費“的速度或穩定性等因素不一致的時候,就需要消息隊列,作為抽象層,彌合雙方的差異。“ 消息 ”是在兩臺計算機間傳送的數據單位。消息可以非常簡單,例如只包含文本字符串;也可以更復雜,可能包含嵌入對象。消息被發送到隊列中,“ 消息隊列 ”是在消息的傳輸過程中保存消息的容器 。
常用的業務場景如下:
1)業務系統觸發短信發送申請,但短信發送模塊速度跟不上,需要將來不及處理的消息暫存一下,緩沖壓力。就可以把短信發送申請丟到消息隊列,直接返回用戶成功,短信發送模塊再可以慢慢去消息隊列中取消息進行處理。
2)調遠程系統下訂單成本較高,且因為網絡等因素,不穩定,攢一批一起發送。
3)任務處理類的系統,先把用戶發起的任務請求接收過來存到消息隊列中,然後後端開啟多個應用程序從隊列中取任務進行處理。
4)在高並發環境下,由於來不及同步處理,請求往往會發生堵塞,比如說,大量的insert,update之類的請求同時到達MySQL,直接導致無數的行鎖表鎖,甚至最後請求會堆積過多,從而觸發too many connections錯誤。通過使用消息隊列,我們可以異步處理請求,從而緩解系統的壓力。
消息對列就相當於兩個應用之間的中間層,用於解決生產和消費的速度不一致的問題,能夠提高系統的響應速度和系統的穩定性。
3.中間表:
具體應用比如閉包表、從屬表等。
閉包表:用於分級存儲的一個簡單而優雅的解決方案,它記錄了樹種所有節點間的關系,而不僅僅只有那些直接的父子關系。
比如一個評論表,一般建表如下:
CREATE TABLE Comments (
comment_id bigint unsigned not null auto_increment primary key,
parent_id bigint unsigned not null,
comment text not null,
foreign key (parent_id) references Comments(comment_id)
)
這樣建設雖然簡單,但是當一個評論下面有很多分支的時候,無論你是用程序還是用sql語句去處理這種層級關系 ,都是比較低效的。
一種很好的解決方案技術增加一個中間表,用於存儲各個評論之間的關系(包括自己與自己,自己與子孫之間的關系),將剛才的表拆分為兩張表如下 :
CREATE TABLE Comments (
comment_id bigint unsigned not null auto_increment primary key,
comment text not null
);
CREATE TABLE TreePaths(
ancestor_id bigint unsigned not null,
descendant_id bigint unsigned not nul,
primary key(ancestor_id , descendant_id),
foreign key (ancestor_id) references Comments(comment_id),
foreign key (descendant_id ) references Comments(comment_id)
);
這樣增加一個中間表後既使關系很清晰,在查詢、做統計分析和增刪改的時候也簡單多了。
從屬表:僅用一列來存儲多值屬性,將多個值存在一張單獨表的多行中而不是一張表的多列中。
比如創建一個有多種屬性值的表,一般建表如下
CREATE TABLE Bug(
bug_id int unsigned not null auto_increment primary key,
description varchar(1000),
tag varchar(500) //用json格式存儲
)
或者
CREATE TABLE Bug(
bug_id int unsigned not null auto_increment primary key,
description varchar(1000),
tag1 varchar(20),
tag2 varchar(20),
tag3 varchar(20),
tag4 varchar(20),
)
這樣建設雖然可以,但不優雅,在做統計查詢或者增加一種屬性時就比較費力了,這是可以增加一張中間表(從屬表)單獨用來存儲bug的屬性,具體建設如下
CREATE TABLE Bug(
bug_id int unsigned not null auto_increment primary key,
description varchar(1000)
)
CREATE TABLE Tag(
bug_id int unsigned not null,
tag varchar(20) not null,
primary key (bug_id,tag),
foreign key (bug_id) references Bug(bug_id)
)
這樣修改後的表在做查詢和統計的時候比較高效,在增加和刪除的時候級聯關系也很嚴格。
主鍵的約束能夠保證不會有重復的記錄出現,一個給定的標簽只能和一個給定的Bug關聯一次。
每個Bug也不在只限於幾個標簽,可以很方便的擴展成tagN 的列。
表建設過程中增加一個中間表非常適用於“有關系”的字段,既方便擴展也方便做普通查詢和統計查詢,嚴格的關系也可以通過外鍵加以控制,只是在查詢的時候多了幾個join(通過索引並不影響查詢效率),這麽好的想法,那就趕快在新項目中嘗試吧。
以上只是通過操作系統的緩存、消息隊列、中間表這三個實例闡述了我對“any problem in computer science can be sloved by another layer of indirecition”這句話的理解,這句話不僅適用於計算機領域,其他工程領域比如工程建造、機械建設、系統流程也是適用,所以經典之所以經典,是因為經典的東西不僅能幫你解決手中現有問題還能幫你解決以後可能出現的問題,還很富有哲學,能應用到各個領域。
寫在最後的話:
“書中自有黃金屋 書中自有顏如玉”,也許你現在讀的不一定現在就懂,等你經歷的多了或者讀得多了再回頭看看,也許你會微微一笑,雖不至於傾城傾國,傾自己倒是絕對可以的。騷年們,當你走的路不多時,那就多讀書吧。
參考博客:
http://blog.csdn.net/bingqingsuimeng/article/details/51858858
http://www.cnblogs.com/xuyatao/p/6864109.html
http://www.ywnds.com/?p=5791
參考書籍:
《代碼大全2》、《SQL反模式》
對計算機領域中間層的理解