2

Ruby クライアントを使用して PostgreSQL で作業しており、次のような SHA-1 ハッシュ ID でテーブルを分割したいと考えています。

                    id                    
------------------------------------------
 fe935b318f6976afdec83fa7339ff2069b0bc0c3
 d67948e38a645fd5ffdde6dab4dc627b2b19d1b1
 0d304f5134b0a46c2248a34c3e9c50ad2b547fdf

プロセス分割では、データセットを N 個の部分に分割し、それぞれを N 個のプロセスの 1 つに割り当てます。RDBMS に整数キーがある場合は、次のように簡単です。

select * from items_to_be_processed where MOD(id, N) = ASSIGNED_PARTITION

Ryan Smithは、文字列キーがある場合、それらに CRC32 を使用して整数を取得し、次にモジュラスを取得できることを示唆していますが、キーがほとんど均等に分散されていると仮定すると (SHA-1 ハッシュの場合と同様に)、これは簡単になる?の場合N = 4:

select * from items_to_be_processed where id < ASSIGNED_PARTITION_1
select * from items_to_be_processed where id < ASSIGNED_PARTITION_2 and id >= ASSIGNED_PARTITION_1
select * from items_to_be_processed where id < ASSIGNED_PARTITION_3 and id >= ASSIGNED_PARTITION_2
select * from items_to_be_processed where id >= ASSIGNED_PARTITION_4

たぶん、N = 2もし

select * from items_to_be_processed where id < '8888888888888888888888888888888888888888'   <- process 1
select * from items_to_be_processed where id >= '8888888888888888888888888888888888888888'  <- process 2

N が与えられた場合、パーティション ポイントを計算するにはどうすればよいですか (半分に8888888888888888888888888888888888888888分割ffffffffffffffffffffffffffffffffffffffffしますが、正しく計算できなかった可能性があります)。SQL (Postgres) で行うべきですか、それとも呼び出しを行う Ruby クライアントで行うべきですか?

PS。MongoDB クックブックのランダム属性のアイデアに触発されました。

アップデート

888...上記は正しく計算されませんでした-私を近づけてくれたCarl Norumの答えのおかげで、Rubyでそれを行う方法があります:

>> 'f'*40
=> "ffffffffffffffffffffffffffffffffffffffff"
>> a = 0xffffffffffffffffffffffffffffffffffffffff
=> 1461501637330902918203684832716283019655932542975
>> b = a / 2
=> 730750818665451459101842416358141509827966271487
>> '%x' % b
=> "7fffffffffffffffffffffffffffffffffffffff"
>> '%x' % (b + 1)
=> "8000000000000000000000000000000000000000"
4

3 に答える 3

2

パーティションの数に応じて、ハッシュの最初のn文字のみが必要です。16までの場合、最初の文字のみ:

select *
from items_to_be_processed
where left(id, 1) < '4'

select *
from items_to_be_processed
where left(id, 1) between '4' and '7'

整数に変換する必要はありません。

次に、左のn文字だけにインデックスを付けて、小さくて高速にすることができます。

create index index_name on items_to_be_processed (left(id, 1))

計画者がこの回答へのコメントとは反対に提案された小さなインデックスを使用するように、条項にleft()表現を含める必要があります。whereこれは私が9.2でテストした方法です:

create table itbp (id char(32));

insert into itbp
select md5(a::text)
from generate_series(1, 100000) s(a)
;

postgresqlのデフォルトのインストールにはsha1関数がないため、より簡単なテストを作成するために、sha1の代わりにmd5を使用しました。

create index itbp_left_1_id_index on itbp (left(id, 1));

analyze itbp;

テストする前に分析することを忘れませんでした。今、両方が説明します:

explain select *
from itbp
where left(id, 1) between '4' and '7'
;
                                             QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on itbp  (cost=529.17..1979.74 rows=24663 width=33)
   Recheck Cond: (("left"((id)::text, 1) >= '4'::text) AND ("left"((id)::text, 1) <= '7'::text))
   ->  Bitmap Index Scan on itbp_left_1_id_index  (cost=0.00..523.00 rows=24663 width=0)
         Index Cond: (("left"((id)::text, 1) >= '4'::text) AND ("left"((id)::text, 1) <= '7'::text))

explain select *
from itbp
where id >= '4' and id < '8'
;
                         QUERY PLAN                         
------------------------------------------------------------
 Seq Scan on itbp  (cost=0.00..2334.00 rows=24784 width=33)
   Filter: ((id >= '4'::bpchar) AND (id < '8'::bpchar))
于 2013-01-08T16:18:09.940 に答える
2

それは正しく計算されていません。あなたの例は、10 進数を取り、それをyields9999で割ると言っているようなものです。それよりも:25555

0xffffffffffffffffffffffffffffffffffffffff

1 未満:

0x10000000000000000000000000000000000000000

その数を範囲に分割するのは簡単です。あなたの N=2 の例では、キーの半分は以下よりも少ないです:

0x8000000000000000000000000000000000000000

そして半分はそれ以上です。N=4 の場合も同様です。

ASSIGNED_PARTITION_1 = 0x4000000000000000000000000000000000000000
ASSIGNED_PARTITION_2 = 0x8000000000000000000000000000000000000000
ASSIGNED_PARTITION_3 = 0xc000000000000000000000000000000000000000

より小さい数 (基数 10 で簡単に記述できるものなど) でパーティショニングを試みると、何が起こっているかがわかります。

比較がどのように機能するかはわかりませんが、それらは大きな数字です。私は Ruby や SQL の専門家ではありません。

于 2013-01-08T15:53:48.210 に答える
1

あなたがしたいことは、パーティション化のために id を整数に変換することです。これを行う簡単な方法を次に示します。id 値が均一に分散されていると仮定すると、最初の 2 桁を使用して 0 から 255 までの値を取得できます。

select substring(t.id, 1, 2)::bit(8)::int as IntHash,
       t.*
from t

次に、次のようなモジュラ演算を使用して範囲を定義できます。

select (substring(t.id, 1, 2)::bit(8)::int)%8 as WhichOfEightPartitions
from t

これは、ハッシュ ID が文字列として格納されていることを前提としています。

この基本的なアイデアは、「トム レーン」による応答のこの投稿から得られました。これは明らかに文書化されていない動作ですが、SQLFiddle では機能します。

于 2013-01-08T16:12:43.027 に答える