大規模資料儲存叢集資料存放的設計,分散式shardid的生成-如何指定範圍隨機數,分組隨機數

德哥發表於2017-07-13

標籤

PostgreSQL , 分組ID生成 , 生成雜湊對映 , sharding , shard


背景

在一些分散式資料庫系統中,通常會有多個資料節點,使用者的資料分佈策略通常有一致性雜湊、按列雜湊、隨機分佈等。

除了隨機分佈,其他的分佈方法資料和資料節點是一對一的關係。

上當節點數變得特別特別多的時候,資料如果依舊按照全域性進行雜湊分佈,可能會帶來一個問題,例如節點數到達1萬個,一張1億的表,會分佈到1萬個節點中,那麼多個表進行JOIN時,會涉及到1萬個節點的運算,這裡面可能還涉及節點和節點之間的互動,網路會話也會特別的多。

實際上並不是每張表都需要分佈到1萬個(所有節點)的。如何解決節點數過多的問題呢?如何讓資料落到某些節點,而不是所有節點。這樣可以使得叢集更加龐大。

例如HDFS,通過NAME NODE來記錄每個BLOCK在什麼機器上。

NAME NODE的問題是,當叢集特別大的時候,NAME NODE會成為瓶頸,不利於擴充套件。

還有一些方法可以解決大叢集的問題,例如多級資料節點、分組資料節點。

大叢集的分組設計舉例

計算節點分組

例如有1萬臺主機,對應一萬個資料庫單元,劃分為一些分組,例如每100個主機(資料庫例項),一共100個分組。

當然,不一定要求每個分組的主機數一致。

給每個資料庫例項一個唯一編號。

1、例子1,如果每個分組的主機數固定,通過這種方法,可以得到某個分組內的一個隨機ID。

(適合這樣的場景,我已經知道某個表應該在哪個分組內,然後這個表在這個分組內是隨機存放的,那麼通過這種方法,可以得到一個組內隨機的主機ID)

create or replace function get_gp_rid1(gid int, gsz int) returns int as $$                                      
  select gsz*gid + (ceil(random()*gsz))::int;  
$$ language sql strict;  

隨機概率如下

postgres=# select id, count(*) from (select  get_gp_rid1(0,10) id from generate_series(1,10000) ) t group by 1 order by 1;  
 id | count   
----+-------  
  1 |   949  
  2 |   965  
  3 |  1012  
  4 |  1064  
  5 |  1029  
  6 |   970  
  7 |   964  
  8 |  1035  
  9 |  1018  
 10 |   994  
(10 rows)  
  
postgres=# select id, count(*) from (select  get_gp_rid1(1,10) id from generate_series(1,10000) ) t group by 1 order by 1;  
 id | count   
----+-------  
 11 |   993  
 12 |  1023  
 13 |   986  
 14 |   981  
 15 |   978  
 16 |   994  
 17 |  1002  
 18 |  1019  
 19 |   976  
 20 |  1048  
(10 rows)  
  
postgres=# select id, count(*) from (select  get_gp_rid1(2,10) id from generate_series(1,10000) ) t group by 1 order by 1;  
 id | count   
----+-------  
 21 |  1009  
 22 |   985  
 23 |   988  
 24 |  1040  
 25 |   988  
 26 |  1065  
 27 |   986  
 28 |   957  
 29 |   993  
 30 |   989  
(10 rows)  
  
postgres=# select id, count(*) from (select  get_gp_rid1(2,10) id from generate_series(1,10000000) ) t group by 1 order by 1;  
 id |  count    
----+---------  
 21 |  999704  
 22 |  999015  
 23 | 1001106  
 24 |  999979  
 25 |  999599  
 26 |  999417  
 27 | 1000242  
 28 | 1000675  
 29 |  999423  
 30 | 1000840  
(10 rows)  
Time: 4629.229 ms  

2、例子2,對於組的機器數不一致,但是主機ID連續的場景,可以使用這種方法得到一個組內的隨機ID。

create or replace function get_gp_rid2(f int, c int) returns int as $$                                      
  select f - 1 + (ceil(random()*(c-f+1)))::int;  
$$ language sql strict;  

隨機分佈均勻

postgres=# select id, count(*) from (select  get_gp_rid2(2,10) id from generate_series(1,10000000) ) t group by 1 order by 1;  
 id |  count    
----+---------  
  2 | 1111981  
  3 | 1112798  
  4 | 1110522  
  5 | 1111070  
  6 | 1111159  
  7 | 1109720  
  8 | 1109822  
  9 | 1111450  
 10 | 1111478  
(9 rows)  
Time: 4631.884 ms  

3、例子3,組的機器數不一致,同時主機ID不連續,可以通過這種方法得到一個組內的隨機ID。

create or replace function get_gp_rid3(id int[]) returns int as $$                                      
  select id[ceil(array_length(id,1)*random())];  
$$ language sql strict;  

資料分佈均勻

postgres=# select id,count(*) from (select get_gp_rid3(array[1,2,3,4,5,7,8,9,100,199]) id from generate_series(1,1000000)) t group by 1 order by 1;  
 id  | count    
-----+--------  
   1 | 100898  
   2 |  99818  
   3 |  99434  
   4 | 100085  
   5 | 100461  
   7 | 100361  
   8 |  99725  
   9 | 100002  
 100 |  99646  
 199 |  99570  
(10 rows)  

表和分組的對映關係

表和分組的對映關係,可以使用類似name node的方法。

因為分組數可能發生變化,不推薦使用一致性演算法類的MAPPING方法,確保表不需要隨著分組的變化而變化。

分組內資料分佈設計

1 完全隨機分佈

如果資料在分組內完全隨機分佈,那麼就可以像前面寫的幾個函式那樣,獲得分組內主機的隨機ID。

2 虛擬BLOCK分佈

首先需要將資料存放規劃為虛擬BLOCK聚集的方式(例如100000條記錄一個BLOCK,舉例而已)。每個BLOCK有對應的編號。

每個BLOCK落在不同的資料庫例項(主機)中,這個對映關係依舊建議使用類似name node的方法。

因為分組內的主機(資料庫例項)數可能發生變化,不推薦使用一致性演算法類的MAPPING方法,確保表不需要隨著分組內主機(資料庫例項)數的變化而變化。

資料記錄和虛擬BLOCK的關係

1、雜湊,例如按某列進行雜湊,根據雜湊值決定記錄寫入哪個BLOCK。

建議使用一致性雜湊分佈,確保在擴充套件或收縮BLOCK數量時,資料的移動最小。

《一致性雜湊在分散式資料庫中的應用探索》

2、範圍,適合自增、時間、序列等型別,例如每100000一個block,等等。

3、固定雜湊,這種方式比較暴力,例如一開始就設計好一個固定的雜湊數,如65536。

固定雜湊的擴容不太方便,擴容時移動的資料可能較多。建議按2的N或者N的N次方雜湊和擴容。這樣的話,擴充套件只是分裂BLOCK,也蠻簡單的。

虛擬BLOCK的遷移

採用NAME NODE記錄了BLOCK和分組內主機的對映關係,因此MOVE block也變得很簡單,只要移動,並更新NAME NODE。

小結

要管理特別大的叢集,資料分佈只是其中很小的一個部分。

但是資料分佈是一個非常重要的緩解,分佈規則沒有涉及好,可能導致將來擴充套件、遷移、效能、穩定性等帶來不便。

分組是一個將大化小的方法,因為往往一個業務、或者一個表,不需要離散到所有主機。離散到過多的主機上可能會導致連線、資料重分佈,資料JOIN等一些問題。

通常的做法是將需要JOIN,或者同類業務的資料,儘量分佈到同樣的主機分組中。確保在進行資料分析時,資料的移動較小。


相關文章