インタフェースの重複要求を防止する方法
2828 ワード
インタフェースの重複要求を防止する方法
に質問
最近、インタフェースの重複要求によるデータ異常のバグに遭遇しました.通知モジュールで、先生が保護者に通知を送信し、保護者が通知を開いた後、クライアントはackインタフェースを呼び出してサーバーに通知を読み取り、notify_readテーブルに保護者の情報を挿入し、内容は多分(notify_id,member_id)で、通知テーブルnotifyのread_numフィールド+1.ネットワークの問題が発生すると、クライアントはインタフェースを複数回要求します.
コンカレント要求が発生した場合、1つの要求がinsertNotifyに実行される場合、別の要求もinsertNotifyに実行されます.incrementNotifyReadNumメソッドが2回実行されると、データがエラーになります.
ソリューション
デルのサービスは分散型であるため、問題を解決する際に2つのリクエストが異なる2つのサーバに落ちている場合を考慮する必要があります.当時、この問題を解決する2つの案があると思っていた.
データベースの使用
新しい中間表を作成
各リクエストは、ビジネス処理を行う前にテーブルにレコードを挿入し、リクエストが完了するとレコードを削除します.
データベース内の一意のキーを使用して、同じ時点でrequestテーブルを正常に挿入できるリクエストが1つしかないことを保証すると、挿入に失敗したリクエストはビジネス処理されません.シナリオは簡単ですが、リクエスト量が大きいとDBの性能が損なわれ、拡張が容易ではありません.
Redisのcounterの使用
Redisを使用することとデータベースを使用することの実装プロセスは、実際にはそれほど悪くありません.要求のたびにkeyに対してsetnx操作を行い、setnx操作が1を返すと、現在の要求だけがこの業務を処理していることを示す.setnxオペレーションが0に等しいことを返すと、このビジネスを処理する他の要求があることを示します.
**SETNX key value **
キーの値をvalueに設定し、キーが存在しない場合にのみ使用します.与えられたkeyが既に存在する場合、SETNXは何もしない.SETNXは『SET if Not eXists』(存在しなければSET)の略である.時間複雑度:O(1)戻り値:設定に成功し、1を返します.設定に失敗し、0を返します.
Redisのsetnxコマンドは、インタフェースの重複要求を防止するために、分散ロックを実装するためによく使用されます.Redisの実装は、データベースの実装に比べてパフォーマンスが向上し、拡張が容易です.
転載先:https://juejin.im/post/5b1e336d51882513bb1c637a
に質問
最近、インタフェースの重複要求によるデータ異常のバグに遭遇しました.通知モジュールで、先生が保護者に通知を送信し、保護者が通知を開いた後、クライアントはackインタフェースを呼び出してサーバーに通知を読み取り、notify_readテーブルに保護者の情報を挿入し、内容は多分(notify_id,member_id)で、通知テーブルnotifyのread_numフィールド+1.ネットワークの問題が発生すると、クライアントはインタフェースを複数回要求します.
int count = queryRead(notifyId, memberId);
if( count == 0){
insertNotify(notifyId, memberId);
incrementNotifyReadNum(notify, memberId);
}
コンカレント要求が発生した場合、1つの要求がinsertNotifyに実行される場合、別の要求もinsertNotifyに実行されます.incrementNotifyReadNumメソッドが2回実行されると、データがエラーになります.
ソリューション
デルのサービスは分散型であるため、問題を解決する際に2つのリクエストが異なる2つのサーバに落ちている場合を考慮する必要があります.当時、この問題を解決する2つの案があると思っていた.
データベースの使用
新しい中間表を作成
CREATE TABLE request(rkey varchar(32)PRIMARY KEY (`rkey`))
各リクエストは、ビジネス処理を行う前にテーブルにレコードを挿入し、リクエストが完了するとレコードを削除します.
try{
insertRequest(String.format("NOTIFY_%s_%s", notify, memberId))
int count = queryRead(notifyId, memberId);
if( count == 0){
insertNotify(notifyId, memberId);
incrementNotifyReadNum(notify, memberId);
}
}catch(Exception e){
//doSomething();
}finally{
deleteRequest(String.format("NOTIFY_%s_%s", notify, memberId));
}
データベース内の一意のキーを使用して、同じ時点でrequestテーブルを正常に挿入できるリクエストが1つしかないことを保証すると、挿入に失敗したリクエストはビジネス処理されません.シナリオは簡単ですが、リクエスト量が大きいとDBの性能が損なわれ、拡張が容易ではありません.
Redisのcounterの使用
Redisを使用することとデータベースを使用することの実装プロセスは、実際にはそれほど悪くありません.要求のたびにkeyに対してsetnx操作を行い、setnx操作が1を返すと、現在の要求だけがこの業務を処理していることを示す.setnxオペレーションが0に等しいことを返すと、このビジネスを処理する他の要求があることを示します.
**SETNX key value **
キーの値をvalueに設定し、キーが存在しない場合にのみ使用します.与えられたkeyが既に存在する場合、SETNXは何もしない.SETNXは『SET if Not eXists』(存在しなければSET)の略である.時間複雑度:O(1)戻り値:設定に成功し、1を返します.設定に失敗し、0を返します.
String key = String.format("NOTIFY_%s_%s", notify, memberId);
try{
int requestCount = redisServer.setnx(key);
if(requestCount == 1){
int count = queryRead(notifyId, memberId);
if( count == 0){
insertNotify(notifyId, memberId);
incrementNotifyReadNum(notify, memberId);
}
}
}catch(Exception e){
//doSomething();
}finally{
//
redisServer.delete(key);
}
Redisのsetnxコマンドは、インタフェースの重複要求を防止するために、分散ロックを実装するためによく使用されます.Redisの実装は、データベースの実装に比べてパフォーマンスが向上し、拡張が容易です.
転載先:https://juejin.im/post/5b1e336d51882513bb1c637a