サーバーインターフェースの最適化スキーム

1. 背景

昨年は古いプロジェクトについてコスト削減と効率向上のために多くのことを行いましたが、その中で最も時間がかかるインターフェースの問題が判明したため、インターフェースのパフォーマンスの最適化に重点を置きました。この記事では、インターフェイスを最適化するための一般的なソリューションを紹介します。

2. インターフェース最適化スキームの概要

1. バッチ処理

バッチ思考: データベースのバッチ操作が理解しやすく、ループ挿入シナリオのインターフェースでは、バッチ実行完了後にデータベースを一度に挿入または更新することができ、複数の IO を回避できます。

//for循环单笔入库
list.stream().forEatch(msg->{
    insert();
});
//批量入库
batchInsert();

2. 非同期処理

非同期の考え方: 時間がかかり、結果に必要のないロジックについては、時間のかかるインターフェイスを削減できる非同期実行を検討できます。

たとえば、財務管理のための購入インターフェイス、アカウント入力購入書類の作成は同期して実行されます。これは T+1 トランザクションであるため、後者の 2 つのロジックは結果には必要なく、注意を払う必要はありません。結果がリアルタイムに表示されるため、会計処理購買伝票の作成が非同期処理に変更されると考えられます。写真が示すように:

非同期実装に関しては、スレッド プール、メッセージ キュー、または一部のスケジューリング タスク フレームワークを使用できます。

3. 時間のためのスペース

スペースと時間の交換のよく知られた例は、キャッシュの合理的な使用です。頻繁に使用され、あまり変更されない一部のデータについては、事前にキャッシュしておき、必要に応じてキャッシュ内で直接チェックすることで、頻繁なデータベース クエリや計算の繰り返しを避けることができます。 。

注意したいのは、ここで合理的という言葉を使っているのは、空間と時間の交換は諸刃の剣でもあるため、利用シーンを総合的に考慮する必要があるためで、結局のところ、キャッシュによるデータの整合性の問題もかなり深刻です。頭痛。

ここでのキャッシュには、R2M、ローカル キャッシュ、memcached、または Map を使用できます。

ストック ツールのクエリの例を次に示します。

ストラテジーローテーションのリバランス情報は週に一度しか更新されないため、元のリバランスインターフェイスでデータベースをチェックするロジックは不合理であり、リバランス情報を取得した後、最終的にバックテスト収入とデータを取得するために複雑な計算を経る必要があります。上海指数や深セン指数を上回るパフォーマンスを目指します。データベースの検索操作と計算結果をキャッシュに保存すると、実行時間を大幅に節約できます。図に示すように:

4. 前処理

つまり、プリフェッチの考え方は、クエリされたデータを事前に計算し、それらをキャッシュまたはテーブル内のフィールドに入れることであり、これを使用するとインターフェイスのパフォーマンスが大幅に向上します。上の例と似ていますが、焦点が異なります。

簡単な例を挙げると、ウェルスマネジメント商品では、正味価値に基づいて年換算収益率を計算するためのデータ表示要件があり、正味価値を使用して年換算収益率の計算式の計算ロジックを適用できます。前処理を使用できるため、各インターフェイス呼び出しが対応するフィールドを直接フェッチしても問題ありません。

5. プール思考

私たちはデータベース接続プールやスレッド プールなどを使用したことがあります。これはプールのアイデアを具体化したものです。これらが解決する問題は、オブジェクトや接続の繰り返しの作成を回避し、再利用して不必要な損失を回避することです。破壊にも時間がかかります。

プーリングの考え方には上記 2 つが含まれますが、これらに限定されません。一般的に言えば、プーリングの考え方の本質は **事前割り当てとリサイクルです。この原理を理解した上で、いくつかのビジネス シナリオを実行する場合でも、利用される。

例: オブジェクト プール

6. シリアルからパラレルへ

シリアルとは、現在の実行ロジックが実行される前に、前の実行ロジックが実行されるまで待機する必要があることを意味します。並列とは、2 つの実行ロジックが互いに干渉しないことを意味し、並列処理は比較的時間を節約します。結果パラメータに依存しないことを前提としています。

例えば、財務管理のポジション情報表示インターフェースの場合、ポジションページを表示するにはユーザーのアカウント情報だけでなく、商品情報やバナービット情報もクエリする必要があり、シリアルだと非常に時間がかかります。基本的には累積的です。並列であれば、インターフェース時間は大幅に短縮されます。

図に示すように:

7. 索引

インデックスを追加すると、データ クエリの効率が大幅に向上します。これはインターフェイスの設計でも考慮されます。ここでは詳細は説明しません。要件を繰り返す中で、インデックスが機能しないいくつかのシナリオを整理することに重点を置きます。有効になります。友達に役立つことを願っています。助けてください。

具体的にどのようなシナリオが成立しないのかについては、一つ一つ挙げることはしませんので、後ほど時間があれば別途整理させていただきます。

8. 大きな取引を避ける

いわゆるビッグ トランザクションの問題とは、実行時間が長いトランザクションのことを指します。トランザクションが一貫して送信されないと、データベース接続が占有され、データベースへの他のアクセス要求に影響を与え、他のインターフェイスのパフォーマンスに影響を与えます。

例えば:

@Transactional(value ="taskTransactionManager", propagation =Propagation.REQUIRED, isolation =Isolation.READ_COMMITTED, rollbackFor ={RuntimeException.class,Exception.class})
    publicBasicResultpurchaseRequest(PurchaseRecordrecord){
        BasicResult result =newBasicResult();
        //插入账户任务
        taskMapper.insert(ManagerParamUtil.buildTask(record,TaskEnum.Task_type.pension_account.type(),TaskEnum.Account_bizType.purchase_request.type()));
        //插入同步任务
        taskMapper.insert(ManagerParamUtil.buildTask(record,TaskEnum.Task_type.pension_sync.type(),TaskEnum.Sync_bizType.purchase.type()));
        //插入影像件上传任务
        taskMapper.insert(ManagerParamUtil.buildTask(record,TaskEnum.Task_type.pension_sync.type(),TaskEnum.Sync_bizType.cert.type()));
        result.setInfo(ResultInfoEnum.SUCCESS);
        return result;
    }

上記のコードは主に購入申請完了後の一連の動作を行うもので、新規購入が完了した場合にはプッシュを送信してユーザーのニーズを通知します。次の図に示すように、後で直接追加する可能性が非常に高くなります: RPC 呼び出しはトランザクション、つまり非 DB 操作にネストされています。これらの非 DB 操作に時間がかかると、大きなトランザクションの問題が発生する可能性があります。 。ビッグデータによって引き起こされる問題には、主にデッドロック、インターフェイスのタイムアウト、マスターとスレーブの遅延などが含まれます。

@Transactional(value ="taskTransactionManager", propagation =Propagation.REQUIRED, isolation =Isolation.READ_COMMITTED, rollbackFor ={RuntimeException.class,Exception.class})
    publicBasicResultpurchaseRequest(PurchaseRecordrecord){
        BasicResult result =newBasicResult();
        ...
        pushRpc.doPush(record);
        result.setInfo(ResultInfoEnum.SUCCESS);
        return result;
    }

したがって、大規模なトランザクションの問題を回避するには、次のスキームを通じて問題を回避できます。

1. RPC 呼び出しはトランザクション内に配置されません。

2. クエリ操作は可能な限りトランザクションの外側に配置する必要があります。

3. トランザクションで大量のデータを処理しないようにする

9. プログラム構造の最適化

プログラム構造の問題は通常、要件を複数回繰り返すと発生し、コードが重ね合わされます。クエリの繰り返しやオブジェクトの複数の作成など、時間のかかる問題が発生します。複数の人がプロジェクトを保守する場合によく発生します。解決するのも比較的簡単で、インターフェース全体をリファクタリングし、各コードブロックの機能と目的を評価し、実行順序を調整する必要があります。

10. 深刻なページネーションの問題

ディープ ページングの問題は比較的一般的です。一般に、ページングで最初に考えるのは制限です。なぜ遅いのですか? 次の SQL を見てみましょう。

select*from purchase_record where productCode ='PA9044'andstatus=4orderby orderTime desclimit100000,200

制限 100000,200 は、100200 行がスキャンされ、その後 200 行が返され、最初の 100000 行が破棄されることを意味します。したがって、実行は遅いです。一般に、ラベル記録方法は次のような最適化に使用できます。

select*from purchase_record where productCode ='PA9044'andstatus=4and id >100000limit200

この最適化の利点は、ページ数に関係なく主キー インデックスにヒットすることです。パフォーマンスは悪くありませんが、連続的な自己インクリメント フィールドが必要であるという制限があります。

11. SQLの最適化

SQL の最適化により、インターフェイスのクエリ パフォーマンスが大幅に向上します。この記事ではインターフェイスの最適化スキームに焦点を当てているため、特定の SQL 最適化を 1 つずつリストすることはしません。インデックス作成、ページング、その他の懸念事項と組み合わせて最適化スキームを検討してください。

12. ロックの粒度が粗すぎることを避ける

ロックは通常、同時実行性の高いシナリオで共有リソースを保護するために使用されますが、ロックの粒度が粗すぎると、インターフェイスのパフォーマンスに大きな影響を与えます。

ロックの粒度について: ロックするロックの範囲の大きさを意味します。同期ロックであっても、Redis 分散ロックであっても、重要なリソースのみをロックする必要があります。共有リソースが関与しない場合は、ロックする必要はありません。トイレに行きたいのと同じで、バスルームのドアをロックするだけでよく、リビングルームのドアをロックする必要はありません。

間違ったロック方法:

//非共享资源
privatevoidnotShare(){
}
//共享资源
privatevoidshare(){
}
privateintwrong(){
    synchronized(this){
      share();
      notShare();
    }
}

正しいロック方法:

//非共享资源
privatevoidnotShare(){
}
//共享资源
privatevoidshare(){
}
privateintright(){
    notShare();
    synchronized(this){
    share();
 }
}

3つ目、ついに

多くのインターフェースの効率性の問題は一朝一夕に形成されるものではないと考えており、デマンドのイテレーションの過程では、デマンドを迅速に起動するために、コードを直接蓄積して機能を実現する方法が採用されており、これにより上記のインターフェースが発生します。パフォーマンスの問題。

考え方を変え、より高いレベルで考え、インターフェイス設計者の観点から要件を開発することで、これらの問題の多くを回避でき、コストを削減し効率を高める効果的な方法でもあります。

ロールアップ、テクノロジーには限界がありません!

おすすめ

転載: blog.csdn.net/baidu_38493460/article/details/130503292