2012年12月9日

ウィンドウ関数を使ってブロック読み込み量の推移を見る

PostgreSQL Advent Calendar 2012(全部俺)のDay 9です。

前回は、PgPerfパッケージを使って、PostgreSQLの各種統計情報のスナップショットを取得・保存する方法を解説しました。

今回は、その保存したデータを分析する方法をご紹介します。

お題は「データベースのブロック読み込み発生の推移を分析する」です。

■使用するスナップショットテーブル


今回使用するスナップショットテーブルは、
  • pgperf.snapshot
  • pgperf.snapshot_pg_stat_database
の2つのテーブルです。前者には、スナップショットIDと取得日時が、後者にはデータベースごとのブロック読み込みの統計情報のスナップショットが保存されています。
SELECT * FROM pgperf.snapshot LIMIT 5;
SELECT * FROM pgperf.snapshot_pg_stat_database LIMIT 5;
これらのテーブルを分析することで、データベースのブロック読み込みの推移を確認してみます。

なお、これらのテーブルの構造の詳細についてはPgPerfパッケージのユーザーマニュアルを参照してください。

PgPerfパッケージユーザーマニュアル
http://www.uptime.jp/go/pgperf-snapshot/

■ブロック読み込みの推移をスナップショットから取得する


まず、スナップショットのテーブルからブロック読み込み(blks_read)の発生数を見てみます。
postgres=# SELECT sid,datname,blks_read FROM pgperf.snapshot_pg_stat_database;
 sid |       datname       | blks_read
-----+---------------------+-----------
   2 | template1           |         0
   2 | template0           |         0
   2 | postgres            |     58122
   2 | pgbench             |         0
   2 | snaga               |       138
   2 | testdb              |         0
   2 | testdb0             | 172543546
   3 | template1           |         0
   3 | template0           |         0
   3 | postgres            |     74454
   3 | pgbench             |         0
   3 | snaga               |       138
   3 | testdb              |         0
   3 | testdb0             | 172543546
   4 | template1           |         0
   4 | template0           |         0
   4 | postgres            |     90749
   4 | pgbench             |         0
   4 | snaga               |       138
   4 | testdb              |         0
   4 | testdb0             | 172543546
   5 | template1           |         0
   5 | template0           |         0
   5 | postgres            |    107030
(...snip...)
まず、データベースごとに保存されているブロック読み込み数の値を、すべて合算してインスタンス全体のブロック読み込み数に変換します。スナップショットIDでGROUP BYしてSUMでblks_readを合計します。
postgres=# SELECT sid,sum(blks_read) FROM pgperf.snapshot_pg_stat_database GROUP BY sid ORDER BY sid;
 sid |    sum
-----+-----------
   2 | 537034870
   3 | 537051202
   4 | 537067497
   5 | 537083778
   6 | 537100021
(...snip...)

■ウィンドウ関数で差分を取得する


ここまで見てきてお分かりの通り、snapshot_pg_stat_databaseテーブルに保存されているブロック読み込みの数値は、過去の数値を積算した値です。つまり、各スナップショットの間隔で発生したブロック読み込み数は、これらの数値の差分ということになります。

例えば、ここで出ているスナップショットIDの2と3の間で発生したブロック読み込みの数は、

537051202 - 537034870 = 16332ブロック

となります。

差分を計算するためには「直前のスナップショットの値」を取得する必要があります。PostgreSQLではウィンドウ関数が使えますので、ウィンドウ関数である lag() を用いて「直前のスナップショットの値」を取得してみます。

ウィンドウ関数の詳細については、以下の記事を参照ください。

Window関数 - 導入編 - still deeper
http://www.chopl.in/blog/2012/12/01/window-function-tutorial/

Window関数 - Let's Postgres
http://lets.postgresql.jp/documents/technical/window_functions

ウィンドウ関数そのものの詳細については上記に譲るとして、ここでは前のレコードとの差分計算だけをやってみることにします。

SQLとしては以下のようなSQLになります。
SELECT sid,
       sum(blks_read),
       lag(sum(blks_read)) OVER (ORDER BY sid)
  FROM pgperf.snapshot_pg_stat_database
 GROUP BY sid
 ORDER BY sid;
このSQLを実行すると、以下のように「直前のスナップショットの値」が「lag」というカラムに取り込まれます。
postgres=# SELECT sid, sum(blks_read), lag(sum(blks_read)) OVER (ORDER BY sid)
  FROM pgperf.snapshot_pg_stat_database GROUP BY sid ORDER BY sid;
 sid  |    sum    |    lag
------+-----------+-----------
    2 | 537034870 |
    3 | 537051202 | 537034870
    4 | 537067497 | 537051202
    5 | 537083778 | 537067497
    6 | 537100021 | 537083778
    7 | 537116221 | 537100021
(...snip...)
ひとつ前のスナップショットの数値(sum)が、その次のスナップショットのカラム(lag)として取り込まれていることが分かります。

あとは、この2つのカラムを使って差分を計算するだけです。
SELECT sid,
       sum(blks_read) - lag(sum(blks_read)) OVER (ORDER BY sid) as "blks_read"
  FROM pgperf.snapshot_pg_stat_database
 GROUP BY sid
 ORDER BY sid;
上記のSQLを実行すると、以下のような結果を取得できます。
postgres=# SELECT sid,
       sum(blks_read) - lag(sum(blks_read)) OVER (ORDER BY sid) as "blks_read"
  FROM pgperf.snapshot_pg_stat_database
 GROUP BY sid ORDER BY sid;
 sid  | blks_read
------+-----------
    2 |
    3 |     16332
    4 |     16295
    5 |     16281
    6 |     16243
    7 |     16200
(...snip...)
これを見ると、スナップショットIDが2から3の間では16332ブロックの読み込みが発生し、その後も同じくらいの読み込みが発生していることが分かります。

■秒単位の数値に変換する


ここまでで、各スナップショット間で発生したブロック読み込みの数(の推移)を取得することができるようになりました。しかし、スナップショットの取得間隔は場合によって異なります。

負荷試験や検証などの場合には1分単位で取得しているかもしれませんし、運用中には1時間間隔かもしれません。そのため、最後に秒単位の数値に変換します。「ブロック読み込み/秒」ということです。

PgPerfパッケージでは、スナップショット間の「秒数」を取得するSQL関数 pgperf.get_interval() が提供されています。この「秒数」で先ほどのブロック読み込み数を除算することで、「ブロック読み込み/秒」を取得することができます。

以下の例は、スナップショットIDの2と3の間隔を秒数で取得している例です。
postgres=# SELECT pgperf.get_interval(2, 3);
 get_interval
--------------
          600
(1 row)

postgres=#
先ほどのウィンドウ関数を使えばスナップショットIDについても「直前の値」を取得することができますので、pgperf.get_interval()関数と併せて使うことによって秒単位の値に変換します。

また、スナップショットIDだけでは時刻が分かりませんので、pgperf.snapshotテーブルのsidを使ってJOINして、スナップショットの取得時刻(ts)を取得します。

上記の追加を行ったクエリが以下のものになります。
SELECT s.ts,
       ( sum(blks_read) - lag(sum(blks_read)) OVER (ORDER BY s.sid) ) /
         pgperf.get_interval(lag(s.sid) OVER (ORDER BY s.sid), s.sid) as "blks_read/sec"
  FROM pgperf.snapshot_pg_stat_database d, pgperf.snapshot s
 WHERE d.sid = s.sid
 GROUP BY s.sid
 ORDER BY s.ts;
このクエリを実行すると、以下のような結果が得られます。
postgres=# SELECT s.ts,
       ( sum(blks_read) - lag(sum(blks_read)) OVER (ORDER BY s.sid) ) /
         pgperf.get_interval(lag(s.sid) OVER (ORDER BY s.sid), s.sid) as "blks_read/sec"
  FROM pgperf.snapshot_pg_stat_database d, pgperf.snapshot s
 WHERE d.sid = s.sid
 GROUP BY s.sid
 ORDER BY s.ts;
             ts             |     blks_read/sec
----------------------------+-----------------------
 2012-11-21 18:20:01.238885 |
 2012-11-21 18:30:01.464424 |   27.2200000000000000
 2012-11-21 18:40:01.685642 |   27.1583333333333333
 2012-11-21 19:21:07.584854 |    6.6021897810218978
 2012-11-21 19:30:01.215188 |   30.4176029962546816
 2012-11-21 19:40:01.442753 |   27.0000000000000000
 2012-11-21 19:50:01.665997 |   26.9500000000000000
(...snip...)
このクエリによって、例えば '2012-11-21 18:30:01' にスナップショットを取得した際のブロック読み込みは「約27.22ブロック/秒」であったことが分かります。

■まとめ


今回は、PgPerfパッケージを使って取得したスナップショットのデータを時系列分析する方法を解説しました。

ウィンドウ関数を使うことによって、直前の値との差分を比較的簡単に取得できることを示し、pgperf.get_interval()関数で取得した秒数で除算することによって、簡単に秒単位の数値に変換できることを示しました。

今回は、もっとも簡単な(と思われる)ブロック読み込みについて解説しましたが、前回も紹介した通り、PgPerfパッケージを使うと、さまざまな統計情報が収集・蓄積されます。これらを分析することで、パフォーマンスについて、より深い知見を得られると思います。

ぜひ、PgPerfパッケージで蓄積したデータを分析して、PostgreSQLの安定運用やパフォーマンス管理に役立てていただければと思います。

では、また。

0 件のコメント:

コメントを投稿