TiDB のベスト プラクティス

このドキュメントでは、SQL の使用や、オンライン分析処理 (OLAP) およびオンライン トランザクション処理 (OLTP) シナリオの最適化のヒント、特に TiDB に固有の最適化オプションなど、TiDB の使用のベスト プラクティスを要約します。

このドキュメントを読む前に、TiDB の技術原則を紹介する 3 つのブログ投稿を読むことをお勧めします。

序文

データベースは汎用インフラストラクチャ システムです。開発プロセスではさまざまなユーザーシナリオを考慮し、特定のビジネスシナリオの実際の状況に応じてデータパラメータや使用方法を変更することが重要です。

TiDB は、MySQL プロトコルおよび構文と互換性のある分散データベースです。ただし、分散storageとトランザクションの内部実装とサポートにより、TiDB の使用方法は MySQL とは異なります。

基本概念

ベスト プラクティスは、その実装原則と密接に関連しています。 Raftコンセンサス アルゴリズム、分散トランザクション、データ シャーディング、負荷分散、SQL から Key-Value (KV) へのマッピング ソリューション、セカンダリ インデックスの実装方法、分散実行など、基本的なメカニズムのいくつかを学習することをお勧めします。エンジン。

このセクションでは、これらの概念について説明します。詳細については、 PingCAP のブログ投稿を参照してください。

Raft

Raft は、強い整合性を備えたデータ レプリケーションを保証するコンセンサス アルゴリズムです。最レイヤーでは、TiDB はRaftを使用してデータを複製します。 TiDB は、成功の結果を返す前に、大部分のレプリカにデータを書き込みます。このようにして、いくつかのレプリカが失われる可能性があっても、システムには依然として最新のデータが保持されます。たとえば、レプリカが 3 つある場合、システムはデータが 2 つのレプリカに書き込まれるまで成功の結果を返しません。レプリカが失われた場合、残りの 2 つのレプリカのうち少なくとも 1 つは最新のデータを保持します。

3 つのレプリカを保存するには、Source-Replica のレプリケーションと比較して、 Raftの方が効率的です。 Raftの書き込みレイテンシーは、最も遅いレプリカではなく、2 つの最も速いレプリカに依存します。したがって、 Raftレプリケーションを使用することで、地理的に分散された複数のアクティブなデータセンターの実装が可能になります。 3 つのデータ センターが 2 つのサイトに分散している一般的なシナリオでは、データの一貫性を保証するために、TiDB は 3 つのデータ センターすべてにデータを書き込むのではなく、ローカル データ センターとより近いデータ センターにデータを正常に書き込むだけで済みます。ただし、これは、クロスデータセンター展開がどのようなシナリオでも実装できることを意味するものではありません。書き込まれるデータの量が多い場合、データセンター間の帯域幅とレイテンシーが重要な要素になります。書き込み速度が帯域幅を超えているか、レイテンシーが高すぎる場合、 Raftレプリケーション メカニズムは依然として適切に機能しません。

分散トランザクション

TiDB は完全な分散トランザクションを提供し、モデルにはGoogle パーコレーターに基づいていくつかの最適化が施されています。このドキュメントでは、次の機能を紹介します。

  • 楽観的なトランザクション モデル

    TiDB の楽観的トランザクション モデルは、コミット フェーズまで競合を検出しません。競合がある場合は、トランザクションを再試行する必要があります。ただし、競合が深刻な場合、このモデルは非効率的です。再試行前の操作は無効であり、繰り返す必要があるためです。

    データベースがカウンターとして使用されると仮定します。アクセスの同時実行性が高いと、深刻な競合が発生し、複数回の再試行やタイムアウトが発生する可能性があります。したがって、深刻な競合が発生するシナリオでは、悲観的トランザクション モードを使用するか、Redis にカウンターを配置するなど、システムアーキテクチャレベルで問題を解決することをお勧めします。それにもかかわらず、アクセス競合がそれほど深刻でない場合、楽観的トランザクション モデルは効率的です。

  • 悲観的トランザクション モード

    TiDB では、悲観的トランザクション モードは MySQL とほぼ同じ動作をします。トランザクションは実行フェーズ中にロックを適用します。これにより、競合状況での再試行が回避され、より高い成功率が保証されます。悲観的ロックを適用すると、 SELECT FOR UPDATEを使用して事前にデータをロックすることもできます。

    ただし、アプリケーション シナリオの競合が少ない場合は、楽観的トランザクション モデルの方がパフォーマンスが高くなります。

  • トランザクションサイズの制限

    分散トランザクションは 2 フェーズ コミットを実行する必要があり、最レイヤーでRaftレプリケーションを実行するため、トランザクションが非常に大きい場合、コミット プロセスが非常に遅くなり、後続のRaftレプリケーション プロセスが停止します。この問題を回避するために、トランザクション サイズが制限されます。

    • トランザクションは 5,000 SQL ステートメントに制限されています (デフォルト)
    • 各 Key-Value エントリは 6 MB 以下です (デフォルト)
    • Key-Value エントリの合計サイズは 10 GB を超えません。

    同様の制限はGoogleクラウドスパナーにもあります。

データシャーディング

TiKV は、キーの範囲に従って最下層のデータを自動的にシャーディングします。各リージョンはキーの範囲であり、左が閉じて右が開いた間隔[StartKey, EndKey)です。リージョン内のキーと値のペアの量が特定の値を超えると、リージョンは自動的に 2 つに分割されます。

負荷分散

配置Driver(PD) は、TiKV クラスター全体のステータスに応じてクラスターの負荷のバランスをとります。スケジューリングの単位はリージョンであり、ロジックは PD によって設定されるストラテジです。

KV 上の SQL

TiDB は、SQL 構造を Key-Value 構造に自動的にマッピングします。詳細はTiDB 内部 (II) - コンピューティングを参照してください。

簡単に言うと、TiDB は次の操作を実行します。

  • データの行は Key-Value ペアにマッピングされます。キーには接頭辞としてTableIDが付き、接尾辞として行 ID が付きます。
  • インデックスは Key-Value ペアとしてマップされます。キーには接頭辞としてTableID+IndexIDが付けられ、接尾辞としてインデックス値が付きます。

同じテーブル内のデータまたはインデックスには同じプレフィックスが付きます。これらの Key-Value は、TiKV のキー空間内の隣接する位置にあります。したがって、書き込まれるデータの量が多く、すべてが 1 つのテーブルに書き込まれると、書き込みホットスポットが作成されます。連続的に書き込まれるデータの一部のインデックス値も連続的である場合 (たとえば、 update timeなど、時間とともに増加するフィールド)、状況はさらに悪化します。これにより、いくつかの書き込みホットスポットが作成され、システム全体のボトルネックになります。

同様に、すべてのデータが集中した狭い範囲 (たとえば、連続した数万行または数十万行のデータ) から読み取られる場合、データのアクセス ホットスポットが発生する可能性があります。

セカンダリインデックス

TiDB は、グローバル インデックスでもある完全なセカンダリ インデックスをサポートします。多くのクエリはインデックスによって最適化できます。したがって、アプリケーションではセカンダリ インデックスを有効に活用することが重要です。

MySQL の豊富な経験は TiDB にも応用できます。 TiDB には独自の機能があることに注意してください。 TiDB でセカンダリ インデックスを使用する場合の注意事項をいくつか示します。

  • セカンダリインデックスは多ければ多いほど良いのでしょうか?

    セカンダリ インデックスを使用するとクエリを高速化できますが、インデックスを追加すると副作用が生じます。前のセクションでは、インデックスのstorageモデルを紹介しました。追加のインデックスごとに、行を挿入するときに Key-Value が 1 つ増えます。したがって、インデックスの数が増えると、書き込み速度が遅くなり、より多くのスペースを消費します。

    さらに、インデックスが多すぎるとオプティマイザーの実行時間に影響し、不適切なインデックスはオプティマイザーに誤解を与えます。したがって、セカンダリ インデックスが多いほどパフォーマンスが向上するわけではありません。

  • どの列にインデックスを作成する必要がありますか?

    前述したようにインデックスは重要ですが、インデックスの数は適切である必要があります。アプリケーションの特性に応じて、適切なインデックスを作成する必要があります。原則として、パフォーマンスを向上させるには、クエリに含まれる列にインデックスを作成する必要があります。インデックスを作成する必要がある状況は次のとおりです。

    • 高度に差別化された列の場合、フィルター処理された行はインデックスによって大幅に削減されます。
    • 複数のクエリ条件がある場合は、複合インデックスを選択できます。同等の条件を持つ列を複合インデックスの前に置くことに注意してください。

    たとえば、よく使用されるクエリがselect * from t where c1 = 10 and c2 = 100 and c3 > 10の場合、複合インデックスIndex cidx (c1, c2, c3)を作成できます。このように、クエリ条件を使用してインデックス プレフィックスを作成し、スキャンすることができます。

  • インデックスを使用したクエリとテーブルの直接スキャンの違い

    TiDB はグローバル インデックスを実装しているため、テーブルのインデックスとデータは必ずしも同じデータ シャーディング上にあるとは限りません。インデックスを介してクエリを実行する場合は、まずインデックスをスキャンして対応する行 ID を取得し、次にその行 ID を使用してデータを取得する必要があります。したがって、この方法には 2 つのネットワーク要求が含まれ、一定のパフォーマンスのオーバーヘッドが発生します。

    クエリに多数の行が含まれる場合、インデックスのスキャンは同時に進行します。結果の最初のバッチが返されると、テーブルのデータの取得を続行できます。したがって、これは並列 + パイプライン モデルです。 2 つのアクセスによりオーバーヘッドが発生しますが、レイテンシーは大きくありません。

    次の 2 つの条件では、2 つのアクセスの問題は発生しません。

    • インデックスの列はすでにクエリ要件を満たしています。 tテーブルのc列にインデックスがあり、クエリがselect c from t where c > 10;であると仮定します。このとき、インデックスにアクセスすると必要なデータがすべて取得できます。この状況をCovering Indexと呼びます。ただし、クエリのパフォーマンスを重視する場合は、フィルターする必要はないがクエリ結果で返す必要がある列の一部をインデックスに入れて、複合インデックスを作成できます。 select c1, c2 from t where c1 > 10;例に挙げます。複合インデックスIndex c12 (c1, c2)を作成することで、このクエリを最適化できます。

    • テーブルの主キーは整数です。この場合、TiDB は主キーの値を行 ID として使用します。したがって、クエリ条件が主キーにある場合は、行 ID の範囲を直接構築し、テーブル データをスキャンして結果を取得できます。

  • クエリの同時実行性

    データは多くのリージョンに分散されるため、クエリは TiDB で同時に実行されます。ただし、システム リソースを大量に消費する場合を考慮して、デフォルトの同時実行性は高くありません。さらに、OLTP クエリには通常、大量のデータが含まれないため、同時実行性が低くても十分です。ただし、OLAP クエリの場合は同時実行性が高く、TiDB は次のシステム変数を通じてクエリの同時実行性を変更します。

    • tidb_distsql_scan_concurrency :

      テーブルおよびインデックス データのスキャンを含む、データのスキャンの同時実行性。

    • tidb_index_lookup_size :

      テーブル データにアクセスする前にインデックスにアクセスして行 ID を取得する必要がある場合、行 ID のバッチを単一のリクエストとして使用してテーブル データにアクセスします。このパラメータはバッチのサイズを設定します。バッチが大きくなるとレイテンシーが増加しますが、バッチが小さくなるとクエリが増加する可能性があります。このパラメータの適切なサイズは、クエリに含まれるデータの量に関係します。通常、変更は必要ありません。

    • tidb_index_lookup_concurrency :

      テーブル データにアクセスする前にインデックスにアクセスして行 ID を取得する必要がある場合、毎回行 ID を介してデータを取得する同時実行性は、このパラメーターによって変更されます。

  • インデックスを使用して結果の順序を保証する

    インデックスを使用してデータをフィルタリングまたは並べ替えることができます。まず、インデックスの順序に従って行 ID を取得します。次に、行 ID の返される順序に従って、行の内容を返します。このようにして、返された結果はインデックス列に従って順序付けされます。インデックスのスキャンと行の取得のモデルが並列 + パイプラインであることは前述しました。インデックスの順序に従って行が返される場合、2 つのクエリ間の同時実行性が高くてもレイテンシーは短縮されません。したがって、デフォルトでは同時実行性は低くなりますが、 tidb_index_serial_scan_concurrency変数を使用して変更できます。

  • 逆インデックススキャン

    TiDB は、通常のスキャンより 20% 遅い速度での逆順の昇順インデックスのスキャンをサポートしています。データが頻繁に変更され、存在するバージョンが多すぎる場合、パフォーマンスのオーバーヘッドが高くなる可能性があります。逆インデックス スキャンはできるだけ避けることをお勧めします。

シナリオと実践

最後のセクションでは、TiDB のいくつかの基本的な実装メカニズムと、その使用法への影響について説明しました。このセクションでは、展開からアプリケーションの使用まで、具体的な使用シナリオと運用方法を紹介します。

導入

導入の前に、 ソフトウェアとハ​​ードウェアの要件をお読みください。

TiUPを使用して TiDB クラスターをデプロイすることをお勧めします。このツールはクラスター全体をデプロイ、停止、破棄、アップグレードできるため、非常に便利です。 TiDB クラスターを手動でデプロイすることはお勧めできません。後で保守したりアップグレードしたりするのが面倒になる可能性があります。

データインポート

インポート プロセス中の書き込みパフォーマンスを向上させるために、 TiKV メモリ パラメータのパフォーマンスを調整するで説明したように TiKV のパラメーターを調整できます。

書く

前述したように、TiDB は Key-Valueレイヤーの単一トランザクションのサイズを制限します。 SQLレイヤーの場合と同様に、データ行は Key-Value エントリにマップされます。インデックスを追加するたびに、Key-Value エントリが 1 つ追加されます。

注記:

トランザクションのサイズ制限を設定するときは、TiDB エンコードのオーバーヘッドと追加のトランザクション キーを考慮する必要があります。各トランザクションの行数は 200 未満、1 行のデータ サイズは 100 KB 未満にすることをお勧めします。そうしないとパフォーマンスが悪くなります。

ステートメントがINSERTUPDATE 、またはDELETEステートメントであっても、ステートメントをバッチに分割するか、ステートメントに制限を追加することをお勧めします。

大量のデータを削除する場合は、 Delete from t where xx limit 5000;を使用することをお勧めします。ループを通じて削除し、ループを終了する条件としてAffected Rows == 0を使用します。

一度に削除する必要があるデータの量が多い場合、各削除が逆方向に実行されるため、このループ方法はますます遅くなります。以前のデータを削除した後、多数の削除済みフラグが短期間残り (その後、ガベージ コレクションによってすべてクリアされます)、次のDELETEステートメントに影響を与えます。可能であれば、 WHEREの条件を絞り込むことをお勧めします。 2017-05-26上のすべてのデータを削除する必要があると仮定すると、次のステートメントを使用できます。

for i from 0 to 23: while affected_rows > 0: delete from t where insert_time >= i:00:00 and insert_time < (i+1):00:00 limit 5000; affected_rows = select affected_rows()

この疑似コードは、前のDeleteのステートメントが後のステートメントに影響を与えないように、巨大なデータのチャンクを小さなチャンクに分割して削除することを意味します。

クエリ

クエリ要件と特定のステートメントについては、 システム変数を参照してください。

SETステートメントを通じて SQL 実行の同時実行性を制御し、ヒントを通じてJoin演算子の選択を制御できます。

さらに、MySQL の標準インデックス選択、ヒント構文を使用したり、 Use Index / Ignore Index hintまでのインデックスを選択するようにオプティマイザを制御したりすることもできます。

アプリケーション シナリオに OLTP と OLAP の両方のワークロードがある場合は、OLTP リクエストと OLAP リクエストを異なる TiDB サーバーに送信して、OLTP に対する OLAP の影響を軽減できます。 OLAP ワークロードを処理する TiDBサーバーには、高性能ハードウェア (たとえば、より多くのプロセッサ コアやより大きなメモリ) を備えたマシンを使用することをお勧めします。

OLTP および OLAP ワークロードを完全に分離するには、 TiFlashで OLAP アプリケーションを実行することをお勧めします。 TiFlashは、OLAP ワークロードで優れたパフォーマンスを発揮するカラムナ型storageエンジンです。 TiFlash はstorageレイヤーで物理的な分離を実現し、一貫した読み取りを保証します。

監視とログ

監視メトリクスは、システムのステータスを知るための最良の方法です。 TiDB クラスターと一緒にモニタリング システムを展開することをお勧めします。

TiDB はグラファナ + プロメテウスを使用してシステムのステータスを監視します。 TiUPを使用して TiDB を展開すると、監視システムが自動的に展開および構成されます。

監視システムには多くの項目があり、その大部分は TiDB 開発者向けです。ソース コードに関する深い知識がなくても、これらの項目を理解する必要はありません。アプリケーションまたはシステム キー コンポーネントの状態に関連するいくつかの項目が選択され、ユーザー用の別のoverviewパネルに配置されます。

監視に加えて、システム ログを表示することもできます。 TiDB の 3 つのコンポーネント、tidb-server、tikv-server、および pd-server には、それぞれ--log-fileパラメーターがあります。クラスターの起動時にこのパラメーターが設定されている場合、ログはパラメーターで設定されたファイルに保存され、ログ ファイルは毎日自動的にアーカイブされます。 --log-fileパラメータが設定されていない場合、ログはstderrに出力されます。

TiDB 4.0 以降、TiDB は使いやすさを向上させるためにTiDB ダッシュボード UI を提供します。ブラウザでhttp://PDIP:{PD_IP}:{PD_PORT}/ダッシュボードアクセスすると、TiDB ダッシュボードにアクセスできます。 TiDB ダッシュボードは、クラスターのステータスの表示、パフォーマンス分析、トラフィックの視覚化、クラスターの診断、ログ検索などの機能を提供します。

ドキュメンテーション

システムについて学び、問題を解決する最善の方法は、そのドキュメントを読み、その実装原理を理解することです。

TiDB には中国語と英語の両方で多数の公式文書があります。問題が発生した場合は、 FAQTiDBクラスタのトラブルシューティング ガイドから始めることができます。 GitHub 上の TiDB リポジトリで問題リストを検索したり、問題を作成したりすることもできます。

TiDB には便利な移行ツールも多数あります。詳細については移行ツールの概要を参照してください。

TiDB の技術的な詳細に関するその他の記事については、 PingCAP公式ブログサイトを参照してください。

TiDB に最適なシナリオ

TiDB は次のシナリオに適しています。

  • データ量がスタンドアロン データベースには大きすぎます
  • シャーディングをしたくない場合
  • アクセス モードに明らかなホットスポットがない
  • トランザクション、強整合性、災害復旧が必要です
  • リアルタイムのハイブリッドトランザクション/分析処理 (HTAP) 分析を利用して、storageリンクを削減したいと考えています。

このページは役に立ちましたか?