PostgreSQLのautovacuumと格闘中の阿部です。
(という出だしで始めましたが本記事ではPostgreSQLのautovacuumについては触れないので、興味があれば公式ドキュメントをご覧ください。)
今回はPGroongaからGroongaのリソースを確認する方法を紹介します。
PGroongaはインデックスとしてGroongaを使うPostgreSQLの拡張機能です。 PostgreSQLにてお手軽にゼロETLで全言語対応の超高速全文検索機能を使えるようになりますが、より専門的なチューニングなどをする場合は内部で利用しているGroongaの状況も確認する必要があります。
今回はその一例としてGroongaのn_buffer_segments
の値を確認する方法を紹介します。
はじめに
この記事はPGroongaでGroongaのリソースを確認するイメージをつかんでもらうことを目的に書いています。 本文中に登場するGroongaコマンドやPGroongaの関数について細かく説明しすぎると目的から外れてしまうので簡単に紹介するに留めます。
この記事では次のGroongaコマンドやPGroongaの関数が登場します。
- Groongaコマンド
object_inspect
: https://groonga.org/ja/docs/reference/commands/object_inspect.html- オブジェクト(テーブルやカラム)を調査するコマンドです
- このコマンドで表題の
n_buffer_segments
の値も確認できます
dump
: https://groonga.org/ja/docs/reference/commands/dump.html- いわゆるdumpコマンドです
- スキーマやデータを出力します
- PGroongaの関数
pgroonga_index_column_name
: https://pgroonga.github.io/ja/reference/functions/pgroonga-index-column-name.html- PGroongaのインデックス名をGroongaのインデックスカラム名に変換します
pgroonga_command
: https://pgroonga.github.io/ja/reference/functions/pgroonga-command.html- PGroongaからGroongaのコマンドを実行するための関数です
以降は次の流れで説明します。
- Groongaの
n_buffer_segments
とは?- なぜ確認する必要があるのかも含めて説明
- Groongaの
n_buffer_segments
を取得- いくつか手順があるので実行例を示しながら説明します
- Groongaの
n_buffer_segments
をまとめて取得- 2の方法で
n_buffer_segments
は確認できるのですが、PGroongaのインデックスが大量にあると確認するのが大変なのでまとめて取得する例を紹介します
- 2の方法で
1. Groongaの n_buffer_segments
とは?
ドキュメント によると
調査対象のインデックスカラム内の「buffer」に使っている最大セグメント数。 「buffer」はポスティングリストを保存する場所です。
というものらしいです。 何のことかわからないといますが、わからなくて大丈夫です。
だいたい何の値でもそうだと思いますが…、この値も上限値を超えるとGroongaにやばいことが起こる、ということだけ抑えておけば良いです。
PGroongaなり、Groongaなりをふつうに使っていれば基本的には上限値を超えることはないので、ふつうに使っている場合は気にしなくて良いです。
PGroongaをふつうに使ってないとき
ふつうに使ってないときは気にしたほうが良いです。 例えば何らかの理由でautovacuumは止めて運用している、などです。
autovacuumが動いているともろもろお掃除されるので、n_buffer_segments
は一定の値からあまり動かないのですが、お掃除されないと増える一方で上限値を超えてしまう場合があります。
ということで、上限値を超える前に検知して手動でお掃除処理を実行するためにn_buffer_segments
の値を監視する必要があります。
このあたりの運用の話題は本題からずれるので割愛しますが、詳しく知りたい場合はお問い合わせよりご連絡ください。
2. Groongaのn_buffer_segments
を取得
次の3ステップでn_buffer_segments
の値が取得できます。
- 対象のPostgreSQLのインデックス名と、対象インデックスのカラム名を確認
- PGroongaのインデックス名をGroongaのインデックスカラム名に変換
- Groongaのインデックスカラム名を元に
object_inspect
を使って、n_buffer_segments
の値を取得
PGroongaのpgroonga_index_column_name関数のページにある「使い方」の内容のほぼ再掲になるのですが、補足説明を入れつつGroongaのn_buffer_segments
を取得するまでの手順を具体的に説明していきます。
説明の準備: サンプルスキーマ
CREATE TABLE memos (
id integer,
title text,
content text,
tag text
);
CREATE INDEX pgroonga_index
ON memos
USING pgroonga (title, content, tag);
このテーブルとインデックスを例に説明します。
対象のPostgreSQLのインデックス名と、対象インデックスのカラム名を確認
pgroonga_index
インデックスのtag
カラムを使って説明します。
PGroongaのインデックス名をGroongaのインデックスカラム名に変換
確認したいのはGroongaのn_buffer_segments
の値です。
PostgreSQLのテーブル名などから直接それを得ることはできないので、まずはGroongaのインデックスカラム名を確認します。
Groongaのインデックスカラム名がわかるとそこからn_buffer_segments
の値が取得できます。
ということでpgroonga_index_column_name
関数を使ってGroongaのインデックスカラム名を確認します。
クエリ例(インデックス名とカラム名を渡すと取得できます):
SELECT pgroonga_index_column_name('pgroonga_index', 'tag');
結果例:
pgroonga_index_column_name
----------------------------
Lexicon17478_2.index
(1 行)
得られたLexicon17478_2.index
がGroongaのインデックスカラム名です。
Groongaのインデックスカラム名を元にobject_inspect
を使って、n_buffer_segments
の値を取得
確認したLexicon17478_2.index
からn_buffer_segments
の値を取得します。
PGroongaのpgroonga_command
関数を使って、Groongaのobject_inspect
コマンドを実行して取得します。
クエリ例:
SELECT jsonb_pretty(
pgroonga_command('object_inspect', ARRAY['name', 'Lexicon17478_2.index'])::jsonb
);
結果例は長いので割愛します。value
の中のstatistics
内にn_buffer_segments
が確認できると思います。
で、終わると物足りないので、ピンポイントでn_buffer_segments
の値を取得するクエリ例を掲載しつつ結果例も提示します。
ピンポイントでn_buffer_segments
の値を取得するクエリ例:
SELECT
(pgroonga_command('object_inspect', ARRAY['name', 'Lexicon17478_2.index'])::jsonb)
->1->'value'->'statistics'->'n_buffer_segments' AS n_buffer_segment;
結果例:
n_buffer_segment
------------------
0
(1 行)
これでPGroongaからGroongaのn_buffer_segments
が確認できました。
3. Groongaのn_buffer_segments
をまとめて取得
「2. Groongaのn_buffer_segments
を取得」の通りに実行すると、Groongaのn_buffer_segments
が確認できるのですが、対象のPGroongaのインデックスが大量にあると、ひとつひとつ実行していくのは大変です。
ということで、SQLを1回実行してまとめてGroongaのn_buffer_segments
を取得するクエリ例も紹介します。
クエリ例:
SELECT
i.index_column,
(pgroonga_command(
'object_inspect',
ARRAY[
'name', i.index_column
]
)::jsonb)->1->'value'->'statistics'->'n_buffer_segments' AS n_buffer_segments
FROM (
SELECT
array_to_string((string_to_array(cmd, ' '))[2:3], '.') AS index_column
FROM (
SELECT
regexp_split_to_table(
pgroonga_command('dump', ARRAY['dump_records', 'no']),
'\n'
) AS cmd
) AS c
WHERE cmd LIKE 'column_create%COLUMN_INDEX%'
) AS i;
結果例:
index_column | n_buffer_segments
----------------------+-------------------
Lexicon17478_0.index | 0
Lexicon17478_1.index | 0
Lexicon17478_2.index | 0
(3 行)
クエリの解説
いくつかのサブクエリを実行しているので、それぞれ分けて実行するとわかりやすいです。
SELECT
regexp_split_to_table(
pgroonga_command('dump', ARRAY['dump_records', 'no']),
'\n'
) AS cmd;
cmd
-----------------------------------------------------------------------------------------------------------------
config_set alias.column Aliases.real_name
table_create Aliases TABLE_HASH_KEY ShortText
column_create Aliases real_name COLUMN_SCALAR ShortText
table_create IndexStatuses TABLE_HASH_KEY UInt32
column_create IndexStatuses max_record_size COLUMN_SCALAR UInt32
column_create IndexStatuses wal_applied_position COLUMN_SCALAR UInt64
table_create Lexicon17478_0 TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
table_create Lexicon17478_1 TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
table_create Lexicon17478_2 TABLE_PAT_KEY ShortText --default_tokenizer TokenBigram --normalizer NormalizerAuto
table_create Sources17478 TABLE_HASH_KEY UInt64
column_create Sources17478 content COLUMN_SCALAR|COMPRESS_ZSTD LongText
column_create Sources17478 tag COLUMN_SCALAR|COMPRESS_ZSTD LongText
column_create Sources17478 title COLUMN_SCALAR|COMPRESS_ZSTD LongText
column_create Lexicon17478_0 index COLUMN_INDEX|WITH_POSITION Sources17478 title
column_create Lexicon17478_1 index COLUMN_INDEX|WITH_POSITION Sources17478 content
column_create Lexicon17478_2 index COLUMN_INDEX|WITH_POSITION Sources17478 tag
(23 行)
↑ではGroongaのdump --dump_records no
の結果を分割してテーブルとして取得しています。
その結果から↓でインデックスカラム名のみを抽出しています。
SELECT
array_to_string((string_to_array(cmd, ' '))[2:3], '.') AS index_column
FROM (
SELECT
regexp_split_to_table(
pgroonga_command('dump', ARRAY['dump_records', 'no']),
'\n'
) AS cmd
) AS c
WHERE cmd LIKE 'column_create%COLUMN_INDEX%';
index_column
----------------------
Lexicon17478_0.index
Lexicon17478_1.index
Lexicon17478_2.index
(3 行)
あとは抽出したインデックスカラム名を元にobject_inspect
を実行してn_buffer_segments
を得ています。
まとめ
(ふつうに使っていればn_buffer_segments
の値を参照する必要はないはずですが、)PGroongaからGroongaのn_buffer_segments
の値を確認する方法を紹介しました。
n_buffer_segments
の値を監視しないといけないような特殊な環境下でのPGroongaやGroongaを運用するときのサポートもいたしますので、何かお困りの際はお問い合わせよりご連絡ください。
補足
「3. Groongaのn_buffer_segments
をまとめて取得」ではGroongaのdump
の結果からインデックスカラム名を得ました。
実は次のようなSQLでもインデックスカラム名を得ることができ、合わせてn_buffer_segments
も得ることができます。
クエリ例:
SELECT
i.relname AS index_name,
t.relname AS table_name,
am.amname AS index_type,
(pgroonga_command(
'object_inspect',
ARRAY[
'name', pgroonga_index_column_name(i.relname::cstring, a.attname)
]
)::jsonb)->1->'value'->'statistics'->'n_buffer_segments' AS n_buffer_segments
FROM
pg_class t
JOIN pg_index ix ON t.oid = ix.indrelid
JOIN pg_class i ON i.oid = ix.indexrelid
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
JOIN pg_am am ON i.relam = am.oid
WHERE
t.relkind = 'r' AND
am.amname = 'pgroonga';
結果例:
index_name | table_name | index_type | n_buffer_segments
----------------+------------+------------+-------------------
pgroonga_index | memos | pgroonga | 0
pgroonga_index | memos | pgroonga | 0
pgroonga_index | memos | pgroonga | 0
(3 行)
例に使ったテーブルではこの方法でも期待通りに結果が得られましたが、そうではない場合があったので、上述のdump
の方式にしました。
どういう場合に期待通りの結果が得られないかは、ぜひみなさん考えてみてください!
おまけ: pgroonga ((ARRAY[title, content, tag]))
のとき
次のインデックスを考えます。式インデックスの場合です。
CREATE INDEX pgroonga_array_index
ON memos
USING pgroonga ((ARRAY[title, content, tag]));
このときpgroonga_index_column_name
関数の引数には何を渡せばよいでしょうか。
インデックス名はpgroonga_array_index
ですぐわかりますが、カラム名は何でしょうか。ARRAY[title, content, tag]
?
正解はarray
です。
SELECT pgroonga_index_column_name('pgroonga_array_index', 'array');
ただし直感的ではないので、本記事では触れませんでしたが、第2引数にカラムの位置を指定する構文を利用するのが無難です。
上述の例だと最初のカラムなので0
を指定します。
SELECT pgroonga_index_column_name('pgroonga_array_index', 0);