1. 程式人生 > 資料庫 >實現資料庫水平切分的兩個思路

實現資料庫水平切分的兩個思路

引言

隨著網際網路應用的廣泛普及,海量資料的儲存和訪問成為了系統設計的瓶頸問題。對於一個大型的網際網路應用,每天幾十億的PV無疑對資料庫造成了相當高的負載。對於系統的穩定性和擴充套件性造成了極大的問題。通過資料切分來提高網站效能,橫向擴充套件資料層已經成為架構研發人員首選的方式。

  • 水平切分資料庫:可以降低單臺機器的負載,同時最大限度的降低了宕機造成的損失;
  • 負載均衡策略:可以降低單臺機器的訪問負載,降低宕機的可能性;
  • 叢集方案:解決了資料庫宕機帶來的單點資料庫不能訪問的問題;
  • 讀寫分離策略:最大限度了提高了應用中讀取資料的速度和併發量;

為什麼要資料切分

上面對什麼是資料切分做了個概要的描述和解釋,讀者可能會疑問,為什麼需要資料切分呢?像Oracle這樣成熟穩定的資料庫,足以支撐海量資料的儲存與查詢了?為什麼還需要資料切片呢?

的確,Oracle的DB確實很成熟很穩定,但是高昂的使用費用和高階的硬體支撐不是每一個公司能支付的起的。試想一下一年幾千萬的使用費用和動輒上千萬元的小型機作為硬體支撐,這是一般公司能支付的起的嗎?即使就是能支付的起,假如有更好的方案,有更廉價且水平擴充套件效能更好的方案,我們為什麼不選擇呢?

我們知道每臺機器無論配置多麼好它都有自身的物理上限,所以當我們應用已經能觸及或遠遠超出單臺機器的某個上限的時候,我們惟有尋找別的機器的幫助或者繼續升級的我們的硬體,但常見的方案還是橫向擴充套件,通過新增更多的機器來共同承擔壓力。我們還得考慮當我們的業務邏輯不斷增長,我們的機器能不能通過線性增長就能滿足需求?Sharding可以輕鬆的將計算,儲存,I/O並行分發到多臺機器上,這樣可以充分利用多臺機器各種處理能力,同時可以避免單點失敗,提供系統的可用性,進行很好的錯誤隔離。

綜合以上因素,資料切分是很有必要的。 我們用免費的MySQL和廉價的Server甚至是PC做叢集,達到小型機+大型商業DB的效果,減少大量的資金投入,降低運營成本,何樂而不為呢?

在大中型專案中,在資料庫設計的時候,考慮到資料庫最大承受資料量,通常會把資料庫或者資料表水平切分,以降低單個庫,單個表的壓力。這裡介紹兩個專案中常用的資料表切分方法。當然這些方法都是在程式中?使用一定的技巧來路由到具體的表的。首先我們要確認根據什麼來水平切分?在我們的系統(SNS)中,使用者的UID貫穿系統,唯一自增長,根據這個欄位分表,再好不過。

方法一:使用MD5雜湊

做法是對UID進行md5加密,然後取前幾位(我們這裡取前兩位),然後就可以將不同的UID雜湊到不同的使用者表(user_xx)中了。

function getTable( $uid ){
 $ext = substr ( md5($uid),2 );
 return "user_".$ext;
}

通過這個技巧,我們可以將不同的UID分散到256中使用者表中,分別是user_00,user_01 ...... user_ff。因為UID是數字且遞增,根據md5的演算法,可以將使用者資料幾乎很均勻的分別到不同的user表中。

但是這裡有個問題是,如果我們的系統的使用者越來越多,勢必單張表的資料量越來越大,而且根據這種演算法無法擴充套件表,這又會回到文章開頭出現的問題了。

方法二:使用移位

具體方法是:

public function getTable( $uid ) {
 return "user_" . sprintf( "d",($uid >> 20) );
}

這裡,我們將uid向右移動20位,這樣我們就可以把大約前100萬的使用者資料放在第一個表user_0000,第二個100萬的使用者資料放在第二個表user_0001中,這樣一直下去,如果我們的使用者越來越多,直接新增使用者表就行了。由於我們保留的表字尾是四位,這裡我們可以新增1萬張使用者表,即user_0000,user_0001 ...... user_9999。一萬張表,每張表100萬資料,我們可以存100億條使用者記錄。當然,如果你的使用者資料比這還多,也不要緊,你只要改變保留表字尾來增加可以擴充套件的表就行了,如如果有1000億條資料,每個表存100萬,那麼你需要10萬張表,我們只要保留表字尾為6位即可。

上面的演算法還可以寫的靈活點:

/**
 * 根據UID分表演算法
 * @param int $uid //使用者ID
 * @param int $bit  //表字尾保留幾位
 * @param int $seed //向右移動位數
 */
function getTable( $uid,$bit,$seed ){
 return "user_" . sprintf( "%0{$bit}d",($uid >> $seed) );
}

小結

上面兩種方法,都要對我們當前系統的使用者資料量做出可能最大的預估,並且對資料庫單個表的最大承受量做出預估。

比如第二種方案,如果我們預估我們系統的使用者是100億,單張表的最優資料量是100萬,那麼我們就需要將UID移動20來確保每個表是100萬的資料,保留使用者表(user_xxxx)四位來擴充套件1萬張表。

又如第一種方案,每張表100萬,md5後取前兩位,就只能有256張表了,系統總資料庫就是:256*100萬;如果你係統的總資料量的比這還多,那你實現肯定要MD5取前三位或者四位甚至更多位了。

兩種方法都是將資料水平切分到不同的表中,相對第一種方法,第二種方法更具擴充套件性。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對我們的支援。如果你想了解更多相關內容請檢視下面相關連結