在大容量,高負荷的web系統中,對資料庫進行一系列拆分,可有效提升資料庫容量和性能。在初學程式的早期,程式員通常都喜歡按傳統資料庫設計模式,設計為單庫和單一功能表的結構,這樣的結構在數據量和併發量達到一定程度之後,會出現嚴重性能問題和維護問題。在出現問題的時候才著手進行優化,會非常痛苦,所以應該在系 ...
在大容量,高負荷的web系統中,對資料庫進行一系列拆分,可有效提升資料庫容量和性能。在初學程式的早期,程式員通常都喜歡按傳統資料庫設計模式,設計為單庫和單一功能表的結構,這樣的結構在數據量和併發量達到一定程度之後,會出現嚴重性能問題和維護問題。在出現問題的時候才著手進行優化,會非常痛苦,所以應該在系統架設之初就考慮好之後會出現的問題。
目前有些資料庫策略是採用單庫結構,然後通過同步分發到數台伺服器實現讀寫分離。個人覺得這樣的策略非常笨拙,還是想辦法將其分隔開來好,否則每台機器的記憶體都很容易超支。
一般只對數據量比較大的表進行拆分,這應該沒有什麼異議;還有一種是有可能會進行維護的比較重要的表,比如文章目錄表,如果有從其它系統倒數據進來的可能的話,也要拆掉,不然倒數據時一不小心把目錄表弄壞了,發現忘了備份,那真是欲哭無淚。
下麵來分析一下:
一、時間結構
如果業務系統對時效性較高,比如新聞發佈系統的文章表,可以把資料庫設計成時間結構,按時間分有幾種結構:
1) 平板式
表類似:
article_200901
article_200902
article_200903
用年來分還是用月可自定,但用日期的話表就太多了,也沒這必要。一般建議是按月分就可以。
這種分法,其難處在於,假設我要列20條數據,結果這三張表裡都有2條,那麼業務上很有可能要求讀三次表。如果時間長了,有幾十張表,而每張表是0條,那不就是要讀完整個系統的表才行麽?另外這個結構,要作分頁是比較難實現的。
主鍵:在這個系統中,主鍵是13位帶毫秒的時間戳,不要用自動編號,否則難以通過主鍵定位到表,也可以在查詢時帶上時間,但比較煩瑣。
2) 歸檔式
表類似:
article_old
article_new
為瞭解決平板式的缺點,可以採用時間歸檔式設計,可以看到這個系統只有兩張表。一張是舊文章表,一張是新文章表,新文章表放2個月的信息,每天定期把2個月中的最早一天的文章歸入舊表中。這樣一方面可以解決性能問題,因為一般新聞發佈系統讀取的都是新的內容,舊的內容讀取少;第二可以委婉地解決功能問題,比如平板式所說的問題,在歸檔式中最多也只需要讀2張表就完成了。
歸檔式的缺點在於舊表容量還是相對比較大,如果業務允許,可對舊表中的超舊內容進行再歸檔或直接清理掉。
二、版塊結構
如果按照文章的所屬版塊進行拆表,比如新聞、體育版塊拆表,一方面可以使每個表數據量分離,另一方面是各版塊之間相互影響可降到最低。假如新聞版塊的數據表損壞或需要維護,並不會影響到體育版塊的正常工作,從而降低了風險。版塊結構同時常用於bbs這樣的系統。
板塊結構也有幾種分法:
1) 對應式
對於版塊數量不多,而且較為固定的形式,就直接對應就好。比如新聞版塊,可以分出新聞的目錄表,新聞的文章表等。
news_category
news_article
sports_category
sports_article
可看到每一個版塊都對應著一組相同的表結構,好處就是一目瞭然。在功能上,因為版塊之間還是有一些隔閡,所以需要聯合查詢的需求不多,開發上比時間結構的方式要輕鬆。
主鍵:依舊要考慮的,在這個系統中,主鍵是版塊+時間戳,單純的時間戳或自動編號也能用,查詢時要記得帶上版塊用於定位表。
2) 冷熱式
對應式的缺點是,如果版塊數量很大而且不確定,那要分出的表數量就太多了。舉個例子:百度貼吧,如果按一個詞條一個表設計,那得有多少張表呢?
用這樣的方式吧。
tieba_汽車
tieba_飛機
tieba_火箭
tieba__unite
這個表汽車、火箭表是屬於熱門表,定義為新建的版塊放在unite表裡面,待到其超過一萬張主貼的時候才開對應表結構。因為在貼吧這種系統中,冷門版塊肯定比熱門版塊多得多,這些冷門版塊通常只有幾張帖子,為它們開表也太浪費了;同時熱門版塊數量和訪問量等,又比冷門版塊多得多,非常有特點。
unite表還可以擴展成哈希表,利用詞條的md5編碼,可以分成n張表,我算了一下,md5前一位可分16張表,兩位即是256張表。
tieba_unite_ab
tieba_unite_ac
...
三、哈希結構
哈希結構通常用於博客之類的基於用戶的場合,在博客這樣的系統里有幾個特點,1是用戶數量非常多,2是每個用戶發的文章數量都較少,3是用戶發文章不定期,4是每個用戶發得不多,但總量仍非常之大。基於這些特點,用以上所說的任何一種分表方式都不合適,一沒有固定的時效不宜用時間拆,二用戶很多,而且還偏偏都是冷門,所以也不宜用版塊(用戶)拆。
哈希結構在上面有所提及,既然按每個用戶不好直接拆,那就把一群用戶歸進一個表好了。
blog_aa
blog_ab
blog_ac
...
如上所說,md5取前兩位哈希可以達到1296張表,如果覺得不夠,那就再加一位,總數可達46656張表,還不夠?
表的數量太多,要創建這些表也是挺麻煩的,可以考慮在程式里往資料庫insert之前,多執行一句判斷表存在與否並創建表的語句,很實用,消耗也並不很大。
主鍵:依舊要考慮的,在這個系統中,主鍵是用戶ID+時間戳,單純的時間戳或自動編號也能用,但查詢時要記得帶上用戶名用於定位表。
四、總分結構
以上的這些結構,根據每個業務系統,能想出的估計還有很多。不過現在互聯網業務越來越複雜了,有些時候,單一的拆分法還不能實現需求,需要幾種拆分方案一起實施,多管齊下,這時候其中的邏輯會讓人繞暈。我就開發過一個系統,僅僅是將哈希結構和時間結構混著一用,覺得邏輯就相當複雜。
所以,除了拆表之外,按最原始的單庫單表,再建一個總表,是非常有利的架構。在這個架構中,每次往資料庫會寫入兩倍數據,讀取主要依賴拆表提升性能,總表用於實現拆表後難以實現的功能並且用於每天的定時備份;另外總表和分表還相互是一個完整的備份,任何一個分表損壞或數據不正常,都可以從總表中讀到正確的數據並恢復,反之亦然。
在總分結構中,讓人感到質疑的是總表的性能和可維護性。我的方案是總表可採用相對能保證穩定的一些服務軟體和架構,例如oracle,或 lvs+pgpool+PostgreSQL,重點保證數據穩定;相對的,分表就用輕量級的mysql,重點在於速度。能夠對總分表各採用不同的軟體和方案,也是總分結構的一大特點。
總結:如何通過拆表來優化系統,最基本的是要按業務需求和特點分析。本文僅僅是提供了幾種基本方法,具體工作要先動腦好好想,千萬不可亂套,用錯了工作量要加十倍噢。