web-dev-qa-db-ja.com

postgresの複数のテーブルで同じシーケンスを使用すると何が問題になるのでしょうか?

共有シーケンスを使用して、データベース内のすべてのテーブルの主キーにIDを割り当てることを検討しています。それらの約100があります。カップルだけが頻繁かつ定期的に挿入されます。実際に試して負荷でテストする段階に移行する前に、それが「明らかな理由によるひどいアイデア」であることを排除したいと思います。

私たちのピーク負荷は、いくつかのテーブルで、1秒あたり1000挿入のオーダーです。

これまでの調査では、シーケンスの生成速度は問題ではないはずです-シーケンスの断片化(ギャップ)が発生しますが、問題ではないはずです-IDの枯渇は問題ではありません

他に大きなものがないかどうかはわかりません。特に以前に試してみて、ポジティブまたはネガティブな経験をした人からの人々の意見に感謝します。

コンテキストについては、これを行うための2つの主な動機があります。

これを行う動機の1つは、一連の辞書(スコープと呼びます)を定義し、人間が読める単語をそれらのIDに割り当てることができるようにすることです。そのため、異なるテーブルのIDが重複しないようにします。したがって、あるスコープでは、id 12345に値「Green」が割り当てられ、別のスコープでは「Verde」が割り当てられる場合があります。 (実際、私たちはそれを国際化に使用しませんが、いつかは使用するかもしれません)。

もう1つの動機は、フィールドに複数のデプロイメントを配置し、(各デプロイメントのシーケンスの最上位桁を一意に設定することによって)デプロイメントが主キーと重複しないことを簡単に確認できるようにすることです。 (GUID liteのように)。

10
Burleigh Bear

心に浮かぶ3つの考えられる問題は次のとおりです。

  1. 共有リソースがあれば、ボトルネックになる可能性があります。私の直感では、ピークロードの場合、これは問題にはならないはずですが、本番環境のような本番規模の環境でそのようなソリューションをベンチマークすることを強くお勧めします。

  2. 基本的に、RDB理論における目的の一部を無効にする代理キーに意味を割り当てます。その性質上、代理キーは、その関係でタプルを識別するためのキー以外の意味を持つべきではありません。エンティティが一緒に意味を持ち、衝突のないキーが必要な場合、それらが個別にモデル化されているか、要件やデータモデル設計で何かが欠落しているかは正しいですか?

  3. 潜在的な障害点を導入しています。展開で最初のシーケンスの開始点が設定されない場合はどうなりますか?次に、デプロイメントブロックエラーが発生するか、デプロイメントが同じ場所から開始して機能が「破壊」されます。また、どこかで誰かがデプロイメントを分岐するのが良い考えだと思った場合はどうしますか(本番環境では、おそらくテナント会社が自社の一部を売却し、データを分離する必要があります)。シードがアップグレードのデプロイメントまたはその他の移行の不良によって何らかの理由でリセットされた場合はどうなりますか?[0]

これらの問題のいずれもあなたに関係がない場合は、先に進んでください。このアイデアはIMOを壊すものではありません。もちろん、この方法自体に問題がないとしても、betterの方法があるかもしれません。


「UUID-lite」と言うときは、すでにUUIDを検討して割引していることを意味します。そうですか、もしそうなら、彼らがこのプロジェクトに適していないと決定する特定の理由はありますか?

UUIDを使用しない1つの考えられる理由は、インデックスの断片化ですが、その重要性は非常に誇張されていることがよくあります[1]。これに対するSQL Serverの答えは、 "シーケンシャルGUID"です。これは、キー値への割り当ての意味を割り引くと、あなたが提案しているものとほぼ同等です-おそらくpostgresはそれと同等ですか?もちろん、非常に特定の大量のワークロードでは、常に増加するインデックスが独自のパフォーマンスの問題(最後のページの競合、インデックスの統計情報の古くなります)を持つ可能性があります[2]

UUIDに対するもう1つの一般的な引数はキーの長さです。4または8で十分なのに、なぜ値あたり16バイトを使用するのですか?一意性が本当に有用なプロパティである場合、これは通常、キーサイズの問題を大幅に切り捨てます。キーサイズが問題であるが、32ビット内に保持する必要がないのではなく64ビットINTを使用したい場合は、シードされた整数キーのアイデアを実行することにより、潜在的な共有リソース競合問題を追加せずに手法を使用できます。テーブルごと[3] 通常のINT IDENTITY(<start>, 1)を使用する[4] 列の定義ですが、これも展開の複雑さを増しています(少量ですが、確かにゼロではありません)。

人間の可読性が問題として挙げられることもありますが、それは代理キーに意味を割り当てることに戻ります。

圧縮性はそれほど一般的ではありませんが、遭遇する可能性のある問題です。ほぼすべての圧縮アルゴリズムでは、SQLサーバーの順次UUIDなどを使用していない限り、UUIDはランダムな(したがって圧縮できない)データのように見える可能性があります。これは、低速ネットワークを介してアプリケーションに提供されるmanyエンティティIDを含む非常に大規模なリンク(または他のデータブロック)のセットの懸念事項になる可能性があります、またはSQL Serverのインデックス圧縮機能などを使用する必要がある場合は、どちらの問題も基本的にはキーサイズの懸念をわずかに異なる方法で表現し直しており、シーケンシャルUUIDも役立ちます。


[0]もちろんこれは通常のID列でも発生する可能性がありますが、あまり一般的でない機能を使用しているため、何か新しいエキサイティングなことをやっていれば、問題を見逃した後に経験の浅いDBAの可能性が高まります他の場所!

[1]私はSQL Serverの人です。潜在的な問題はpostgresでも同じだと思いますが、影響を軽減できる別のインデックスレイアウトがある可能性があります。

[2]繰り返しになりますが、これらはSQL Server固有である可能性があります。

[3]上位2バイト:データベースによって異なり、次の2バイト:テーブルによって異なり、残りの4バイト:増分ビット

[4]これはMS SQL Serverの構文です。postgresの構文は異なる場合がありますが、私が何を意味しているのかを理解して翻訳できるはずです。


tl; dr:自分でホイールを再発明している場合は、既存のすべてのデザインreallyが適切でないことを確認してくださいなぜ新しいものがあるのか​​、そうでないのかを考える前に。

5
David Spillett

共有シーケンスを使用して、データベース内のすべてのテーブルの主キーにIDを割り当てることを検討しています。それらの約100があります。カップルだけが頻繁かつ定期的に挿入されます。実際に試して負荷でテストする段階に移行する前に、それが「明らかな理由によるひどいアイデア」であることを排除したいと思います。

それは恐ろしい考えです。それを除外してください。 GUID/UUIDを使用するだけです。なぜあなたはその考えを否定したのですか? PostgreSQLでは _uuid-ossp_ を使用します。

uuid_generate_v4()この関数は、完全に乱数から派生したバージョン4のUUIDを生成します。

このような、

_CREATE EXTENSION uuid-ossp;
CREATE TABLE f ( f_id uuid DEFAULT uuid_generate_v4() );
_

回答が有効であるためには、多くの仮定を行います。

  • 速度は「問題ではない」
  • ギャップは「問題ではない」
  • idの枯渇は起こりません

あなたはそれを想定する必要はありません。 IDでDOSを取得し、大きなギャップを作り、1つのシャードでロールオーバーをプッシュした場合はどうなりますか?この問題に業界ソリューションを使用しないのはなぜですか?単一の欠点があることは明らかではありません。すべてが勝つ可能性が高いです。数バイトのストレージを除きます。

3
Evan Carroll

私は、他のすべてのIDの外部キーが使用する追加の中央IDテーブルで提案したパターンを使用しました。主要な生産システムで完全に機能しました。

これを行う本当の理由は、IDにデータベースを超えるスコープがある場合です。たとえば、私の例では、これらのIDは一意の金融証券および会社を列挙しました。各テーブルの自動インクリメント主キーとして、会社のIDのセットと証券の2番目のセットを作成してみませんか?他の時系列レコードが証券または会社のいずれかを参照するようにしたかったからです。したがって、中央のidテーブルにキー設定された時系列テーブル。

上記の場合、GUID/UUIDも正常に機能します。ただし、これらの形式は128ビットサイズであることが多く、ほとんどすべてのインデックス、主キー、および外部データベース内のキー、および合計id範囲内での非順次配置を軽減するのは難しい場合があり、選択パフォーマンスが最適化されない可能性があります。データベースは、選択パフォーマンスを対象としています。

GUID/UUIDには1つの利点があります。それは、統合生成プロセスを作成するのがはるかに簡単であることです。つまり、競合することはないと想定するだけで、調整なしで企業内に複数のID生成/割り当てプロセスを持つことができます。唯一のID生成プロセスがデータベース内にある場合、それはそれほど問題ではありませんが、言及する価値があります。

UUIDの生成はMACアドレスを一意にすることに依存しているため、仮想/コンテナ環境ではこれに注意する必要があることに注意してください。

0
ThatDataGuy

これを行う動機の1つは、一連の辞書(スコープと呼びます)を定義し、人間が読める単語をそれらのIDに割り当てることができるようにすることです。そのため、異なるテーブルのIDが重複しないようにします。したがって、あるスコープでは、id 12345に値「Green」が割り当てられ、別のスコープでは「Verde」が割り当てられる場合があります。 (実際、私たちはそれを国際化に使用しませんが、いつかは使用するかもしれません)。

それだけで、風変わりで壊れやすいデザインを選択する理由にはなりません。このルートをたどると、たとえば参照整合性を保証するためにデータベース機能を利用する方法がなくなります。同じことを実現する従来の正規化された方法には、RIを超えるメリットがあります。

create table tab1(tab1_id serial primary key);
create table tab2(tab2_id serial primary key);
create table scope(scope_id serial primary key, scope_name text);
create table scope_tab1(scope_id integer references scope, tab1_id integer references tab1, val text, primary key(scope_id,tab1_id));
insert into scope(scope_name) values ('English'),('French');
insert into tab1(tab1_id) select generate_series(1,5);
insert into tab2(tab2_id) select generate_series(1,5);
insert into scope_tab1(scope_id,tab1_id,val) values (1,1,'Green'),(2,1,'Verde');
select tab1_id
     , (select val from scope_tab1 where scope_id=1 and tab1_id=tab1.tab1_id) val_s1
     , (select val from scope_tab1 where scope_id=2 and tab1_id=tab1.tab1_id) val_s2
from tab1;
 tab1_id | val_s1 | val_s2 
 ------:| :-- :----- 
 1 |緑|ヴェルデ
 2 | null| null
 3 | null| null
 4 | null| null
 5 | null| null

dbfiddle ---(ここ

もう1つの動機は、フィールドに複数のデプロイメントを配置し、(各デプロイメントのシーケンスの最上位桁を一意に設定することによって)デプロイメントが主キーと重複しないことを簡単に確認できるようにすることです。 (GUID liteのように)。

他の人がしたように、新しいUUID-liteを発明するよりもUUIDを使用する方がはるかに優れている(つまり、エラーが発生しにくい)ことをお勧めします。

私はまだそれがあなたの最善の策であるとは思いません—あなたはシャーディングではないので、あなたが提供した情報から見ることができるデプロイメント間で重複しないIDを実際に持つ必要はありません。おそらく、これらのテーブルのIDを確認する以外に、データベース内のデプロイメントを識別する方法は他にもあるでしょう。