一、緣起
當資料庫的資料量非常大時,水準切分和垂直拆分是兩種常見的降低資料庫大小,提升性能的方法。假設有使用者表:
user(
uid bigint,
name varchar(16),
pass varchar(16),
age int,
sex tinyint,
flag tinyint,
sign varchar(64),
intro varchar(256)
…);
水準切分是指,以某個字段為依據(例如uid),按照一定規則(例如取模),将一個庫(表)上的資料拆分到多個庫(表)上,以降低單庫(表)大小,達到提升性能的目的的方法,水準切分後,各個庫(表)的特點是:
(1)每個庫(表)的結構都一樣
(2)每個庫(表)的資料都不一樣,沒有交集
(3)所有庫(表)的并集是全量資料
二、什麼是垂直拆分
垂直拆分是指,将一個屬性較多,一行資料較大的表,将不同的屬性拆分到不同的表中,以降低單庫(表)大小,達到提升性能的目的的方法,垂直切分後,各個庫(表)的特點是:
(1)每個庫(表)的結構都不一樣
(2)一般來說,每個庫(表)的屬性至少有一列交集,一般是主鍵
還是以上文提到的使用者表為例,如果要垂直拆分,可能拆分結果會是這樣的:
user_base(
uid bigint,
name varchar(16),
pass varchar(16),
age int,
sex tinyint,
flag tinyint,
…);
user_ext(
uid bigint,
sign varchar(64),
intro varchar(256)
…);
三、垂直切分的依據是什麼
當一個表屬性很多時,如何來進行垂直拆分呢?如果沒有特殊情況,拆分依據主要有幾點:
(1)将長度較短,通路頻率較高的屬性盡量放在一個表裡,這個表暫且稱為主表
(2)将字段較長,通路頻率較低的屬性盡量放在一個表裡,這個表暫且稱為擴充表
如果1和2都滿足,還可以考慮第三點:
(3)經常一起通路的屬性,也可以放在一個表裡
優先考慮1和2,第3點不是必須。另,如果實在屬性過多,主表和擴充表都可以有多個。
一般來說,資料量并發量比較大時,資料庫的上層都會有一個服務層。需要注意的是,當應用方需要同時通路主表和擴充表中的屬性時,服務層不要使用join來連表通路,而應該分兩次進行查詢:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iYxYGMzgDOwUTYkJjY2ETOlJmM0ETMyYDMxY2MyM2Yj9CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
原因是,大資料高并發網際網路場景下,一般來說,吞吐量和擴充性是主要沖突:
(1)join更消損耗資料庫性能
(2)join會讓base表和ext表耦合在一起(必須在一個資料庫執行個體上),不利于資料量大時拆分到不同的資料庫執行個體上(機器上)。畢竟減少資料量,提升性能才是垂直拆分的初衷。
四、為什麼要這麼這麼拆分
為何要将字段短,通路頻率高的屬性放到一個表内?為何這麼垂直拆分可以提升性能?因為:
(1)資料庫有自己的記憶體buffer,會将磁盤上的資料load到記憶體buffer裡(暫且了解為程序内緩存吧)
(2)記憶體buffer緩存資料是以row為機關的
(3)在記憶體有限的情況下,在資料庫記憶體buffer裡緩存短row,就能緩存更多的資料
(4)在資料庫記憶體buffer裡緩存通路頻率高的row,就能提升緩存命中率,減少磁盤的通路
舉個例子就很好了解了:
假設資料庫記憶體buffer為1G,未拆分的user表1行資料大小為1k,那麼隻能緩存100w行資料。
如果垂直拆分成user_base和user_ext,其中:
(1)user_base通路頻率高(例如uid, name, passwd, 以及一些flag等),一行大小為0.1k
(2)user_ext通路頻率低(例如簽名, 個人介紹等),一行大小為0.9k
那邊記憶體buffer就就能緩存近乎1000w行user_base的記錄,通路磁盤的機率會大大降低,資料庫通路的時延會大大降低,吞吐量會大大增加。
五、總結
(1)水準拆分和垂直拆分都是降低資料量大小,提升資料庫性能的常見手段
(2)流量大,資料量大時,資料通路要有service層,并且service層不要通過join來擷取主表和擴充表的屬性
(3)垂直拆分的依據,盡量把長度較短,通路頻率較高的屬性放在主表裡
希望沒有浪費你這一分鐘,幫轉哈。
==【完】==