在大中型項目中,在數據庫設計的時候,考慮到數據庫最大承受數據量,通常會把數據庫或者數據表水平切分,以降低單個庫,單個表的壓力。這里介紹兩個項目中 常用的數據表切分方法。當然這些方法都是在程序中?使用一定的技巧來路由到具體的表的。首先我們要確認根據什么來水平切分?在我們的系統(SNS)中,用 戶的UID貫穿系統,唯一自增長,根據這個字段分表,再好不過。
function getTable( $uid ){ $ext = substr ( md5($uid) ,0 ,2 ); return "user_".$ext; } /* * 參 數:$project 字符串 項目 * $group 數組 待進行分表的字段及值 * &$tables 數組 目前已經分的表,以值址方式傳參 * $length 整型 截取字符串前幾位,此項與分表的數據密切相關,默認為2 * 功 能:分表引擎 */ function engineSplitTable($project, $group, &$tables, $length = MD5_PREFIX_LENGTH) { $keys = array_keys($group); $values = array_values($group); $sequence = substr(md5(implode('', $values)), 0, $length); $table = $project.'_'.implode('_', $keys).'_'.$sequence; if(isset($tables[$table])) { } else { $tables[$table] = array('textFile' => '', 'fields' => '', 'counter' => 0); } return $table; }
注意:md5出來的字串僅包含0~9a~f,你可以把每位看成一個16進制的數字。那么 substr ( md5($uid) ,0 ,2 ) 出來的范圍就是16進制的 00~ff,轉成10進制就是 0~16*16
md5后的各位實際上是: 10個數字 + 6個字符的組合 = 16
md5后的值要么全是小寫字母,要么全是大寫字母,不會出現混合的情況,所以字母就只有 a-f || A-F 這兩種之一
通過這個技巧,我們可以將不同的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取前三位或者四位甚至更多位了。
兩種方法都是將數據水平切分到不同的表中,相對第一種方法,第二種方法更具擴展性。
參考:http://www.nowamagic.net/librarys/veda/detail/1528
http://lampblog.org/285.html
http://blog.csdn.net/hhq163/article/details/6219221