TiDB User Day 2024のアーカイブ動画を公開中!詳細を見る
best-practices-for-tidb-load-balancing.png

※このブログは2021年11月02日に公開された英語ブログ「Best Practices for TiDB Load Balancing」の拙訳です。

著者: Daniël van Eeden (PingCAPテクニカルサポートエンジニア)

編集者: Ran Huang, Tom Dewan

負荷分散とはアプリケーションからTiDBサーバーのインスタンスへの接続を分散させることです。 これは、複数のマシンに負荷を分散するのに役立ち、負荷分散オプションに応じて、TiDBインスタンスが使用できなくなった場合に接続を自動的に再ルーティングできます。

負荷分散の種類

負荷分散を実装するにはさまざまな方法があります。このセクションでは、最も一般的な種類について説明します。

 負荷分散の種類  使用シナリオ  利点  欠点
 MySQL Connector/Jや MySQL Connector/C++ などのコネクタベース  特定のアプリケーションの負荷分散  余分なコンポーネント必要無し  コネクタ固有の実装と動作
 DNS (ラウンドロビンもしくはSRV)  アプリケーションの変更を必要としない単純な負荷分散  既存のコンポーネントとメソッドを再利用  障害が発生したノードをサービスから自動的に削除しない
 Kubernetesロードバランサー (metallbやAmazon Elastic Load Balancing など)  クラウドでの負荷分散  Kubernetesとの適切な統合  Kubernetesが必要
 ソフトウェアベースのロードバランサー  負荷分散を実装するデーモン  柔軟性  多くの場合、サービス固有
 ハードウェアベースのロードバランサー (F5 など)  ハードウェアロードバランサー  ハードウェアアクセラレーションを使用できる  高価

利用可能な負荷分散の最初の種類は、コネクタベースの負荷分散です。MySQL Connector/Jや MySQL Connector/C++などのMySQLコネクタの多くはこの種類です。このアプローチの利点は、余分なネットワークホップがなく、アプリケーションが接続先のサーバーに関するより多くの情報を取得できることです。 欠点は、集中管理が行われないことと、構成を変更する場合、すべてのアプリケーションホストで構成を変更する必要があることです。 使用するプログラミング言語によっては、負荷分散が利用できないか、より高度なオプションが提供されない場合があります。 コネクタベースの負荷分散は、多くの場合、接続文字列(Java Database Connectivity (JDBC) URL など)で構成されるため、ほとんどのサードパーティアプリケーションで機能します。

DNSラウンドロビンを使用することもできます。 ただし、使用できないマシンへの接続を防止できないため、これはお勧めできません。 これは、すべてのサーバーが使用可能ではない場合、アプリケーションがより頻繁に接続を再試行する必要があることを意味します。 新しい MySQLコネクタの一部は、 DNS SRVをサポートしています。これはラウンドロビンDNSに多少似ていますが、優先順位を設定できます。 DNS SRVの利点は、業界標準であるため、クライアント側の構成が容易になることです。

Kubernetesを使用している場合は、Amazon ELBや他のクラウドベンダーの同様のサービスと連携する LoadBalancer サービスタイプを使用することをお勧めします。 オンプレミスのKubernetesの場合は、 metallbなどを使用できます。

Kubernetesを使用してクラウドサービスにデプロイしていない場合でも、クラウドプロバイダーの負荷分散サービスを使用できます。

もう1つの一般的なオプションは、ProxySQL、HAProxy、MySQLルーターなどのソフトウェアベースのロードバランサーを使用することです。

最後の種類の負荷分散では、ハードウェアベースの負荷分散を使用します。 これらは、ネットワークケーブルを接続する物理マシンです。これらは多くの場合、コストがかかりますが、高いスループットも提供します。

TiDBの一般的な負荷分散要件

TiDBがロードバランサーに課す要件は、典型的なMySQLのセットアップが必要とするものとはいくつかの点で異なります。

ロードバランサーはMySQLの読み込み/書き込み分割などの高度な機能を提供する可能性がありますが、TiDBはこれを必要としません。ロードバランサーはMySQLプロトコルを検査する必要はありません。TiDBはTCPレベルのロードバランサーで正常に機能します。

ロードバランサーはTiDBサーバー(デフォルトではTCPポート4000)へのアクセスのみを必要とし、配置ドライバー(PD)またはTiKVサーバーへのアクセスは必要ありません ステータスAPI (TCP port 10080 by default)を使用する場合、このポートへのアクセスも必要です。

http://<ip_of_tidb>:10080/status は正常なシャットダウン機能をサポートしているため、ヘルス チェックとして使用することをお勧めします。この機能により、TiDBサーバーをシャットダウンする前にクライアント接続をドレインすることができ、アプリケーションへの影響が軽減されます。この機能を利用するには、 graceful-wait-before-shutdown 変数 をゼロ以外の値に設定する必要があります。

read_only またはその他のMySQLまたはInnoDB固有の変数をチェックするヘルスチェックは避ける必要があります。これらの変数は、多くの場合、TiDBでは「noop」として実装されます。

ロードバランサーを配置する場所

ソフトウェアベースのロードバランサーの場合、ロードバランサーをアプリケーションと同じホストに配置することも、別のホストに配置することもできます。どちらにも利点があります。

アプリケーションのサイドカーと同じホストにロードバランサーをインストールすると、追加のネットワークホップが不要になるため、パフォーマンスが向上します。ただし、負荷分散ソフトウェアの多くのインスタンスを管理する必要があるため、これが少し複雑になります。

別々のホストにロードバランサーをインストールすると、追加のネットワークホップが必要になるため、通常、レイテンシが増加します。ネットワークによっては、ロードバランサーとの間の接続が飽和状態になるリスクもあります。ただし、サイドカーアプローチと比較すると、この方法は管理が簡単です。

これらのオプションを決定する際には、高可用性も重要な考慮事項です。前述のセットアップでは、ロードバランサーが使用不可になると単一のアプリケーションホストにのみ影響します。 後述のソリューションでは、ロードバランサーのソフトウェアの可用性を高めるためにいくつかの手順を実行する必要があり、構成が複雑になります。

負荷分散のテスト

本番環境で負荷分散を展開する前に、まず構成をテストして、すべてが期待どおりに機能することを確認する必要があります。 大規模な構成変更、アップグレード、およびその他のメンテナンスタスクも、運用セットアップで行う前にテストする必要があります。

tiup playground --db 2を使用してローカルテストを実行できます。これにより、2つのTiDBインスタンスが提供され、テスト中に接続がどのように切り替わるかを確認できます。

ロードバランサー経由で接続する場合、次のクエリを使用して、インスタンスへの到達を確認することができます。

SELECT @@hostname, @@port

mysql -h localhost...を使用すると、TCPポートが提供されていても、 mysql はUNIXソケット経由で接続しようとします。 構成によっては、これが機能しない場合があります。 代わりに、 mysql -h 127.0.0.1... を使用して、接続が確実にTCPを使用するようにします。

構成例

このセクションでは、TiDBを使用した最も一般的な負荷分散ソリューションの構成例が含まれています。

ProxySQL

ProxySQLは、MySQLプロトコル用のオープンソースの高性能プロキシです。 ProxySQLは MySQLプロトコルを認識しています。これにより、クエリのキャッシュやクエリの書き換えなど、多くのMySQL固有の機能がもたらされます。

最初のステップは、ProxySQLをインストールすることです。 他のプラットフォームの詳細と手順については、 ProxySQLのダウンロードとインストールを参照してください:

sudo dnf install proxysql

ProxySQLを構成するには、設定ファイル (/etc/proxysql.cnf) または管理インターフェイスを使用できます。 管理インターフェイスはMySQLプロトコルを使用し、ポート6032で使用できます。デフォルトの認証情報は 'admin'/'admin'です。

mysql -h 127.0.0.1 -P 6032 -u admin -padmin --prompt 'admin> '

次のステップは、TiDBサーバーの情報をProxySQLに設定することです。

admin> INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (1, '127.0.0.1', 4000);
Query OK, 1 row affected (0.00 sec)

admin> INSERT INTO mysql_servers(hostgroup_id, hostname, port) VALUES (1, '127.0.0.1', 4001);
Query OK, 1 row affected (0.00 sec)

admin> LOAD MYSQL SERVERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)

admin> SAVE MYSQL SERVERS TO DISK;
Query OK, 0 rows affected (0.04 sec)

デフォルトでは、ProxySQLは「モニター」と呼ばれるアカウントを使用して、バックエンド サーバーへの接続をテストします。 したがって、このユーザーを作成します。このユーザーのユーザー名とパスワードは、mysql-monitor_usernameおよびmysql-monitor_password変数によってProxySQLで設定されるため、必要に応じてこれを変更できます。

tidb> CREATE USER 'monitor'@'%' IDENTIFIED BY 'monitor';
Query OK, 0 rows affected (0.06 sec)

ここで、 tiup playgroundで利用可能なデフォルトの「root」ユーザーに一致するユーザーを ProxySQL に追加します。

admin> INSERT INTO mysql_users(username,password,default_hostgroup) VALUES ('root','',1);
Query OK, 1 row affected (0.00 sec)

admin> LOAD MYSQL USERS TO RUNTIME;
Query OK, 0 rows affected (0.00 sec)

admin> SAVE MYSQL USERS TO DISK;
Query OK, 0 rows affected (0.04 sec)

接続してみましょう:

$ mysql -h 127.0.0.1 -P 6033 -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 8
Server version: 5.5.30 (ProxySQL)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

mysql> select version();
+--------------------+
| version()          |
+--------------------+
| 5.7.25-TiDB-v5.0.0 |
+--------------------+
1 row in set (0.00 sec)

ProxySQLのアプリケーショントラフィックのデフォルトポートは6033(3306の逆!)です。

クエリルーティングまたはクエリキャッシングを使用したくない場合は、 mysql_users テーブルに設定されているfast_forwardを有効にすることをお勧めします。これにより、接続ごとにプロキシが実行する必要のある作業量が減り、レイテンシがわずかに短縮されます。

HAProxy

HAProxyは、主にHTTP負荷分散で知られる高性能ロードバランサーです。 ただし、TCPロードバランサーとしても使用できます。MySQLプロトコルについては基本的なヘルスチェック以外は対応していません。

HAProxy のインストールから始めます。

sudo dnf install haproxy

次に、 /etc/haproxy/haproxy.cfg を編集して以下を追加して、HAProxyを設定します:

listen tidb
    mode tcp
    bind 127.0.0.1:3306
    balance leastconn
    option mysql-check user root
    server tidb1 127.0.0.1:4000 check
    server tidb2 127.0.0.1:4001 check
frontend stats
    bind 127.0.0.1:8080
    stats enable
    stats uri /
    stats refresh 10s
    stats admin if LOCALHOST

これにより、2つのバックエンドサーバーを持つtidb-serviceが追加されます。そして接続数が最も少ないサーバーに接続がルーティングされます。 これにより、MySQLプロトコル固有のチェックで、バックエンドサーバーが正常かどうかを確認できます。

stats はオプションであり、Webインターフェイスで統計を表示し、サーバーを管理できるようにします。

ここでは、localhost のみにバインドするために 127.0.0.1 が使用されました。 外部接続を許可する場合は、 bind ::3306を使用します。

SELinux が有効になっているシステムでは、3306 TCPポートは mysqld_port_tとしてラベル付けされます。 HAProxyがこのポートを使用できるようにするには、次の手順を実行します。 SELinuxが有効になっていない場合は、この手順をスキップできます。

sudo setsebool -P haproxy_connect_any 1

HAProxy を起動します:

sudo systemctl enable haproxy --now

TiDBでHAProxyを使用するためのベストプラクティスも参照してください。

MySQL Router

MySQL Routerは、OracleのMySQLチームによるMySQLプロトコル用のオープンソースの負荷分散ソリューションです。

まず、MySQLルーターをインストールします。 選択したプラットフォームのドキュメントの指示 に従います。 この例では、Linuxを使用しています。

sudo rpm -ivh https://dev.mysql.com/get/mysql80-community-release-fc34-1.noarch.rpm
sudo dnf install mysql-router

 /etc/mysqlrouter/mysqlrouter.confに以下を追加して、ルーターを設定します:

[routing:tidb]
bind_address = 127.0.0.1:6446
routing_strategy = round-robin
protocol = classic
destinations = 127.0.0.1:4000, 127.0.0.1:4001
sudo systemctl enable mysqlrouter --now

注:TiDBは Xプロトコルをサポートしていないため、 protocol = classicを使用する必要があります。

MySQL Connector/J

MySQL Connector/Jは、Javaおよびその他のJVMベースの言語用のMySQLプロトコルを実装するJDBCドライバーです。

MySQL Connector/Jは、さまざまな種類の マルチホスト接続をサポートしています。この例では、 loadbalance オプションを使用します。この構成により、負荷が複数のTiDBサーバーに分散されます。フェイルオーバーが発生すると、リストの最初のホストを使用しようとするため、負荷が不均一に分散されます。

TiDBはXプロトコルをサポートしていないため、TiDBでConnector/J X Dev API 接続を使用することはできません。

これは、MySQL Connector / J 8.0.24の例です。

package com.pingcap.tidb_demo_java;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.ResultSet;
public class App
{
    public static void main( String[] args )
    {
        try {
                Class.forName("com.mysql.cj.jdbc.Driver").newInstance();
        } catch (Exception ex) {
                System.out.println("Failed to load driver: " + ex.getMessage());
        }
        try {
                Connection conn = DriverManager.getConnection("jdbc:mysql:loadbalance://127.0.0.1:4000,127.0.0.1:4001?user=root");
                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT @@hostname, @@port");
                rs.next();
                System.out.println(rs.getString("@@hostname") + ":" + rs.getString("@@port"));
        } catch (SQLException ex) {
                System.out.println("SQLException: " + ex.getMessage());
        }
    }
}

JavaアプリケーションでTiDBを使用する方法の詳細については、 TiDBを使用してJavaアプリケーションを開発するためのベストプラクティスを参照してください。

MySQL Connector/Python

MySQL Connector/Pythonは、Python用の公式のMySQLドライバーです。Python用に一般的に使用されるMySQLドライバーは他にもありますが、それらはここで説明する負荷分散構成をサポートしていません。

この例では、MySQL Connector/Python  8.0.24 (mysql-connector-python==8.0.24 では pip freeze output)を使用しています。

このコネクタをインストールするには、 コネクタ/Python のインストールを参照してください。

#!/usr/bin/python3
import mysql.connector

config = {
    "failover": [
        {"host": "127.0.0.1", "port": 4000, "user": "root"},
        {"host": "127.0.0.1", "port": 4001, "user": "root"},
    ],
}

for _ in range(5):
    c = mysql.connector.connect(**config)
    cur = c.cursor()
    cur.execute("select @@hostname, @@port")
    print(cur.fetchone())
    cur.close()
    c.close()

このように出力されます:

('myserver1', 4001)
('myserver1', 4000)
('myserver1', 4001)
('myserver1', 4001)
('myserver1', 4000)

Amazon ELB

Amazon ELBは、Amazon Web Services (AWS)クラウドの負荷分散サービスです。

ロードバランサーは、AWS EC2ダッシュボード(以下を参照)を使用して手動で作成するか、CloudFormationなどの自動化を使用して作成できます。Amazon Elastic Kubernetes Service (EKS)を使用する場合、ロードバランサーはコントローラーによって作成されます。

AWS EC2 Create Network Load Balancer
AWSのNetwork Load Balancerの作成画面
  1. ロードバランサーのタイプは「Network Load Balancer」を選択します。 要件に応じて、”internet-facing”または”internal”を選択する必要があります。
  2. TCP ポート3306または4000を使用するようにリスナーを構成します。
  3. TiDB インスタンスの IP アドレスを使用してターゲットグループを作成します。TiDB 用に構成したポートに応じて、ポート3306または4000を選択します。

TiDBマシンのセキュリティグループがロードバランサーからのアクセスを許可していることを確認してください。 アプリケーションからのアクセスを許可するが、インターネットの残りの部分からのアクセスを制限するように、ロードバランサーセキュリティグループを構成する必要があります。

詳細については、 Network Load Balancer の使用を開始するを参照してください。

詳細設定

サーバーのハードウェア構成が異なる場合(たとえば、より多くのメモリとCPUがある場合)、特定のサーバーをロードバランサープールから除外するか、別のロードバランサープールを作成することができます。これは、分析タスクまたはデータ読み込みタスクを分離するために使用できます。

自動化

インスタンスの追加や削除などの操作を自動化するには、 TiDB HTTP APIを使用することをお勧めします。

すべてのサーバーをロードバランサープールに配置したくない場合は、HTTP APIを使用してサーバーラベルでフィルター処理できます。

curl -s http://127.0.0.1:10080/info/all | jq '.all_servers_info'

{
  "431ccb25-24a6-4311-b48d-613cac401b22": {
    "version": "5.7.25-TiDB-v5.0.1",
    "git_hash": "1145e347d3469d8e89f88dce86f6926ca44b3cd8",
    "ddl_id": "431ccb25-24a6-4311-b48d-613cac401b22",
    "ip": "127.0.0.1",
    "listening_port": 4000,
    "status_port": 10080,
    "lease": "45s",
    "binlog_status": "Off",
    "start_timestamp": 1620024407,
    "labels": {},
    "server_id": 0
  },
  "b830f979-c277-4e47-934e-87bb2063cf4d": {
    "version": "5.7.25-TiDB-v5.0.1",
    "git_hash": "1145e347d3469d8e89f88dce86f6926ca44b3cd8",
    "ddl_id": "b830f979-c277-4e47-934e-87bb2063cf4d",
    "ip": "127.0.0.1",
    "listening_port": 4001,
    "status_port": 10081,
    "lease": "45s",
    "binlog_status": "Off",
    "start_timestamp": 1620024407,
    "labels": {},
    "server_id": 0
  }
}

TiDBを試す

TiDBを使い始めるには、 コミュニティエディションまたはTiDB Cloud無料トライアルをお試しください。日本語ドキュメントのTiDBクイックスタートガイド、またはTiDB Cloudワークショップガイドのご利用をお勧めします。ご不明な点などございましたら、お問い合わせフォームに入力してご連絡ください。 また、GitHubで問題を報告し、遭遇した問題を報告することもできます。


Have questions? Let us know how we can help.

Contact Us

TiDB Cloud Dedicated

TiDB Cloudのエンタープライズ版。
専用VPC上に構築された専有DBaaSでAWSとGoogle Cloudで利用可能。

TiDB Cloud Serverless

TiDB Cloudのライト版。
TiDBの機能をフルマネージド環境で使用でき無料かつお客様の裁量で利用開始。