ビジネス開発では、リクエストの繰り返しを防ぐという問題に直面することがよくあります。リクエストに対するサーバーの応答にデータの変更やステータスの変更が含まれる場合、大きな損害を引き起こす可能性があります。繰り返しのリクエストによる影響は、トランザクション システム、販売後の権利保護、支払いシステムにおいて特に深刻です。
フォアグラウンド操作、高速操作、ネットワーク通信、またはバックエンドの応答にジッターがあると、バックエンド処理が繰り返される可能性が高くなります。フロントエンド操作をデバウンスし、高速操作を防ぐための対策を講じるために、まずフロントエンドの制御層について考えます。フロントエンドが操作をトリガーすると、確認インターフェイスがポップアップ表示されたり、エントリが無効になったりする場合があり、カウントダウンなどについてはここでは詳しく説明しません。しかし、フロントエンドの制限だけでは解決できる問題は少なく、十分に徹底されていないため、バックエンド独自の重複防止対策が不可欠かつ義務化されています。
インターフェイスの実装では、繰り返されるリクエストのうち 1 つだけが有効であることを保証するために、インターフェイスが冪等性を満たす必要があることがよくあります。
クエリ クラスのインターフェイスはほとんどの場合冪等ですが、データの挿入や複数モジュールのデータ更新が含まれる場合、冪等、特に同時実行性が高い場合の冪等の要件を達成することがより困難になります。たとえば、サードパーティの支払いフロントエンド コールバックとバックグラウンド コールバック、サードパーティの支払いバッチ コールバック、遅いビジネス ロジック (ユーザーが返金申請を提出する、加盟店が返品/返金に同意するなど)、または低速なネットワーク環境が原因で、繰り返し処理のリスクシナリオ。
ここでは、試した重複防止処理方法の効果を説明するために、「ユーザーが返金申請を送信する」例を示します。バックエンドの重複防止処理の 3 つの方法を試しました。
この方法はシンプルで直感的であり、DB Refund からクエリされます。詳細 (ステータスを含む) は、繰り返しのリクエストに対処するために特別な作業を費やすことなく、後続のロジックでも使用できることがよくあります。
ステータスをクエリした後のこの種の検証ロジックは、コードがオンラインになって以来、ステータスを含むすべてのビジネス ロジック処理に常に存在しており、不可欠です。ただし、重複防止処理の効果は良好ではありません。フロントエンドに重複防止送信を追加する前は、平均数が 1 週間あたり 25 件でしたが、フロントエンドの最適化後は、平均数が 1 週間あたり 7 件に減少しました。この数字は払い戻し申請総数の 3% を占めており、この割合は依然として受け入れがたいものです。
理論的には、データのステータスが更新される前にリクエストがクエリ操作を完了する限り、ビジネス ロジックの処理が繰り返し発生します。以下に示すように。最適化の方向性は、クエリと更新の間のビジネス処理時間を短縮することです。これにより、ギャップ期間による同時実行性の影響を軽減できます。極端な場合、クエリと更新がアトミック操作になれば、現在の問題は存在しなくなります。
Redis ストレージ クエリは軽量かつ高速です。リクエストが到着すると、最初にキャッシュに記録できます。後続の受信リクエストは毎回検証されます。プロセス全体が完了し、キャッシュがクリアされます。払い戻しを例に挙げます:
1) の発行と比較して、データベースは次のキャッシュに置き換えられます。より速い応答。しかし、まだアトミックな操作ではありません。キャッシュの挿入と読み取りの間には、まだ時間間隔があります。極端な場合には、依然として操作が繰り返される場合があります。この方法が最適化された後、この操作は週に 1 回繰り返されます。
はアトミックな操作が必要なので、データベースの一意のインデックスを考えました。新しい TradeLock テーブルを作成します:
CREATE TABLE `TradeLock` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `type` int(11) NOT NULL COMMENT '锁类型', `lockId` int(11) NOT NULL DEFAULT '0' COMMENT '业务ID', `status` int(11) NOT NULL DEFAULT '0' COMMENT '锁状态', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Trade锁机制';
# リクエストが受信されるたびに、データがテーブルに挿入されます:
成功,则可以继续操作(相当于获取锁); 失败,则说明有操作在进行。
# 操作が完了したら、このレコードを削除します。 (ロックを解除することに相当)。
現在オンラインになっており、来週のデータ統計を待っています。
データベース操作は比較的高いパフォーマンスを消費するため、redis カウンタもアトミック操作であることがわかりました。カウンターを思い切って使いましょう。パフォーマンスが向上するだけでなく、ストレージの必要性がなくなり、ピーク QPS も向上します。
注文の払い戻しを例に挙げてみましょう:
# リクエストが受信されるたびに、キーとして orderId を持つ新しいカウンターが作成され、+1 されます。
如果>1(不能获得锁): 说明有操作在进行,删除。 如果=1(获得锁): 可以操作。
# 操作の終了 (ロックの削除): このカウンタを削除します。
rreeeePHP语言自身没有提供进程互斥和锁定机制。因此才有了我们上面的尝试。网上也有文件锁机制,但是考虑到我们的分布式部署,建议还是用缓存。在大并发的情况下,程序各种情况的发生。特别是涉及到金额操作,不能有一分一毫的差距。所以在大并发要互斥的情况下可以考虑3、4两种方案。
爱迪生尝试了1600多种材料选择了钨丝发明了灯泡,实践出真知。遇到问题,和问题斗争,最后解决问题是一个最大提升自我的过程,不但加宽自己的知识广度,更加深了自己的技能深度。达到目标之后的成就感更是不言而喻。
以上が注文の繰り返し提出を防ぐ戦略と方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。