6

電子メールが 1 回だけ送信されるようにしたいので、Oracle SQL で次のステートメントを使用しています。

update mytable set mail_sent = 't' where id = ? and mail_sent = 'f'

変更された行数を調べます。行が変更されていない場合、別のプロセスが最初に同じことを行い、メールを送信します。1 行が変更された場合、メールを送信します。(もちろん、メールの送信に失敗した場合は、mail_sent をリセットします。プロセスがクラッシュして mail_sent が 't' のままになる可能性がわずかにあるため、メールは送信されません。それで我慢します。)

これが競合状態に対して安全であると確信することはできません (プロセス 1 が 'f' を読み取り、プロセス 2 が 'f' を読み取り、プロセス 1 が 't' を書き込む前に、両方のプロセスが行を変更したと見なし、2 つの電子メールが送信されます。問題を回避するために分離レベルを SERIALIZABLE に設定していますが、これは実際に必要ですか、それなしでも安全ですか?

4

3 に答える 3

2

これを行う安全な方法の 1 つは、更新する行を選択することです。これにより、行が排他ロックされ、電子メールが送信され、レコードが 't' に更新されてコミットされます。

レコードのロックは、このメソッドの意図的な設計目的です。電子メールの送信が確認されるまでは、送信したことを示したくありません。送信が実際に失敗したことを示す回復プロセスが必要です。同様に、電子メールの送信プロセスを開始したときに、別のセッションでそのプロセスを開始したくありません。

長期間のロックを回避する必要がある場合は、プロセスを 2 つのステップに分割することをお勧めします。メール送信プロセスが開始されたことを確認するフラグを設定し (実際にはタイムスタンプを付けます)、再度設定します。 (または別の設定)送信を確認します。確認を取得するのにかかった時間を監視できるため、それ自体は悪い方法ではありません。また、私の経験では、一部のインターネット リクエストがアプリケーション時間のかなりの割合を占める可能性があります。

于 2013-09-12T18:11:53.623 に答える