web-dev-qa-db-ja.com

シーケンスとタイムスタンプの代替:時間内でレコードを一意に順序付け

Oracle 11gR2 Exadata

いつレコードが作成されるかを一意に識別する必要があります。シーケンスキャッシングは、シーケンスベースのIDを使用できないことを意味し、バッチ挿入は、1つのバッチで挿入されたすべてのレコードが同じタイムスタンプ値を持つことを意味します(TIMESTAMP(9)を使用しても)。 Twitterの since_id concept に似ています。

これまでに考えた中で最高の選択肢

  • 一意のタイムスタンプごとに使用される追加のシーケンスを作成する
  • 各レコードに一意のタイムスタンプを強制するためにレコードの挿入をバッチ処理しない
  • これはExadataでの問題を解決しないという議論がありましたが、シーケンスをキャッシュしていません

これが私の要件です。ユーザーがマーカーとしてシーケンスを提供し、それ以降のすべてのレコードを要求できるAPIがあります。たとえば、マーカー7の1000レコードをリクエストすると、IDが1007より大きいテーブルから1000レコードが取得されます。例として、返された1000レコードの数値的に最大のIDが2045であるため、2045を次のように返します。マーカークライアントは後で、2045のマーカーで1000レコードを要求し、次の1000のバッチと新しいマーカーの取得を期待しています。

すべてのレコードを欠落することなく機能する任意のサイズですべてのレコードを取得できるようにするかなり簡単な方法。ただし、複数のExadataノード間でのシーケンス・キャッシングのため、クライアントがマーカー1007の1000レコードをリクエストした時点で、2020のIDを持つレコードが作成されていない可能性があります。したがって、2045年のマーカーを使用して次のリクエストを行うと、彼らは永遠に2020年の記録を逃しています。 IDを使用して関連付けられたレコードのタイムスタンプを取得することでこれを解決しますが、一意のタイムスタンプを保証するために、常にテーブルにレコードを常に挿入する必要があります。

仮定:

  • 一括/一括挿入で個々のレコードのタイムスタンプを個別に取得する方法ではありません
  • 複数のノードが原因で、シーケンスにNOCACHEが指定されていても、レコードの挿入が非時系列になる場合があります(たとえば、シーケンス値180のレコードは、シーケンス値179のレコードよりも大きいsystimestampで書き込まれる可能性があります)

うまくいけば、私は既存の答えを検索するための正しい用語を見つけていないだけです。これは何年もの間いくつかのパターンによって解決されるべきだった問題だと感じています。 Twitterはそれを解決したと思います...

5
eebbesen

タイムスタンプで注文することをお勧めします。同一のタイムスタンプを持つ唯一のレコードが同じトランザクションに挿入される限り、シーケンスベースのIDを二次的な順序として使用できます。

5
Tyler Holien

まあ、結局のところ、最初の挿入が完了した後、同期プロセスを使用して各レコードにIDを割り当てる必要がありました。

私に指摘されたように、私が本当に探していたのはコミットシーケンスでした。残念ながら、タイムスタンプでもコミット時ではなく挿入時に生成されるため、タイムスタンプでもそれは提供されません。したがって、1つのレコードグループ(A)のタイムスタンプが2番目のレコードグループ(B)の前にタイムスタンプがあり、実際にはグループBが最初にデータベースにコミットされる場合があります。その後、私は元の問題を解決していませんでした。

最後に、(「シーケンス」テーブルでの)テーブルロックとスキップロックを使用するプロシージャを介して、同期されたホーム実装シーケンスであるものを使用しました。したがって、挿入プロセスをシングルスレッド化して速度を落とす必要はありませんが、IDがレコードにコミットされたときのコミット順であることが保証されたIDが保証されます。

1
eebbesen