6

背景とシステム ビュー

分散環境に請求システムを実装しました。端末ごとに毎分約 2 枚の請求書を生成する 4 つの端末があります。バックエンドとして Mysql を使用し、クライアント技術として C#、winforms を使用しています。

請求システムで最も重要な制約は、請求書番号が連続していなければならないということです。そのために、次のようなクエリを実行します

疑似コードで

let x ="SELECT count(*) from Orders where IsInvoiceGenerated=1 and FinancialYear=val

new invoicenum = x + 1;

問題 411 番目の請求書まではすべて正常に実行されていましたが、その後、システムは突然 2 つの請求書をスキップし、請求書 414 を生成しました。システムの状態を調査したところ、システムが外部から改ざんされていないことがわかり、ワークベンチから誰もデータベースにアクセスしていないことがわかりました。これは、法的な問題もあるため、大きな問題です。

請求番号が常に連続していることを確認するための最善の方法を提案していただけますか?

4

5 に答える 5

2

これが私が思いついた解決策です:

CREATE TABLE `inv` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `invNo` int(10) DEFAULT NULL,
  `invName` varchar(100) DEFAULT NULL,
  `cratetedAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE     CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;

このクエリで新しい請求書を作成します

INSERT INTO `inv` (`invNo`, `invName`) SELECT (SELECT MAX(invNo)+1 FROM `inv` FOR UPDATE) AS `invNo`, 'Invoice 1';

を使用SELECT FOR UPDATEすると、テーブルの書き込みロックが取得されるため、同時挿入はブロックされ、同時に読み取りに制限はありません。したがって、唯一のボトルネックは、一度に 1 つずつしか発生しない請求書の作成である可能性があり、それは許容できると思います。

唯一の懸念は、サーバーが停止したり、クラッシュしたり、ネットワークが失われたり、何か異常が発生したりして、コードがトランザクションを終了する機会を得られず、デッドロックが発生する可能性がある場合にどうなるかです。

wait_timeout率直に言って、この最後の状況を処理する方法がわかりませんが、これに MySql プロパティを使用できることをどこかで読んでいましたが、使用方法がわかりません。ただし、サーバーコードにSpringを使用し、@Transactional timeoutプロパティを使用していますが、それが私をカバーするかどうかはわかりません.

于 2016-03-11T13:29:08.623 に答える
2

一意の番号を作成するには、現在の番号をテーブルに保存してから、新しい請求書を作成するときに次の手順を実行する必要があります。

  1. 取引を開始する
  2. テーブルから番号を取得する
  3. 請求書に番号を設定する
  4. 先ほどの表に数字+1をセット
  5. 専念
于 2013-01-08T13:25:54.890 に答える
1

始める前に、@Grumbler85に謝罪したいと思います-あなたは正しかったです。この質問はしばらくの間私を悩ませました、そして私は私ができる限り私の知識にそれを答えようとします。

トランザクションとロックの両方が不十分なソリューションです。

理由:テーブルをロックしたら、ロックを解除する必要があるため、ロックは適切ではありません。ロック解除は失敗する可能性があります。一般に、ネットワークとコンピューターの不安定な性質は誰もが知っています。つまり、ロックとロック解除を発行するには、C#アプリケーションを使用する必要があります。請求書を生成するたびに、カウンターとして使用されているテーブルをロックする必要があり、他のすべてのMySQLセッションはロックを解除するまで待機する必要があります。私の経験から、数日以内に、ロックを解除することを仕事とする管理者を雇う必要があります。

各トランザクションはデータのスナップショットで動作するため、トランザクションは十分ではありません(簡単な説明、トランザクション分離レベルは変更できます)。つまり、1つのトランザクションで請求書番号を6と計算でき、別のトランザクションでも請求書番号を6と計算できます。

できることは、invoice_numberを一意にすることです。これにより、2つ(またはそれ以上)のトランザクションが同じ番号を挿入しようとすると、少なくとも1つのトランザクションで例外が発生し、ギャップを防ぎますが、請求書の作成に失敗します。

auto_incrementを使用することもオプションではありません。Auto_incrementは単なるカウンターです。つまり、auto_incrementは、何らかの理由で削除された数値を「再利用」しません。これは、エラーが発生してトランザクションを保存できなかったため、そのレコードに対して計算されたauto_incrementが事実上失われるためです。

では、どのようなオプションがありますか?個人的には、事前定義された時間間隔で実行され、設定されていない請求書を更新する単純なサービスを作成しますinvoice_number。このサービスは同時アクセスを提供せず、すでに挿入されている請求書のセットで機能する1つの接続が常にアクティブになります。

確かに(イギリスなどの特定の国では)請求書の番号付​​けを順番に行う必要があることを規定する法律がありますが、それについても間違っていました。出典:http ://www.hmrc.gov.uk/vat/managing/charge/vat-invoices.htmおよび出典からの抜粋:

一意で、前の請求書の番号に続く請求書番号-シリアル番号の付いた請求書を台無しにするかキャンセルする場合は、次回のVAT検査でVAT担当者に表示するために保持する必要があります

最後のオプションは、2つ以上のトランザクションが同じ請求書番号を取得した場合に請求書作成の失敗に満足することです。つまり、失敗したトランザクションを再実行する方法を実装する必要があります(これは単純ではありません)。

于 2013-01-10T16:23:08.540 に答える
0

うーん、もっと簡単かもしれません。

  • 始める;
  • 番号なしで正確なcreated_at datetimeで請求書を保存します
  • このcreated_at datetime & autoincrement pkの前に作成された請求書の数を計算します
  • 請求書の更新番号
  • 専念;

何もロックする必要はありません。

于 2015-08-02T19:47:12.103 に答える