5

請求用の SaaS ペット プロジェクトがあります。その中で、クライアントがそれぞれチケット番号 1001 から始まるようにします。明らかに、Postgres で単純な自動フィールドを使用して値に 1000 を追加することはできません。これは、すべてのクライアントが同じデータベースと同じticketsテーブルを共有するためです。整数型の列とクエリ (疑似 SQL)SELECT LATEST number FROM tickets WHERE client_id = [current client ID]を使用して最新の番号+ 1を取得し、その番号を使用して次のnumber. 問題は、並行性の場合、このように 2 つのチケットが同じ番号で終了する可能性が高いことです。Django内または生のSQLでこれを実行できるようにするために必要な数(Bashまたはその他の種類のものを使用する場合と比較して)。

私の例を強制的に機能させる方法を探しているわけではありません。クライアントごとにチケット番号を個別にインクリメントする必要があるという問題の解決策を探しています。

4

2 に答える 2

5

この問題に対する「安価な」解決策はないと思います。マルチユーザー環境で安全な (必ずしも高速であるとは限りません) 唯一の解決策は、顧客ごとに 1 つの行を持つ "カウンター" テーブルを用意することです。

各トランザクションは、次のように、新しいチケットを挿入する前にまず顧客のエントリをロックする必要があります。

UPDATE cust_numbers
  SET current_number = current_number + 1
WHERE cust_id = 42
RETURNING current_number;

ワンステップで3つのことを行う

  1. その顧客の現在の「連番」を増やす
  2. 行をロックして、同じことをしている他のトランザクションがロックを待つ必要があるようにします
  3. その列の新しい値を返します。

その新しい番号を使用して、新しいチケットを挿入できるようになりました。トランザクションがコミットされると、テーブルのロックも解放されるcust_numbersため、他のトランザクションは「番号を待機」して続行できます。

この背後にあるロジックが一元化されるように、2 つのステップ (update.. return & insert) を単一のストアド関数にラップすることができます。アプリケーションはselect insert_ticket(...)、チケット番号がどのように生成されるかを知らずに呼び出すだけです。

cust_numbersまた、顧客テーブルにトリガーを作成して、新しい顧客が作成されたときにテーブルに行を自動的に挿入することもできます。

これの欠点は、同じ顧客の新しいチケットを挿入するトランザクションを効果的にシリアル化することです。システムの挿入量によっては、これがパフォーマンスの問題になる場合があります。

編集
これのもう 1 つの欠点は、新しい開発者がこれを忘れた場合などに問題が発生する可能性がある方法でチケットを挿入することを強制されないことです。

于 2012-04-18T15:19:30.177 に答える
2

顧客ごとにシーケンスを作成してから、列の値をに設定できますnextval('name_of_the_sequence')。これが実際の仕組みserialです。あなたの場合の唯一の違いは、列にデフォルト値を使用せず、複数のシーケンスがあることです。

新しい行を挿入するときに正しいシーケンスを選択してこれらのシーケンスを作成することは、PL/Pgsqlプロシージャを介してうまく行うことができます。

于 2012-04-18T14:02:59.310 に答える