5.8. 負荷分散

Pgpool-IIのSELECTクエリの負荷分散はrawモードを除くすべてのクラスタリングモードで動作します。 有効時、Pgpool-IIは更新を伴うクエリを、ストリーミングレプリケーションモード、ロジカルレプリケーションモード、Slonyモードではプライマリノードに、レプリケーションモードでは全てのバックエンドノードに対し送信します。 そして、その他のクエリは全てのバックエンドの間で負荷分散されます。 statement_level_load_balanceが指定されている場合を除き、負荷分散メカニズムが参照クエリをどのノードに送信するかはセッション開始時に決められ、セッションの終了まで変更されません。 ただし、いくつかの例外があります。 詳細については以下をご覧ください。

注意: 負荷分散ができないためにプライマリノードまたは全バックエンドノードに送られるクエリもまた、 負荷分散アルゴリズムの考慮に入れられます。

注意: どのDBノードが負荷分散ノードになっているかは、SHOW POOL_NODESを利用して確認できます。

5.8.1. 負荷分散の条件について

クエリが負荷分散されるためには、以下の全ての条件を満たす必要があります:

注意: SELECTクエリの前に任意のコメントを挿入することにより負荷分散を抑制することができます。

/*REPLICATION*/ SELECT ...
   

SQLコメントの記述が負荷分散に影響を与えないようにするには、allow_sql_commentsをonにします。 replicate_selectも参照してください。

注意: JDBC ドライバにはautocommitオプションがあります。 autocommit を無効にすると、ドライバが内部でBEGINおよびCOMMITコマンドを実行し、明示的なトランザクションが開始されます。 この場合、トランザクション内における上記の負荷分散の制限事項が適用されます。

5.8.2. 更新を伴うクエリは負荷分散に影響する可能性がある

一般的には、特定の条件を満たしている場合、参照クエリは負荷分散します。 ただし、更新を伴うクエリは負荷分散に影響する可能性があります。 ここで、”更新を伴うクエリ”とは以下の例を除く全てのクエリを指します。

更新を伴うクエリが存在する場合、続いて実行される参照クエリは負荷分散しません。 すなわち、disable_load_balance_on_writeの設定に応じて(ストリーミングレプリケーションの)プライマリノードまたは(その他のモードの)メインノードに送ります。

5.8.3. ストリーミングレプリケーションにおける負荷分散

ストリーミングレプリケーションとHot Standbyを利用している環境では、プライマリノードに送ってよい問い合わせ、スタンバイに送ってもよい問い合わせ、両方に送らなければならない問い合わせを厳密に管理する必要があります。 Pgpool-IIのストリーミングレプリケーションモードは、こうした振り分けを自動的に行ないます。

クエリそのものから、どのクエリがどのノードに送られるべきかを区別します。

明示的なトランザクションでは、以下のようになります。

問い合わせが、拡張問い合わせモードで実行される場合は、問い合わせのparse段階で、問い合わせがスタンバイに送信可能かが決まります。 その際の判断ルールは通常のSQLと同じです。 たとえば問い合わせがINSERTならば、プライマリノードに送られます。 それに後に続くbind, describe, executeも同じくプライマリに送られます。

注意: 負荷分散によりSELECT文のparseがスタンバイノードに送信され、その後INSERTなどのDML文がPgpool-IIに送られた場合、parse済のSELECTはプライマリノードで実行されなければなりません。 そのため、同じSELECTがプライマリノードで再度パースされることになります。

最後に、pgpool-IIのパーサが構文エラーと判断した問い合わせはプライマリノードだけに送られます。

5.8.4. 負荷分散の設定

load_balance_mode (boolean)

onに設定すると、Pgpool-IIは入ってきたSELECTクエリに対する負荷分散を有効にします。 すなわち、クライアントからのSELECTクエリは設定されたPostgreSQLバックエンドに振り分けます。 デフォルトはonです。

このパラメータはサーバ起動時にのみ設定可能です。

ignore_leading_white_space (boolean)

onに設定すると、負荷分散の際にSQL文行頭の空白を無視します(全角スペースは無視されません)。 これは、DBI/DBD:Pgのように、ユーザの意図に反して空白を追加するようなAPIを使っているときに有用です。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

read_only_function_list (string)

データベースに対して更新を行なわない関数名をコンマ区切りで指定します。 このリストに指定されていない関数呼び出しを含むSELECTは負荷分散されません。 これらのクエリはレプリケーションモードにおいてはすべてのDBノードで複製され、それ以外のモードにおいてはプライマリノードにのみ送信されます。

関数名のマッチングに正規表現を使うことができます。 正規表現には自動的に^$が付与されます。

例 5-2. 正規表現の使用

もし読み出しのみを行う関数が"get_"あるいは"select_"で始まるようにしてあるのであれば、read_only_function_listを以下のように設定可能です。

read_only_function_list = 'get_.*,select_.*'
	

注意: 問い合わせがスキーマ修飾なしあるいはスキーマ修飾ありの両方の関数を参照するなら、その両者をリストに登録しなければなりません。

#たとえば、ある問い合わせが"f1()"を、他の問い合わせが"public.f1()"を関数f1を参照するなら、
#read_only_function_listは以下のように設定されるでしょう。

read_only_function_list = "f1,public.f1"

       

注意: このパラメータとwrite_function_listが空文字なら、関数の揮発性属性が検査されます。 これが揮発性(volatile)なら、この関数は書き込みを行うと見なされます。 これは便利ですし、推奨できる設定です。 しかしこれにより初回システムカタログへのアクセスが発生します(次回以降はキャッシュされた結果が使用されるので余計な問い合わせは送信されません。) そのようなクエリの送信を望まないのであれば、このパラメータを使い続けることができます。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

write_function_list (string)

データベースに対して更新を行う関数名をコンマ区切りで指定します。 このリストに指定された関数呼び出しを含むSELECTは負荷分散されません。 これらのクエリはレプリケーションモードにおいてはすべてのDBノードで複製され、それ以外のモードにおいてはプライマリノードにのみ送信されます。

関数名のマッチングに正規表現を使うことができます。 正規表現には自動的に^$が付与されます。

例 5-3. 正規表現の使用

もし更新を行う関数が"set_"、"update_"、"delete_"、あるいは"insert_"で始まるようにしてあるのであれば、write_function_listを以下のように設定可能です。

	 write_function_list = 'nextval,setval,set_.*,update_.*,delete_.*,insert_.*'
	

注意: 問い合わせがスキーマ修飾なしあるいはスキーマ修飾ありの両方の関数を参照するなら、その両者をリストに登録しなければなりません。

#たとえば、ある問い合わせが"f1()"を、他の問い合わせが"public.f1()"を関数f1を参照するなら、
#write_function_listは以下のように設定されるでしょう。

write_function_list = "f1,public.f1"
       

注意: write_function_listread_only_function_listは互いに排他的で、2つのリストの内、どちらか一方のみ設定することができます。

例 5-4. nextval()setval()を適切なバックエンドに送る設定

Pgpool-II V3.0より前のバージョンでは、固定でnextval()setval()がデータベースに書き込みを行なう関数であると認識されていました。 以下のようにread_only_function_listwrite_function_listを設定することで、それと同じように動作させることができます。

read_only_function_list = ''
write_function_list = 'nextval,setval,lastval,currval'
	 

注意: PostgreSQLにはnextval()setval()に加え、lastval()currval()があります。 lastval()currval()は書き込みを行う関数ではありませんが、これらの関数が負荷分散されることで発生するエラーを防ぐため、lastval()currval()を書き込みを行う関数として扱うのが望ましいです。

注意: このパラメータとread_only_function_listが空文字なら、関数の揮発性属性が検査されます。 これが揮発性(volatile)なら、この関数は書き込みを行うと見なされます。 これは便利ですし、推奨できる設定です。 しかしこれにより初回システムカタログへのアクセスが発生します(次回以降はキャッシュされた結果が使用されるので余計な問い合わせは送信されません。) そのようなクエリの送信を望まないのであれば、このパラメータを使い続けることができます。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

primary_routing_query_pattern_list (string)

特定の SQL をプライマリノードに送信するように primary_routing_query_pattern_listを設定します。 SQL パターンをセミコロン区切りで指定します。 ネイティブレプリケーションモード以外でのみで動作します。

SQL のマッチングに正規表現を使うことができます。 正規表現には自動的に^$が付与されます。 "'"、";" あるいは "*" などの文字を SQL パターンの中で利用する場合、"\" でエスケープする必要があります。 SQL パターンの中で正規表現の特殊文字を使用する場合、"\" でエスケープする必要があります。 例えば、"'"、";"、"*"、"("、")"、"|"、"+"、"."、"\"、"?"、"^"、"$"、"{"、"}"、"{"、"}" などの特殊文字の場合、"\" でエスケープする必要があります。 また、指定される SQL パターンでは大文字と小文字を区別しません。

例 5-5. 正規表現の使用

もし以下の SQL をプライマリノードに送信したい場合、primary_routing_query_pattern_listを以下のように設定可能です。

  • SELECT * FROM table_name1;

  • SELECT col1, col2 FROM table_name2 WHERE col1 LIKE '%a%';

  • table_name3 という文字列を含む SQL

primary_routing_query_pattern_list = 'SELECT \* FROM table_name1\;;SELECT col1, col2 FROM table_name2 WHERE col1 LIKE \'%a%\'\;;.*table_name3.*'
       

注意: primary_routing_query_pattern_listread_only_function_listの両方にマッチした場合、 read_only_function_listの設定が無視され、プライマリノードのみに送信されます。

SQL のパターンにもよりますが、この機能を使用する場合、パフォーマンスが 1-2% 低下する可能性があります。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

database_redirect_preference_list (string)

特定のデータベース接続でSELECTクエリが指定したロードバランス比率で特定のバックエンドノードに送信されるように、"データベース名:ノードID(比率)"ペアのリストを指定します。ロードバランス比率は0-1の間の値を指定します。 比率の指定を省略した場合は、デフォルトの1.0となります。

たとえば"test:1(0.5)"とした場合、"test"データベースへの接続においては、Pgpool-IIは50%のSELECTクエリをIDが1のバックエンドノードに送信します。 複数の"データベース名:ノードID"のペアを カンマ(,)で区切って指定することができます。

データベース名には正規表現を指定することができます。 ノードIDには特別なキーワードを使うことができます。 "primary"が指定された場合にはクエリはプライマリノードに送られます。 また、"standby"が指定された場合はスタンバイノードのうちどれかをウェイト(backend_weight)に応じてランダムに選択します。

例 5-6. database_redirect_preference_listの利用

SELECTクエリのルーティングルールを以下のように設定したい場合:

  • 全てのpostgresデータベースにおけるSELECTクエリはプライマリバックエンドノードに送る。

  • 全てのmydb0またはmydb1データベースにおける30%のSELECTクエリはIDが1のバックエンドノードに送る。

  • 全てのmydb2データベースにおけるSELECTクエリはスタンバイバックエンドノードに送る。

database_redirect_preference_listは以下のように設定します。

database_redirect_preference_list = 'postgres:primary,mydb[01]:1(0.3),mydb2:standby'
	

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

app_name_redirect_preference_list (string)

特定のクライアントアプリケーションの接続でSELECTクエリが指定したロードバランス比率で特定のバックエンドノードに送信されるように、"アプリケーション名:ノードID(比率)"ペアのリストを指定します。

注意: 「アプリケーション名」とはクライアントがデータベースに接続する時に指定する名称で、PostgreSQL V9.0以降で利用可能です。

たとえば、psqlコマンドのアプリケーション名は"psql"です。

注意: Pgpool-IIは、クライアントから送信されたスタートアップパケットに含まれるアプリケーション名だけを認識します。 クライアントは事後にセッションの中でアプリケーション名を指定できますが、それはPgpool-IIのクエリルーティングでは考慮されません。

app_name_redirect_preference_listの記法はdatabase_redirect_preference_listと同じですので、アプリケーション名には正規表現も使用できます。 同様に特別なキーワード"primary"はプライマリノードを、"standby"はスタンバイサーバのいずれかを意味します。 ロードバランス比率は0-1の間の値を指定します。ロードバランス比率の指定を省略した場合は、デフォルトの1.0となります。

例 5-7. app_name_redirect_preference_listの利用

SELECTクエリのルーティングルールを以下のように設定したい場合:

  • 全てのpsqlクライアントからのSELECTクエリはプライマリバックエンドノードに送る。

  • 全てのmyapp1クライアントからの30%のSELECTクエリはIDが1のバックエンドノードに送る。

  • 全てのmyapp2クライアントからのSELECTクエリはスタンバイバックエンドノードに送る。

app_name_redirect_preference_listは以下のように設定します。

app_name_redirect_preference_list = 'psql:primary,myapp1:1(0.3),myapp2:standby'
	

注意: app_name_redirect_preference_listは、database_redirect_preference_listよりも優先されます。

たとえば、 database_redirect_preference_list = 'postgres:standby(1.0)'app_name_redirect_preference_list = 'myapp1:primary(1.0)' と設定した場合、アプリケーションmyapp1postgresデータベースでのSELECTはプライマリバックエンドノードに送られます。

注意: app_name_redirect_preference_list及びdatabase_redirect_preference_listの設定では、複数のデータベース名やアプリケーション名にマッチした場合、最初の設定が反映されます。

たとえば、 database_redirect_preference_list = 'postgres:primary,postgres:standby' と設定した場合、postgres:primaryの設定が反映されます。

注意

JDBCドライバのpostgresql-9.3以前のバージョンでは、JDBCドライバの"ApplicationName" と "assumeMinServerVersion=9.0"オプションを指定してもスタートアップパケットの中にアプリケーション名を含みません。 JDBCからapp_name_redirect_preference_list機能を使用したければ、postgresql-9.4 以降のドライバをお使いください

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。

allow_sql_comments (boolean)

onに設定すると、Pgpool-IIは負荷分散やクエリキャッシュができるかどうかの判定の際にSQLコメントを無視します。 このパラメータがoffに設定されている場合、クエリのSQLコメントにより、クエリの負荷分散やキャッシュを防止することができます。 (Pgpool-II V3.4より前のバージョンの動作です)。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。 現在のセッションでのパラメータ値は、PGPOOL SETコマンドで変更することもできます。

disable_load_balance_on_write (string)

writeクエリが発行された時の負荷分散の振る舞いを指定します。 このパラメータは、特にストリーミングレプリケーションモードで有効です。 writeクエリがプライマリサーバに送られた時、この変更はスタンバイサーバにも適用されますが、そこには時間差があります。 ですからクライアントが行を更新して同じ行をスタンバイから読むと、その行の最新データを読めないかもしれません。 もしこれが問題になるようなら、クライアントは常にプライマリサーバからデータを読むべきです。 しかし、これは実質的に負荷分散を無効にして、性能低下をもたらします。 このパラメータは、クラスタリングについて考慮しないアプリケーションにおける互換性と、性能の間におけるトレードオフの細かな調整を可能にします。

このパラメータがoffなら、writeクエリが来ても負荷分散が行われます。 この設定では最大の性能を期待できますが、クライアントは古いデータを読むことになるかもしれません。 この設定は、PostgreSQLのパラメータsynchronous_commit = 'remote_apply'が設定されているか、ネィティブレプリケーションモードのときに有用です。 そのような環境ではレプリケーションの遅延が発生しないからです。

このパラメータがtransactionで、明示的なトランザクション中にwriteクエリが来ると、トランザクションが終了するまで負荷分散が行われません。 なお、明示的なトランザクション中ではないreadクエリは、このパラメータの影響を受けないことに注意してください。 この設定は、たいていの場合に最良のバランスを提供するので、この設定から試すことをお勧めします。 これはデフォルトで、Pgpool-II 3.7以前のバージョンと同じ振る舞いです。

このパラメータがtrans_transactionで、明示的なトランザクション中にwriteクエリが来ると、トランザクションが終了するまで負荷分散が行われません。 また、セッションが終了するまで後続の明示的なトランザクション中でも負荷分散が行われません。 この設定は古いアプリケーションにとって安全ですが、transactionよりは性能が低下します。 なお、明示的なトランザクション内にないreadクエリは、このパラメータの影響を受けないことに注意してください。

このパラメータがalwaysで、writeクエリが来ると、明示的なトランザクション中であるかどうかを問わず、セッションが終了するまで負荷分散が行われません。 この設定により、クラスタリングについて考慮しないアプリケーションを使った場合最大の互換性が得られますが、性能は最低になります。

このパラメータがdml_adaptiveに設定されると、Pgpool-IIは明示的なトランザクション内のWRITE文に含まれる個々のテーブルを追跡し、テーブルが同じトランザクション内で事前に変更されている場合には後続のREADクエリを負荷分散しません。 テーブルが依存する関数、トリガ、ビューはdml_adaptive_object_relationship_listで設定できます。

dml_adaptive_object_relationship_list (string)

オブジェクトに依存しているREAD問い合わせが負荷分散しないようにするために、オブジェクト名の後にコロン(:)、続いてカンマ(,)で区切られた依存オブジェクトの名前のリストを指定できます。 "[object]:[dependent-object]" 明示的なトランザクションブロックの中でWRITE文が発行された後、依存オブジェクトへの参照を含むREAD文は負荷分散されないようになります。

例 5-8. dmlが関与するオブジェクト関係の設定

table_1へのINSERTのたびにtable_2にINSERTを行うトリガがtable_1に設定されており、table_1へのINSERTの後でtable_2からの読み出しが同じトランザクション内で負荷分散されないようにしたいとします。 このための設定は以下のようになります。

dml_adaptive_object_relationship_list = 'table_1:table_2'
		

このパラメータはdisable_load_balance_on_write='dml_adaptive'のときにのみ有効です。

注意: 関数の依存性を設定するには、関数はwrite_function_listで設定済みでなければなりません。

statement_level_load_balance (boolean)

onに設定し、load_balance_modeがonに設定されていると、参照クエリごとに負荷分散先を決めます。 offに設定すると、セッションが始まるときに決められた負荷分散先がセッションが終了するまで変更されません。 例えば、コネクションプールを利用し、バックエンドサーバに接続したままのようなアプリケーションの場合、 セッションが長い間保持される可能性があるので、セッションが終了するまで負荷分散先のノードが変わりません。 このようなアプリケーションでは、statement_level_load_balanceを有効にすると、 セッションごとではなく、クエリごとに負荷分散先を決めることが可能です。デフォルトはoffです。

注意: streaming replication modeでは、BEGIN/END/COMMIT/ABORT/SET/SAVEPOINT/RELEASE SAVEPOINT/DEALLOCATE ALL/DISCARDのようなクエリはプライマリノードとロードバランスノードに送られます。 statement_level_load_balanceがonなら、そうしたクエリはすべてのスタンバイノードにも送られます これは通常問題になりません。 しかしスタンバイノードの一つが遠隔ネットワークにあると、ネットワーク遅延のためにこうしたクエリで大きな速度低下を起こすことがあります。

このパラメータはPgpool-IIの設定を再読み込みすることで変更可能です。