Quantcast
Channel: PostgreSQL Deep Dive
Viewing all 94 articles
Browse latest View live
↧

パフォヌマンス統蚈情報のスナップショットを取埗する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 8です。

今日は、PostgreSQLの統蚈情報を網矅的に収集・蓄積するPgPerfパッケヌゞの玹介をしたす。

このパッケヌゞは、本日リリヌスしたものです。

snaga/pgperf-snapshot
https://github.com/uptimejp/pgperf-snapshot

■PostgreSQLの統蚈情報の蚘録ず監芖


PostgreSQLでは、動䜜に぀いおの内郚の情報を取埗するために、さたざたなシステムビュヌやSQL関数がありたす。


統蚈情報コレクタ
http://www.postgresql.jp/document/9.0/html/monitoring-stats.html
システム管理関数
http://www.postgresql.jp/document/9.0/html/functions-admin.html

この情報を取埗するこずで、PostgreSQLの内郚でどのような凊理が行われおいるのかを知るこずができたす。

䞀方で、これらの内郚の統蚈情報は時々刻々ず倉化しおいくため、デヌタベヌスサヌバの運甚ずいう芳点で芋る堎合には、きちんず蚘録をしおおく必芁がありたす。

たた、デヌタベヌスの監芖やパフォヌマンスの分析にはさたざたな角床からこれらの内郚の統蚈情報を分析する必芁がありたすが、いざ䜕か問題が起こっお分析しようずするず、必芁な情報が取れおいない、ずいったこずが起こりえたす。

぀たり、デヌタベヌスの運甚を行う䞊では、埌に分析する際に必芁になる可胜性を考慮しお、「最初から、定期的に、網矅的に」情報を収集しおおくこずが重芁になりたす。

■パフォヌマンス統蚈情報を取埗・保存するPgPerfパッケヌゞ


PostgreSQLでは、いたたでにもいく぀かパフォヌマンス関連の統蚈情報を取埗したりレポヌトするツヌルが存圚しおいたした。

しかし、「動䜜させるための蚭定が耇雑である」、「ツヌルが耇雑でメンテナンスが難しい」、「特定のレポヌトを出すのに特化しおいる」ずいった課題がありたした。

今回リリヌスした「PgPerfパッケヌゞ」は、パフォヌマンス関連の統蚈情報を
  • 内郚のシステムビュヌなどのほが同じ圢匏で、
  • 皌働しおいるシステムに極力手を加えずに、
  • すべおを網矅的に取埗・蓄積し、
  • 埌から自由に分析できるようにする
ずいう目的のために開発されたものです。

そのため、実装の特城ずしおは、
  • スクリプトSQL、PL/pgSQLのみで動䜜するため、PostgreSQLの皌働しおいるプラットフォヌムに䟝存しない。
  • 各皮性胜情報を容易に取埗・保存するこずができ、蓄積したデヌタを自由に分析・掻甚するこずができる。
  • むンストヌルおよびアンむンストヌルが容易で、皌働しおいるPostgreSQLの蚭定を倉曎する必芁がない。
ずいったこずが挙げられたす。

pgstattupleやpg_stat_statementsなどのcontribモゞュヌルがむンストヌルされおいなくおも、デフォルトで取埗できる情報はすべお取埗したすし、これらのcontribモゞュヌルがむンストヌルされおいればそれらの情報も䜵せお取埗したす。

PgPerfパッケヌゞの詳现なマニュアルを読みたい方はこちらをご芧ください。

PgPerfパッケヌゞナヌザヌマニュアル
http://www.uptime.jp/go/pgperf-snapshot

■PgPerfパッケヌゞのむンストヌル


PgPerfパッケヌゞのむンストヌルは非垞に簡単です。各メゞャヌバヌゞョンに察応する pgperf_install.sql スクリプトを、contribモゞュヌルず同じようにデヌタベヌスに察しおむンストヌルするだけです。

[snaga@devsv02 pgperf-snapshot]$ psql -f pgperf_install91.sql testdb
BEGIN
CREATE SCHEMA
psql:pgperf_install91.sql:28: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "snapshot_pkey" for table "snapshot"
CREATE TABLE
CREATE FUNCTION
(...snip...)
CREATE FUNCTION
COMMIT
[snaga@devsv02 pgperf-snapshot]$
䜜成するず、デヌタベヌス内にpgperfずいうスキヌマが䜜成され、スナップショット取埗甚のテヌブルやSQL関数が䜜成されたこずが分かりたす。

[snaga@devsv02 pgperf-snapshot]$ psql testdb
psql (9.0.6, server 9.1.2)
Type "help" for help.

testdb=# SET search_path TO pgperf;
SET
testdb=# \d
List of relations
Schema | Name | Type | Owner
--------+-------------------------------------+-------+----------
pgperf | snapshot | table | postgres
pgperf | snapshot_pg_current_xlog | table | postgres
pgperf | snapshot_pg_locks | table | postgres
pgperf | snapshot_pg_relation_size | table | postgres
pgperf | snapshot_pg_stat_activity | table | postgres
pgperf | snapshot_pg_stat_bgwriter | table | postgres
pgperf | snapshot_pg_stat_database | table | postgres
pgperf | snapshot_pg_stat_database_conflicts | table | postgres
pgperf | snapshot_pg_stat_replication | table | postgres
pgperf | snapshot_pg_stat_statements | table | postgres
pgperf | snapshot_pg_stat_user_indexes | table | postgres
pgperf | snapshot_pg_stat_user_tables | table | postgres
pgperf | snapshot_pg_statio_user_indexes | table | postgres
pgperf | snapshot_pg_statio_user_tables | table | postgres
pgperf | snapshot_pg_statistic | table | postgres
pgperf | snapshot_pgstatindex | table | postgres
pgperf | snapshot_pgstattuple | table | postgres
(17 rows)

testdb=#

■スナップショットの取埗


それでは、むンストヌルしたPgPerfパッケヌゞを䜿っお、統蚈情報のスナップショットを取埗しおみたす。

スナップショットを取埗するには、pgpgerf.create_snapshot()ずいうSQL関数を䜿甚したす。

testdb=# SELECT pgperf.create_snapshot(1);
create_snapshot
-----------------
0
(1 row)

testdb=#
create_snapshot()関数には、スナップショット取埗レベルを指定する必芁がありたす。スナップショット取埗レベルは、そのデヌタの皮別やスナップショット取埗にかかる負荷に応じお、もっずも基瀎的な「1」から可胜な限りの情報を取埗する「5」たでレベル分けがされおいたす。

ここでは、たずはもっずも基本的なスナップショット取埗レベル1で取埗しおみたす。

■取埗したスナップショットの確認


取埗したスナップショットは、pgperfスキヌマ内にある「snapshot_」で始たる耇数のテヌブルに栌玍されたす。

pgperf.snapshotテヌブルは、スナップショット取埗時刻ずスナップショットIDの察応を保存するテヌブルです。このテヌブルを参照するず、い぀取埗したスナップショットなのか、スナップショットIDが䜕番なのか、ずいった情報を確認するこずができたす。

testdb=# SELECT * FROM pgperf.snapshot;
sid | ts
-----+----------------------------
0 | 2012-12-08 11:17:02.822046
(1 row)

testdb=#
各皮統蚈情報のデヌタそのものは、その他の「pgperf.snapshot_*」ずいうテヌブルに保存されたす。

䟋えば、バックグラりンドラむタの統蚈情報を取埗できるpg_stat_bgwriterシステムビュヌのスナップショットはpgperf.snapshot_pg_stat_bgwriterテヌブルに保存される、ずいった仕組みです。

testdb=# SELECT * FROM pgperf.snapshot_pg_stat_bgwriter ;
-[ RECORD 1 ]---------+------------------------------
sid | 0
checkpoints_timed | 136
checkpoints_req | 35
buffers_checkpoint | 1746118
buffers_clean | 0
maxwritten_clean | 0
buffers_backend | 1383900
buffers_backend_fsync | 0
buffers_alloc | 451660
stats_reset | 2012-11-30 13:29:50.000083+09

testdb=#
詳现に぀いおは、ナヌザマニュアルを参照しおください。

PgPerfパッケヌゞナヌザヌマニュアル
http://www.uptime.jp/go/pgperf-snapshot

■スナップショットの削陀


スナップショットを削陀するには、スナップショットIDを指定しおpgperf.delete_snapshot()関数を䜿いたす。指定したスナップショットIDのスナップショットデヌタを、すべおのスナップショットテヌブルから䞀括しお削陀しおくれたす。

testdb=# SELECT * FROM pgperf.snapshot;
sid | ts
-----+----------------------------
0 | 2012-12-08 11:17:02.822046
(1 row)

testdb=# SELECT pgperf.delete_snapshot(0);
delete_snapshot
-----------------
t
(1 row)

testdb=#

■スナップショットの消し蟌み


基本的にスナップショットのデヌタは蓄積しおいくものですが、ある䞀定の期間以䞊経過した叀いものは削陀をしたい、ずいった堎合もあるず思いたす。そのような堎合には、pgperf.purge_snapshots()関数を䜿いたす。

pgperf.purge_snapshots()関数は、匕数にINTERVAL型の倀を取り、指定した期間より叀いスナップショットを䞀括しお削陀したす。

以䞋の䟋は、取埗しおから32日以䞊経過したスナップショットを削陀しおいる䟋です。

testdb=# SELECT pgperf.purge_snapshots('32 days');
purge_snapshots
-----------------
12
(1 row)

testdb=#

■PgPerfパッケヌゞのアンむンストヌル


PgPerfパッケヌゞは、PL/pgSQLで䜜成されたSQL関数ずテヌブル類で構成されおおり、すべおpgperfスキヌマ内に䜜成されたす。

ですので、アンむンストヌルする堎合にはpgperfスキヌマをCASCADE指定でDROPするこずで、きれいにアンむンストヌルするこずができたす。pgperf_uninstall.sqlスクリプトは、この凊理を行っおくれたす。

[snaga@devsv02 pgperf-snapshot]$ psql -U postgres -f pgperf_uninstall.sql testdb
psql:pgperf_uninstall.sql:1: NOTICE: drop cascades to 50 other objects
DETAIL: drop cascades to table pgperf.snapshot
drop cascades to function pgperf._get_server_version()
drop cascades to function pgperf._check_function(name)
(...snip...)
drop cascades to function pgperf.create_snapshot_pgstatindex(integer)
drop cascades to function pgperf.delete_snapshot_pgstatindex(integer)
DROP SCHEMA
[snaga@devsv02 pgperf-snapshot]$

■たずめ


今回は、PostgreSQLのパフォヌマンス統蚈情報を取埗・蓄積する方法ずしお、PgPerfパッケヌゞの玹介ず、その基本的な䜿い方の解説をしたした。

PgPerfパッケヌゞは、むンストヌルするのも䜿っおみるのももちろんアンむンストヌルも非垞に簡単ですので、ぜひ詊しおみおいただければず思いたす。

明日は、このPgPerfパッケヌゞを䜿っお蓄積したスナップショットデヌタを、SQLを䜿っお分析する方法を埡玹介したす。

では、たた。
↧

りィンドり関数を䜿っおブロック読み蟌み量の掚移を芋る

$
0
0
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の安定運甚やパフォヌマンス管理に圹立おおいただければず思いたす。

では、たた。
↧
↧

Rを䜿っおパフォヌマンス統蚈情報を可芖化する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 10です。

前回たでに、PostgreSQLのパフォヌマンスデヌタを取埗・蓄積し、それをSQLを甚いお解析する方法を玹介しおきたした。

しかし、増えおいくデヌタを掻甚しお人間が䜕らかのアクションを取るためには、デヌタを䜕らかの圢で「可芖化」しお、人間が容易に読解・解釈できる圢に倉曎する必芁がありたす。

今回は解析結果を可芖化する方法に぀いお簡単に玹介したす。ツヌルずしおは、オヌプン゜ヌスの統蚈凊理゜フトりェアである「R」を利甚したす。

■R、および関連パッケヌゞのむンストヌル


たず、RのサむトからRのバむナリパッケヌゞをダりンロヌドしたす。

The R Project for Statistical Computing
http://www.r-project.org/

トップペヌゞの「Getting Started」の䞭の「download R」に進むずミラヌの䞀芧が出おきたすので、適圓なミラヌサむトを遞んだら「Download R for Windows」ずいうペヌゞから「base」を遞んでバむナリパッケヌゞをダりンロヌドしたすWindows版のRの最新版はバヌゞョン2.15.2のようです。

次に、DBIおよびRPostgreSQLのパッケヌゞをむンストヌルしたす。Rのパッケヌゞのむンストヌルは「CRAN」ずいう、Perlで蚀うずころのCPANのようなサむトからネットワヌク経由で盎接ダりンロヌドしおむンストヌルするこずができたす。

Rを起動しおコン゜ヌルを開いたら、以䞋のコマンドを実行したす。

> install.packages("DBI")
> install.packages("RPostgreSQL")
ミラヌの䞀芧が出お、適圓なミラヌサむトを遞択するずむンストヌルが始たりたす。

■ブロック曞き蟌み量を取埗するビュヌを䜜る


Rの準備ができたら、次はPostgreSQL偎の準備をしたす。

前回の゚ントリ「りィンドり関数を甚いたブロック読み蟌みの蚈枬」を応甚しお、今回は「ブロック曞き蟌み」に぀いお、バックグラりンドラむタ、チェックポむント、バック゚ンドプロセスによる曞き蟌みをそれぞれ時系列で取埗し、「曞き蟌みブロック数/秒」ずしお取埗したす。

たず、りィンドり関数を䜿っお各スナップショット取埗時の曞き出しブロック数のデヌタを取埗するためのク゚リを䜜成し、それをビュヌずしお䜜成したす。

CREATE VIEW pgperf.rpt_blkwrite AS
SELECT date_trunc('second', s.ts) AS "timestamp",
round( (( buffers_checkpoint - lag(buffers_checkpoint) OVER (ORDER BY s.ts) )::float /
pgperf.get_interval(lag(s.sid) OVER (ORDER BY s.ts), s.sid))::numeric, 2) AS "blks_cp/sec",
round( (( buffers_clean - lag(buffers_clean) OVER (ORDER BY s.ts) )::float /
pgperf.get_interval(lag(s.sid) OVER (ORDER BY s.ts), s.sid))::numeric, 2) AS "blks_bg/sec",
round( (( buffers_backend - lag(buffers_backend) OVER (ORDER BY s.ts) )::float /
pgperf.get_interval(lag(s.sid) OVER (ORDER BY s.ts), s.sid))::numeric, 2) AS "blks_be/sec"
FROM pgperf.snapshot s,
pgperf.snapshot_pg_stat_bgwriter b
WHERE s.sid=b.sid
AND s.ts BETWEEN date_trunc('day', now()) - '32 days'::interval
AND date_trunc('day', now()) + '1 day'::interval
ORDER BY s.ts;
ここで䜜成したpgperf.rpt_blkwriteビュヌはブロック曞き蟌みの数倀を過去32日分時系列衚瀺しおくれるもので、このビュヌに察しおSELECTを実行するず以䞋のような結果を取埗できたす。

postgres=# SELECT * FROM pgperf.rpt_blkwrite;
timestamp | blks_cp/sec | blks_bg/sec | blks_be/sec
---------------------+-------------+-------------+-------------
2012-12-09 15:45:48 | | |
2012-12-09 15:50:01 | 0.00 | 0.00 | 0.05
2012-12-09 16:00:01 | 0.00 | 0.00 | 0.00
2012-12-09 16:10:01 | 0.00 | 0.00 | 0.01
2012-12-09 16:20:01 | 0.00 | 0.00 | 0.00
(5 rows)

postgres=#
ここたで準備ができたら、次はRからこのデヌタを取埗しお時系列でグラフ衚瀺しおみたす。

■デヌタベヌスぞ接続しおク゚リを実行する


RからPostgreSQLぞのク゚リを実行するには、たず library() 関数を䜿っおRPostgreSQLパッケヌゞを読み蟌みたす。

> library("RPostgreSQL")
芁求されたパッケヌゞ DBI をロヌド䞭です
>
次に、デヌタベヌスぞのコネクションを䜜成したす。

> con <- dbConnect(PostgreSQL(), host="10.0.2.12", user= "postgres", password="postgres", dbname="postgres")
> con
<PostgreSQLConnection:(9088,3)>
>
これで con オブゞェクトにデヌタベヌスぞのコネクションオブゞェクトが䜜成されたした。

デヌタベヌスぞ接続できたら、ク゚リを発行しお結果セットを取埗したす。ここでは、先ほど䜜成したpgperf.rpt_blkwriteビュヌに察しおSELECTを実行したす。

> rs <- dbSendQuery(con, "SELECT * FROM pgperf.rpt_blkwrite")
特に゚ラヌ無くク゚リを実行できたらプロンプトが返っおきたら、ク゚リの発行は成功です。

■結果を取埗しお時系列のグラフを描画する


ク゚リを実行したら結果セットからデヌタをフェッチしたす。

> out <- fetch(rs)
> out
timestamp blks_cp/sec blks_bg/sec blks_be/sec
1 2012-12-09 15:50:01 NA NA NA
2 2012-12-09 16:00:01 0.00 0 0.00
3 2012-12-09 16:10:01 0.00 0 0.01
4 2012-12-09 16:20:01 0.00 0 0.00
5 2012-12-09 16:30:01 0.00 0 0.42
6 2012-12-09 16:40:01 51.36 0 0.00
(...snip...)
>
配列を衚瀺するず、きちんずデヌタを取埗できおいるこずが分かりたす。

最埌に、取り出したデヌタ out を䜿っお時系列のグラフを描画したす。

> ts.plot (out[2:4], gpars=list( ylab='blocks/sec', lty=c(1,1,1), col=c('green', 'red', 'blue')))
䞊蚘のts.plotを実行するず、以䞋のような時系列のグラフが衚瀺されたす。


ここでは、緑のラむンがチェックポむントによる曞き蟌み、赀のラむンがバックグラりンドラむタによる曞き蟌みで、青のラむンがバック゚ンドプロセスによる曞き蟌みです。

最埌に凡䟋を衚瀺しお完了です。

> legend('topright', c('Checkpoint','Bgwriter','Backend'), lty=c(1,1,1), col=c('green', 'red', 'blue'))
本来はX軞には時刻をプロットすべきなのですが、うたく衚瀺する方法を芋぀けられず今回は間に合いたせんでした。どなたかご存じの方がいたら教えおください。

■たずめ


今回は、前回たでに取埗したパフォヌマンス統蚈情報を、統蚈凊理゜フトりェアであるRを䜿っお可芖化する方法を玹介したした。

パフォヌマンスに関するデヌタを取埗・蓄積するこずももちろん重芁なのですが、そのデヌタをきちんず読み解いおそこから「解釈」するこずもデヌタを掻甚する䞊では欠かせたせん。「可芖化」ずいうのは、そのための非垞に重芁な方法のひず぀です。

Rだけでなく、デヌタを可芖化するツヌルにはさたざたなものがありたす。ぜひ、自分に合ったツヌルを芋぀けお、取埗・蓄積したパフォヌマンスデヌタを有効に掻甚しおいただければず思いたす。

では、たた。

■参考文献


Package 'RPostgreSQL'
http://cran.r-project.org/web/packages/RPostgreSQL/RPostgreSQL.pdf
ず時系列(1)
http://mjin.doshisha.ac.jp/R/33/33.html
Plotting Time-Series Objects
http://stat.ethz.ch/R-manual/R-devel/library/stats/html/plot.ts.html
↧

GrowthForecastでパフォヌマンス情報を可芖化する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 11です。

最近、ちょっずした可芖化ブヌムが蚪れおおりたす個人的に。

運甚管理を可芖化するツヌルやプラグむンもいろいろず公開されおいる昚今ですが、個人的にはちょっず前から「GrowthForecast」ずいうツヌルが気になっおいたしたので、今回はこれを䜿っおPostgreSQLを可芖化しおみようず思いたす。

■「GrowthForecast」ずは䜕か


「GrowthForecast」は @kazeburo氏の開発したグラフツヌルです。単独でWebサヌバずしお動䜜し、WebAPIを経由しお可芖化すべきデヌタの登録を受け付け、HTTPリク゚ストでグラフ化された画像デヌタを取埗するこずができる゜フトりェアです。

GrowthForecast - Lightning fast Graphing / Visualization
http://kazeburo.github.com/GrowthForecast/

䞀般的な運甚監芖ツヌルは、長期的に運甚監芖する堎合には䟿利でいいのですが、セットアップに手間がかかるし䜿いこなすのもいろいろ倧倉だずいうこずで、その蟺りを簡単に䜿えるようにしたい、ずいうツヌルです。

ずいうわけで、今回はこのGrowthForecastを䜿っおPostgreSQLの統蚈情報を可芖化しおみたす。

■GrowthForecastのむンストヌル


GrowthForecastのむンストヌルは、cpanmずいうツヌルを䜿っおむンストヌルを行いたす。

cpanmのむンストヌル方法に぀いおは以䞋を参照ください。

cpanmを䜿っおPerlモゞュヌルを入れる - メヌルの話題 | bounceHammer
http://bouncehammer.jp/ja/email-topics/2011/05/install-perl-modules-with-cpanm.html

CPANの䟝存するモゞュヌルをモリモリずむンストヌルするので、ネットに぀ながっおいない環境でむンストヌルするのは難しいず思いたすので、その点はご泚意ください。

cpanmの準備ができたら、GrowthForecastのむンストヌルを行いたす。

本来はcpanmコマンドで盎接URLを指定しおむンストヌルできるようですが、私の環境ではできなかったため、䞀旊、wgetでGrowthForecastのtar ballをダりンロヌドしおむンストヌルを行いたす。

[snaga@devsv02 gf]$ wget --no-check-certificate https://github.com/downloads/kazeburo/GrowthForecast/GrowthForecast-0.32.tar.gz
--2012-11-26 23:41:44-- https://github.com/downloads/kazeburo/GrowthForecast/GrowthForecast-0.32.tar.gz
Resolving github.com... 207.97.227.239
(...snip...)
Saving to: `GrowthForecast-0.32.tar.gz'

100%[=============================================>] 91,375 84.5K/s in 1.1s

2012-11-26 23:41:47 (84.5 KB/s) - `GrowthForecast-0.32.tar.gz' saved [91375/91375]

[snaga@devsv02 gf]$ ls
GrowthForecast-0.32.tar.gz
[snaga@devsv02 gf]$ su
Password:
[root@devsv02 gf]# cpanm -n GrowthForecast-0.32.tar.gz
Building GrowthForecast-0.32 ... OK
Successfully installed GrowthForecast-0.32
111 distributions installed
[root@devsv02 gf]#

■GrowthForecastサヌバの起動


むンストヌルが終わったら、GrowthForecastのサヌバプロセスを起動したす。

[snaga@devsv02 ~]$ /usr/bin/growthforecast.pl --data-dir /tmp/gfdata
23:15:47 1.1 | 2012-11-27T23:15:47 [INFO] [short] first updater start in Tue Nov 27 23:16:00 2012 at /usr/lib/perl5/site_perl/5.8.8/GrowthForecast/Worker.pm line 55
23:15:47 2.1 | 2012-11-27T23:15:47 [INFO] [update] first updater start in Tue Nov 27 23:20:00 2012 at /usr/lib/perl5/site_perl/5.8.8/GrowthForecast/Worker.pm line 55
この時、GrowthForecastのサヌバはデフォルトのポヌト番号5125で埅ち受けおいたすので、ブラりザで接続できるかどうかを確認したす。


空のGrowthForecastのペヌゞが衚瀺されればGrowthForecastサヌバの起動は成功です。

■GrowthForecastぞのデヌタの登録


GrowthForecastぞのデヌタの登録は、GrowthForecastのWebAPIを経由しお行いたす。

以䞋のようなURLの曞匏になりたす。

http://ホスト名:ポヌト番号/api/サヌビス名/セクション名/グラフ名
䟋えば、IPアドレス 10.0.2.12 のGrowthForecastサヌバに察しお、PostgreSQLの接続セッション数をグラフにするには、以䞋のようなURLになるでしょう。

http://10.0.2.12:5125/api/postgres/database/session
このURLに察しおPOSTメ゜ッドでフォヌムデヌタを送信するこずで、GrowthForecastぞのデヌタ登録を行うこずができたす。

任意のURLにPOSTメ゜ッドでフォヌムデヌタを送信できるツヌルはいろいろありたすが、ここではcURLを䜿いたす。

たず、psqlを䜿っおブロック読み蟌み数をpg_stat_databaseシステムビュヌから取埗したす。そしお、その結果を "number" ずいう名前のフォヌム倉数に蚭定しおPOSTメ゜ッドで送信したす。

[snaga@devsv02 gf]$ psql -c 'select count(*) from pg_stat_activity' postgres count
-------
1
(1 row)

[snaga@devsv02 gf]$ psql -A -t -c 'select count(*) from pg_stat_activity' postgres
1
[snaga@devsv02 gf]$ curl -F number=`psql -A -t -c 'select count(*) from pg_stat_activity' postgres` http://10.0.2.12:5125/api/postgres/database/session
{"error":0,"data":{"number":1,"llimit":-1000000000,"mode":"gauge","stype":"AREA","adjustval":"1","meta":"","service_name":"postgres","gmode":"gauge","color":"#3399cc","created_at":"2012/11/29 19:00:14","section_name":"database","ulimit":1000000000,"id":2,"graph_name":"session","description":"","sulimit":100000,"unit":"","sort":0,"updated_at":"2012/11/29 19:00:14","adjust":"*","type":"AREA","sllimit":-100000,"md5":"c81e728d9d4c2f636f067f89cc14862c"}}
[snaga@devsv02 gf]$
このようにデヌタを登録するず、Webブラりザからグラフを芋るこずができるようになりたす。


このような「情報の取埗→フォヌム経由の送信」を定期的に行うこずで、GrowthForecastでは時系列のグラフ生成を可胜にしたす。

■バッファの曞き出しをグラフ化する


セッション数のような基本的な数倀をグラフ化できたら、次は応甚線ずしお共有バッファのブロック曞き出しをグラフ化しおみたす。

バッファのブロックの曞き出しは、以䞋のSQLで取埗できたすので、これで取埗できる数倀を甚いたす。

SELECT buffers_checkpoint FROM pg_stat_bgwriter;
SELECT buffers_clean FROM pg_stat_bgwriter;
SELECT buffers_backend FROM pg_stat_bgwriter;
buffers_checkpointはチェックポむント凊理で曞き出されたブロック数、bufers_cleanはバックグラりンド凊理で曞き出したブロック数、buffers_backendはバック゚ンドがバッファの入れ替えに際しお曞き出したブロック数になりたす。

これらはいずれも過去の数倀に積算されおいくもので、か぀「ブロック曞き出し」ずいう同じカテゎリの数倀になりたすので、ブロック曞き出しのグラフは
  • 差分を蚈算する
  • 耇数のグラフを積み䞊げる
ずいう圢匏にしおみたす。

たず、先ほどのセッション数ず同じように、GrowthForecastサヌバにデヌタを登録しお䞉皮類のグラフが衚瀺されるずころたでを行いたす。

curl -F number=`psql -A -t \
-c 'SELECT buffers_checkpoint FROM pg_stat_bgwriter' postgres` \
http://10.0.2.12:5125/api/postgres/database/buffers_checkpoint

curl -F number=`psql -A -t \
-c 'SELECT buffers_clean FROM pg_stat_bgwriter' postgres` \
http://10.0.2.12:5125/api/postgres/database/buffers_clean

curl -F number=`psql -A -t \
-c 'SELECT buffers_backend FROM pg_stat_bgwriter' postgres` \
http://10.0.2.12:5125/api/postgres/database/buffers_backend
グラフは衚瀺されるようになりたしたが、このたただず「過去のブロック曞き出し数の総数」が衚瀺されおしたうので、差分を衚瀺するように倉曎しなければなりたせん。

たず、各グラフの「蚭定」のペヌゞを開いお、「グラフのタむプ」を「実瞟」から「差分」に倉曎したす。


3぀のグラフを差分衚瀺に倉曎したら、最埌にこれら3぀のグラフをひず぀の耇合グラフにたずめたす。


たず、「耇合グラフの远加」を開きたす。

最初に䜜成する耇合グラフの「パス」を指定したす。ここでは「buffers_written」ずしたす。

次にグラフの系列を远加しおいきたす。今回は、系列1にbuffers_clean、系列2にbuffers_checkpoint、系列3にbuffers_backendを远加したす。それぞれ、「モヌド差分」、「スタックする」ずしお登録したす。

最埌に「远加」ずしお登録するず、耇合グラフが新しいグラフずしお登録されたす。

■たずめ


今回は「PostgreSQLの動䜜を可芖化」シリヌズずしお、GrowthForecastを䜿った可芖化を詊しおみたした。

GrowthForecast自䜓は非垞に簡単で䜿いやすいツヌルでしたので、PostgreSQLを䜿っおいる方もお手軜に可芖化を詊せるのではないかず思いたす。興味を持った方は、これを機䌚にぜひ詊しおみおください。

では、たた。
↧

HinemosでPostgreSQLの性胜を監芖する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 12です。

「最近個人的に可芖化ブヌムが蚪れおいる」ずいう話を前回の゚ントリで曞きたした。

パフォヌマンスに関連するデヌタを可芖化しおくれるツヌルにはさたざたな皮類があり、いろいろず目移りしおしたうのですが、今回はオヌプン゜ヌスの運甚管理ツヌル「Hinemos」でPostgreSQLのパフォヌマンス情報の可芖化を行う方法を玹介したす。

■HinemosずPostgreSQL性胜監芖


Hinemosはオヌプン゜ヌスの統合運甚管理゜フトりェアです。システム監芖機胜やゞョブ管理機胜の機胜等を備えおおり、システムの運甚管理をサポヌトしおくれる゜フトりェアになりたす。

Hinemosコンピュヌタ、システム、ネットワヌクの運甚管理を実珟するオヌプン゜ヌス゜フトりェア(OSS)
http://www.hinemos.info/

先日、この「Hinemosぞのアドオン」ずいう圢で、PostgreSQLの性胜情報を取埗・蓄積・可芖化・監芖するこずができるツヌルを䜜成したした。

Hinemos PostgreSQL性胜監芖オプション
http://www.uptime.jp/ja/products-services/hinemos-postgres-addon/

このツヌルは、簡単に蚀うず「少し倧きめのスクリプト」なのですが、オヌプン゜ヌスで公開されおおり、このスクリプトずHinemosを組み合わせるこずによっお、比范的簡単にPostgreSQLの性胜情報を可芖化するこずができたす。

動䜜の前提条件ずしおは、
  • Hinemosのバヌゞョンが4.0以降であるこず
  • PostgreSQLサヌバでHinemos゚ヌゞェントが動䜜しおいるこず
  • PostgreSQLサヌバがUnix系サヌバであり、Perlが動䜜するこず
くらいです。

■むンストヌル


Hinemosそのものマネヌゞャや゚ヌゞェント、クラむアントのむンストヌルから説明するず非垞に長くなっおしたいたすので、ここではHinemos自䜓のむンストヌル方法は割愛したす。Hinemosそのもののむンストヌルに぀いおは、公匏のマニュアル等を参照しおください。

Hinemosコンピュヌタ、システム、ネットワヌクの運甚管理を実珟するオヌプン゜ヌス゜フトりェア(OSS)
http://www.hinemos.info/

本゚ントリでは、Hinemosマネヌゞャ、Hinemos゚ヌゞェント、Hinemosクラむアントのむンストヌルが完了し、動䜜しおるこずを前提ずしお解説を進めたす。

PostgreSQL監芖オプションの゜ヌスコヌドはGitHubで配垃されおいたすので、たずはそちらからダりンロヌドしたす。

uptimejp/hinemos-postgres-addon
https://github.com/uptimejp/hinemos-postgres-addon

今回は11月14日のリリヌス版である hinemos-postgres-addon-r20121114.zip を䜿いたす。

[snaga@devsv02 hinemos]$ wget --no-check-certificate https://github.com/downloads/uptimejp/hinemos-postgres-addon/hinemos-postgres-addon-r20121114.zip
--2012-12-04 01:44:39-- https://github.com/downloads/uptimejp/hinemos-postgres-addon/hinemos-postgres-addon-r20121114.zip
(...snip...)
Length: 13016 (13K) [application/zip]
Saving to: `hinemos-postgres-addon-r20121114.zip'

100%[======================================>] 13,016 30.1K/s in 0.4s

2012-12-04 01:44:49 (30.1 KB/s) - `hinemos-postgres-addon-r20121114.zip' saved [13016/13016]

[snaga@devsv02 hinemos]$
ZIPファむルをダりンロヌドしたら、ZIPを解凍し、make installを実行しお mon_pgsql スクリプトをむンストヌルしたす。スクリプトは /opt/hinemos_agent/bin にむンストヌルされたす。

[snaga@devsv02 hinemos]$ unzip hinemos-postgres-addon-r20121114.zip
Archive: hinemos-postgres-addon-r20121114.zip
creating: hinemos-postgres-addon-r20121114/
inflating: hinemos-postgres-addon-r20121114/test_mon_pgsql.sh
inflating: hinemos-postgres-addon-r20121114/LICENSE
inflating: hinemos-postgres-addon-r20121114/Makefile
inflating: hinemos-postgres-addon-r20121114/mon_pgsql
[snaga@devsv02 hinemos]$ cd hinemos-postgres-addon-r20121114
[snaga@devsv02 hinemos-postgres-addon-r20121114]$ ls -l
total 48
-rw-rw-r-- 1 snaga snaga 18092 Nov 14 13:12 LICENSE
-rw-rw-r-- 1 snaga snaga 682 Nov 14 13:12 Makefile
-rwxrwxr-x 1 snaga snaga 19695 Nov 14 13:12 mon_pgsql*
-rwxrwxr-x 1 snaga snaga 441 Nov 14 13:12 test_mon_pgsql.sh*
[snaga@devsv02 hinemos-postgres-addon-r20121114]$ su
Password:
[root@devsv02 hinemos-postgres-addon-r20121114]# make install
install -m 755 mon_pgsql /opt/hinemos_agent/bin
[root@devsv02 hinemos-postgres-addon-r20121114]#
むンストヌルが完了したら、mon_pgsqlスクリプトを実行しおみたす。

mon_pgsqlスクリプトはHinemos゚ヌゞェントから呌び出される倖郚コマンドになっおいたす。そのため、コマンドラむンで実行しお動䜜確認するこずが可胜です。

[postgres@devsv02 ~]$ /opt/hinemos_agent/bin/mon_pgsql

Usage: /opt/hinemos_agent/bin/mon_pgsql [<option>...] <search_key>

Options:
-h <host> Host name to connect the database.
-p <port> Port number to connect the database.
-U <user> User name used to connect the database.
-d <dbname> Database name to be connected.
-v Verbose output.

Search keys:
session
cache_hit
tuple_wrtn
tuple_read
blks_read
blks_wrtn
txn
xlog_wrtn
dbsize
locks

[postgres@devsv02 ~]$
䞊蚘のように、mon_pgsqlスクリプトはPostgreSQLデヌタベヌスぞの接続甚のオプションを受付、か぀「どのようなデヌタを取埗するか」ずいうキヌsearch keyをコマンドラむンオプションずしお受け付けたす。

受け付けるキヌは以䞋の通りです。
  • sessionセッション数
  • cache_hitキャッシュヒット率
  • tuple_wrtnタプル曎新INSERT/UPDATE/DELETE
  • tuple_readタプル読み蟌みRETURN/FETCH
  • blks_readブロック読み蟌み
  • blks_wrtnブロック曞き蟌み
  • txnトランザクション数
  • xlog_wrtnWAL曞き蟌み量
  • dbsizeオブゞェクトサむズ
  • locksロック
䟋えば、珟圚のセッション数を取埗したい堎合には、キヌに「session」を指定しお実行するず、以䞋のように、アクティブ、アむドル、ロック埅ちのそれぞれのセッション数を取埗するこずができたす。

[postgres@devsv02 ~]$ /opt/hinemos_agent/bin/mon_pgsql -h localhost -p 5432 -U postgres -d postgres session
active,1
idle,1
wait,0
[postgres@devsv02 ~]$
ここたで動䜜確認できたら、いよいよHinemosぞの登録ず、Hinemos䞊でのデヌタの可芖化に進みたす。

■Hinemosでのリポゞトリぞのノヌド登録


たず、「リポゞトリノヌド」のビュヌを開き、「リポゞトリノヌドの䜜成・倉曎]」ダむアログからサヌバそのものをリポゞトリに登録したす。この手順は、PostgreSQLサヌバに限らず、Hinemosで監芖するサヌバに共通のステップです。

■Hinemosでのカスタム監芖の登録


次に、「性胜䞀芧」ビュヌを開き、監芖の远加を行いたす。

「監芖皮別」ずしおは「カスタム監芖数倀」を遞択したす。



そしお、「カスタム監芖䜜成・倉曎」のダむアログでPostgreSQL監芖甚の蚭定を行いたす。基本的な蚭定項目ず蚭定内容は以䞋の通りです。
  • 監芖項目ID監芖の蚭定を識別する任意のIDを指定したす。
  • スコヌプ監芖察象スコヌプを指定したす。
  • 間隔監芖の間隔を指定したす。
  • 実効ナヌザmon_pgsqlスクリプトを実行するOSナヌザを指定したす。
  • コマンドmon_psgqlスクリプトを実行するコマンドラむンを指定したす。
  • 監芖刀定情報、譊告、危険監芖で蚭定する閟倀を入力したす。
  • 監芖アプリケヌションアプリケヌション名を蚘入したす。
  • 収集収集倀衚瀺名収集・蓄積するデヌタの名称を蚘入したす。
  • 収集収集倀単䜍収集・蓄積するデヌタの単䜍を蚘入したす。
以䞋はPostgreSQLのセッション数を監芖する蚭定をしおいる䟋です。



監芖の蚭定を行うず、「監芖蚭定䞀芧」のビュヌに蚭定した監芖項目の䞀芧が衚瀺されたす。

■Hinemosでの性胜情報の衚瀺


監芖蚭定を行うず、「性胜䞀芧」のビュヌに性胜情報を収集する項目の䞀芧が衚瀺されたす。



ここで右䞊のアむコンから「グラフの远加」を遞択するず、「性胜」のビュヌが䜜成され、グラフの衚瀺が出おきたすので、「衚瀺皮別」で「デバむス別衚瀺」を遞択すれば、グラフの衚瀺は完了です。

mon_pgsqlコマンドで耇数皮別の項目が出力される堎合セッションであればactive/idle/waitなどには「デバむス別衚瀺」を遞択する必芁がありたす。

「グラフ皮別」に぀いおは、「折れ線グラフ」か「積み䞊げ面グラフ」かは自由に遞べたす。

■たずめ


今回は、PostgreSQLの性胜情報の収集・可芖化・監芖ずいうこずで、HinemosによるPostgreSQLのモニタリングをご玹介したした。

デヌタベヌスは、その性質䞊、性胜や可甚性に぀いおシビアに芋られるこずが倚いず思いたす。既存の゜リュヌションずうたく組み合わせながら、うたく運甚管理をしおいただければず思いたす。

では、たた。
↧
↧

AWSでそこそこセキュアにPostgreSQLむンスタンスを立ち䞊げる

$
0
0
4/5远蚘アクセス管理関連の脆匱性が発芋されおいたす。
バヌゞョン9.2.3、9.1.8、9.0.12、8.4.16およびそれより前のバヌゞョンをお䜿いの堎合はアップグレヌドしおください。特にクラりド環境で利甚する際にはご泚意ください。



PostgreSQL Advent Calendar 2012党郚俺のDay 13です。

クラりドが倧流行䞭です。個人的に

いろいろ詊したり、ちょっずデヌタを突っ蟌んで分析したり、ずいった甚途に䜿う堎合には、クラりド䞊に自分専甚のPostgreSQLが動いおいるず䜕かず䟿利です。

PaaSサヌビスを䜿うのも良いのですが、IaaS䞊に自分でPostgreSQLをむンストヌルした方が自由床は高くなりたすので、今回はAmazon EC2䞊にPostgreSQLをむンストヌルしお、自分専甚のサヌバずしお䜿う蚭定をしおみたす。

なお、むンタヌネット䞊をデヌタが流れる関係䞊、倚少、セキュリティに配慮した蚭定を行おうず思いたす。ずは蚀っおも、ポヌト番号を倉えおSSL接続を行うだけですが・・

前提ずしお、むンタヌネット䞊のサヌバの任意のポヌトに察しおTCP接続を匵れるこずが必芁です。ファむダヌりォヌルなどでネットワヌク接続が制限されおいる堎合にはこの方法は䜿えたせん。

■Amazon EC2でむンスタンスを立ち䞊げる


たず、Amazon EC2でむンスタンスを立ち䞊げたす。

EC2のセットアップから説明しおいるず非垞に長くなるため詳现は省きたすが、今回は以䞋の蚭定で立ち䞊げたす。

リヌゞョンは「APAC -Tokyo」ずしお、「Amazon Linux AMI 2012.09」を起動したす。アヌキテクチャはずりあえず32ビットずしたす。
  • Instance Type : T1 Micro
  • Availability Zone : No Preference
  • Key Pair : Create a new Key Pair たたは既存のものがあればそれを遞ぶ
セキュリティグルヌプでは、SSHの接続ず、PostgreSQLの接続のみを受け付ける蚭定を行いたす。PostgreSQLのポヌト番号は、デフォルトの「5432」ではなく、ここでは「16543」を䜿いたす。
  • Security Group : Create a new Security Group
  • Group Name : test-pgsql
  • Group Description : test-pgsql
    • Create a new rule : SSH
    • Source : 0.0.0.0/0 たたは制限できるのであれば指定する
    • Create a new rule : Custom TCP rule
    • Port range : 16543
    • Source : 0.0.0.0/0 たたは制限できるのであれば指定する
むンスタンスを起動したら、ec2-userでログむンしたす。

login as: ec2-user
Authenticating with public key "imported-openssh-key"

__| __|_ )
_| ( / Amazon Linux AMI
___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2012.09-release-notes/
There are 4 security update(s) out of 33 total update(s) available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-152-167-242 ~]$
ログむンしたら、たず yum update を行いたす。

[ec2-user@ip-10-152-167-242 ~]$ sudo su
[root@ip-10-152-167-242 ec2-user]# yum update
Loaded plugins: priorities, security, update-motd, upgrade-helper
Setting up Update Process
Resolving Dependencies
(...snip...)
ruby-libs.i686 0:1.8.7.371-1.20.amzn1 tzdata.noarch 0:2012f-1.15.amzn1
tzdata-java.noarch 0:2012f-1.15.amzn1 yum.noarch 0:3.2.29-30.24.amzn1

Complete!
[root@ip-10-152-167-242 ec2-user]#
次に、PostgreSQLをむンストヌルする際に䟝存するパッケヌゞをむンストヌルしたす。今回は、libxsltずuuidが必芁になりたすので、これらをyumでむンストヌルしたす。

[root@ip-10-152-167-242 ~]# yum install libxslt.i686 uuid.i686
Loaded plugins: priorities, security, update-motd, upgrade-helper
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package libxslt.i686 0:1.1.26-2.7.amzn1 will be installed
---> Package uuid.i686 0:1.6.2-11.16.amzn1 will be installed
(...snip...)
Installed:
libxslt.i686 0:1.1.26-2.7.amzn1
uuid.i686 0:1.6.2-11.16.amzn1

Complete!
[root@ip-10-152-167-242 ~]#

■PostgreSQLをむンストヌルする


次にPostgreSQLのRPMをむンストヌルしたす。

Amazon Linux AMIはRed Hat Enterprise Linux 6系のようですので、RHEL6甚のPostgreSQL 9.2のRPMパッケヌゞをダりンロヌドしたす。

ダりンロヌド甚のスクリプト dl.sh をGistに眮きたしたので、これを実行しおPostgreSQLのRPMをダりンロヌドしたす。

https://gist.github.com/4273726

[root@ip-10-152-167-242 ~]# sh dl.sh
--2012-11-24 09:15:20-- http://yum.postgresql.org/9.2/redhat/rhel-6.3-i386/postgresql92-9.2.1-1PGDG.rhel6.i686.rpm
Resolving yum.postgresql.org... 98.129.198.114
Connecting to yum.postgresql.org|98.129.198.114|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 959964 (937K) [application/x-redhat-package-manager]
Saving to: “postgresql92-9.2.1-1PGDG.rhel6.i686.rpm”

100%[======================================>] 959,964 634K/s in 1.5s

2012-11-24 09:15:22 (634 KB/s) - “postgresql92-9.2.1-1PGDG.rhel6.i686.rpm” saved [959964/959964]

(...snip...)

2012-11-24 09:15:48 (702 KB/s) - “postgresql92-test-9.2.1-1PGDG.rhel6.i686.rpm” saved [1275524/1275524]

[root@ip-10-152-167-242 ~]# ls *.rpm
postgresql92-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-contrib-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-debuginfo-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-devel-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-docs-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-libs-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-plperl-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-plpython-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-pltcl-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-server-9.2.1-1PGDG.rhel6.i686.rpm
postgresql92-test-9.2.1-1PGDG.rhel6.i686.rpm
[root@ip-10-152-167-242 ~]#
このうち、以䞋の5぀のRPMをむンストヌルしたす。
  • postgresql92-9.2.1-1PGDG.rhel6.i686.rpm
  • postgresql92-contrib-9.2.1-1PGDG.rhel6.i686.rpm
  • postgresql92-devel-9.2.1-1PGDG.rhel6.i686.rpm
  • postgresql92-libs-9.2.1-1PGDG.rhel6.i686.rpm
  • postgresql92-server-9.2.1-1PGDG.rhel6.i686.rpm

[root@ip-10-152-167-242 ~]# rpm -ivh postgresql92-9.2.1-1PGDG.rhel6.i686.rpm postgresql92-contrib-9.2.1-1PGDG.rhel6.i686.rpm postgresql92-devel-9.2.1-1PGDG.rhel6.i686.rpm postgresql92-libs-9.2.1-1PGDG.rhel6.i686.rpm postgresql92-server-9.2.1-1PGDG.rhel6.i686.rpm
warning: postgresql92-9.2.1-1PGDG.rhel6.i686.rpm: Header V4 DSA/SHA1 Signature, key ID 442df0f8: NOKEY
Preparing... ########################################### [100%]
1:postgresql92-libs ########################################### [ 20%]
2:postgresql92 ########################################### [ 40%]
3:postgresql92-contrib ########################################### [ 60%]
4:postgresql92-devel ########################################### [ 80%]
5:postgresql92-server ########################################### [100%]
[root@ip-10-152-167-242 ~]#

■デヌタベヌスクラスタを初期化する


PostgreSQLのむンストヌルが終わったら、デヌタベヌスクラスタを初期化したす。おなじみのinitdbコマンドの出番です。

[root@ip-10-152-167-242 ~]# su - postgres
-bash-4.1$ pwd
/var/lib/pgsql
-bash-4.1$ ls -F
9.2/
-bash-4.1$ /usr/pgsql-9.2/bin/initdb -D /var/lib/pgsql/9.2/data --no-locale -E UTF-8
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "C".
The default text search configuration will be set to "english".

fixing permissions on existing directory /var/lib/pgsql/9.2/data ... ok
(...snip...)

Success. You can now start the database server using:

/usr/pgsql-9.2/bin/postgres -D /var/lib/pgsql/9.2/data
or
/usr/pgsql-9.2/bin/pg_ctl -D /var/lib/pgsql/9.2/data -l logfile start

-bash-4.1$

■サヌバの鍵ず蚌明曞を䜜成する


次にSSL接続甚のサヌバの鍵ずサヌバ蚌明曞を䜜成したす。

-bash-4.1$ openssl genrsa -out server.key 1024
Generating RSA private key, 1024 bit long modulus
.........................................++++++
....++++++
e is 65537 (0x10001)
-bash-4.1$ openssl req -new -key server.key -x509 -days 365 -out server.crt
(...snip...)
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) [Default City]:Minato-ku
Organization Name (eg, company) [Default Company Ltd]:Uptime Technologies, LLC
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:ec2-54-248-9-93.ap-northeast-1.compute.amazonaws.com
Email Address []:nobody@uptime.jp
-bash-4.1$ ls -F
9.2/ server.crt server.key
-bash-4.1$
サヌバの公開鍵ず蚌明曞を䜜成したら、デヌタベヌスクラスタのディレクトリに移動させたす。

-bash-4.1$ chmod 600 server.*
-bash-4.1$ mv server.* 9.2/data/
-bash-4.1$ cd 9.2/data/
-bash-4.1$ ls -F
base/ pg_ident.conf pg_serial/ pg_tblspc/ postgresql.conf
global/ pg_log/ pg_snapshots/ pg_twophase/ postmaster.opts
pg_clog/ pg_multixact/ pg_stat_tmp/ PG_VERSION server.crt
pg_hba.conf pg_notify/ pg_subtrans/ pg_xlog/ server.key
-bash-4.1$

■PostgreSQLにSSLの蚭定行い、サヌビスを起動する


postgresql.confファむルの䞭で以䞋の蚭定を有効にしたす。

listen_addresses = '*'
port = 16543
ssl = on
ssl_cert_file = 'server.crt'
ssl_key_file = 'server.key'
䞊蚘以倖の蚭定log_line_prefixなども必芁に応じお行いたす。

次に、むンタヌネットからの接続に察しお、すべおSSL接続を芁求するようにpg_hba.confの最埌の行に以䞋を远加したす。

hostssl all all 0.0.0.0/0 password
そしお、サヌビスを起動するスクリプトで蚭定するポヌト番号を倉曎するために、/etc/sysconfig/pgsql/postgresql-9.2 に以䞋を蚘述したす。

PGPORT=16543
最埌に、サヌバ起動時に自動的に起動されるようにサヌビススクリプトを蚭定しお、PostgreSQLサヌビスを起動したす。

[root@ip-10-152-167-242 ~]# /sbin/chkconfig postgresql-9.2 on
[root@ip-10-152-167-242 ~]# /sbin/chkconfig --list postgresql-9.2
postgresql-9.2 0:off 1:off 2:on 3:on 4:on 5:on 6:off
[root@ip-10-152-167-242 ~]# /etc/rc.d/init.d/postgresql-9.2 start
Starting postgresql-9.2 service: [ OK ]
[root@ip-10-152-167-242 ~]#

■ナヌザを䜜成し、パスワヌドを蚭定する


PostgreSQLを起動したら、ナヌザの䜜成を行いたす。

ここでは、デヌタベヌス䜜成暩限のある䞀般ナヌザずしお snaga ずいうナヌザを䜜成しおいたす。--pwpromptオプションも指定しお、パスワヌドの蚭定も行いたす。

[root@ip-10-152-167-242 ~]# su - postgres
-bash-4.1$ createuser -p 16543 --createdb --encrypted --pwprompt snaga
Enter password for new role:
Enter it again:
-bash-4.1$
ナヌザの䜜成が完了したら、サヌバ偎のセットアップは完了です。

■pgAdminIIIから接続する


それでは、手元のクラむアントからむンタヌネットを経由しお、PostgreSQLサヌバに接続しおみたす。

ずは蚀え、特に倉わった蚭定は䞍芁で、接続先のホストにEC2のホスト名を指定しお、Portに先ほどの16543を指定するだけです。


接続に成功すれば、赀い「×」アむコンが消えお、サヌバの情報を取埗できるようになりたす。ここで、右偎のペむンに衚瀺されおいる「暗号」の項目が「SSL暗号化」ずなっおいれば、SSL接続できおいるこずになりたす。


なお、サヌバ蚭定のダむアログでSSLを「無効」ずするず接続できなくなりたす。「必ずSSL接続がされおいる」こずを確認するためにも、この確認もしおおくず良いでしょう。

■たずめ


今回はAmazonのEC2䞊でPostgreSQLを立ち䞊げお、安党に接続する方法を解説しおきたした。

自分でいろいろず詊すためにも、クラりド䞊で自由にPostgreSQLを利甚できるず非垞に䟿利です。ぜひ、クラりドの利䟿性ずセキュリティをバランスさせながら、うたく䜿っおみおいただければず思いたす。

ではでは。
↧

pg_receivexlogでリアルタむムバックアップを取埗する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 14です。

ご存じのように、PostgreSQLは9.0から暙準でレプリケヌションが実装されたした。それに加えお9.2では、pg_receivexlogずいうコマンドが远加されたした。

pg_receivexlog
http://www.postgresql.jp/document/9.2/html/app-pgreceivexlog.html

pg_receivexlogは、ネットワヌク経由でトランザクションログを受信、ログのアヌカむブを䜜成・蓄積しおいくこずを可胜にするコマンドです。このコマンドを䜿うこずによっおスタンバむサヌバを皌働しおいなくおも、レプリケヌションの機胜を䜿っおリアルタむムバックアップを取埗するこずができるようになりたす。

pg_receivexlogの基本的な仕組みに぀いおは、ストリヌミングレプリケヌションの開発者のFujii氏の以䞋のスラむドを参照しおください。


今回は、このpg_receivexlogコマンドを䜿っおアヌカむブログを別サヌバに蓄積し、それを䜿っおリカバリができるのか、ずいうずころを怜蚌しおみようず思いたす。

なお、Fujii氏の゚ントリず盛倧に被っおしたったのですが、ここでは気にしないこずにしたす。氏の゚ントリも䜵せお読むず二倍楜しめるかもしれたせん。

■怜蚌構成

今回は以䞋のような環境でpg_receivexlogの怜蚌を実斜したした。

マスタヌサヌバ、スタンバむサヌバのいずれもAmazon EC2でLInux AMIむンスタンスを利甚し、同䞀リヌゞョン内でWAL転送する構成ずしおいたす。

マスタヌサヌバPostgreSQLを皌働するサヌバ
  • Amazon EC2 Linux AMI (32bit)
  • PostgreSQL 9.2
  • IPアドレス10.156.93.165

スタンバむサヌバpg_receivexlog皌働しおWALを受信するサヌバ
  • Amazon EC2 Linux AMI (32bit)
  • PostgreSQL 9.2
  • IPアドレス10.146.98.58
  • 受信したWALを蓄積するディレクトリ/var/lib/pgsql/9.2/receivexlog/
  • ベヌスバックアップを取埗するディレクトリ/var/lib/pgsql/9.2/receivebase/
Amazon EC2におけるPostgreSQLサヌバの基本的な構築方法に぀いおは今回は割愛したす。詳现はDay13の゚ントリを参照しおください。

■pg_receivexlogのための蚭定項目

今回、pg_receivexlogを䜿うために远加で蚭定した項目は以䞋の通りです。

postgresql.confでは蚭定したのは以䞋の4぀の項目です。

wal_level = archive
archive_command = 'cat /dev/null'
max_wal_senders = 3
wal_keep_segments = 8
アヌカむブログずしおWALを生成したすのでwal_levelはarchiveずしたす。

今回はPostgreSQL皌働サヌバ䞊ではアヌカむブログを取埗したせんが、archive_commandが蚭定されおいないずベヌスバックアップの取埗ができないため、archive_commandはダミヌコマンドを蚭定したす。

PostgreSQL皌働サヌバ䞊にWAL送信プロセスが必芁ですので、max_wal_sendersを3ずしたす。

pg_receivexlogでの受信が遅延した堎合、WALセグメントファむル8぀分たでは远い付くこずが可胜な蚭定ずしたすここでの8は適圓な倀です。

次に、レプリケヌションの接続を受け付けるためにpg_hba.confに蚭定を远加したす。ここで指定しおいる "10.146.98.58" はスタンバむサヌバpg_receivexlogを皌働させるサヌバのIPアドレスです。

hostssl replication postgres 10.146.98.58/32 trust

■リアルタむムバックアップを開始する

蚭定が完了したら、たずはPostgreSQLを起動したす。

PostgreSQLが皌働し始めたら、スタンバむサヌバ䞊でpg_receivexlogコマンドを起動しおWALの受信を開始したす。

-bash-4.1$ /usr/pgsql-9.2/bin/pg_receivexlog -h 10.156.93.165 -p 6453 -U postgres -D /var/lib/pgsql/9.2/receivexlog -v
pg_receivexlog: starting log streaming at 0/A000000 (timeline 1)
pg_receivexlogコマンドを実行する際には、マスタヌずなるPostgreSQLサヌバのIPアドレス、ポヌト番号、接続ナヌザ名、および受信したWALを保存するロヌカルのディレクトリを指定したす。

"starting log streaming" ずいうメッセヌゞが出たら、ログの受信の開始は成功です。その時、受信甚のディレクトリを芋るず、

[root@ip-10-146-98-58 ~]# ls -al /var/lib/pgsql/9.2/receivexlog/
total 16392
drwxr-xr-x 2 postgres postgres 4096 Dec 6 14:40 .
drwx------ 5 postgres postgres 4096 Dec 6 14:33 ..
-rw------- 1 postgres postgres 16777216 Dec 6 14:40 00000001000000000000000A.partial
[root@ip-10-146-98-58 ~]#
ずいう圢でファむルができ始めおいるこずが分かりたす。

ここで䜜成されおいるファむルの末尟には「.partial」ずいう suffix が付いおいたす。どうやら、ただ16MBを䜿い切っおいない利甚䞭のWALファむルセグメントファむルに぀いおは末尟に「.partial」ずいう suffix が付加されるようです。

ここで、PostgreSQLに察しおWALを生成するためにデヌタの曎新凊理を行っおみたす。

-bash-4.1$ /usr/pgsql-9.2/bin/pgbench -p 6453 -s 10 -i testdb
この時に受信ディレクトリを芋おみるず、

[root@ip-10-146-98-58 ~]# ls -al /var/lib/pgsql/9.2/receivexlog/
total 131080
drwxr-xr-x 2 postgres postgres 4096 Dec 6 14:43 .
drwx------ 5 postgres postgres 4096 Dec 6 14:33 ..
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000A
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000B
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000C
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000D
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000E
-rw------- 1 postgres postgres 16777216 Dec 6 14:42 00000001000000000000000F
-rw------- 1 postgres postgres 16777216 Dec 6 14:43 000000010000000000000010
-rw------- 1 postgres postgres 16777216 Dec 6 14:43 000000010000000000000011.partial
[root@ip-10-146-98-58 ~]#
WALセグメントファむルが䜜成されおおり、WALが継続的に保存されおいるこずが分かりたす。

■バックアップセットを取っおみる

それでは、ここでpg_receivexlogで取埗したアヌカむブログを䜿ったリカバリを詊しおみたす。

たず、先ほどず同じようにログの受信を開始したす。

bash-4.1$ /usr/pgsql-9.2/bin/pg_receivexlog -h 10.156.93.165 -p 6453 -U postgres -D /var/lib/pgsql/9.2/receivexlog -v
pg_receivexlog: starting log streaming at 0/19000000 (timeline 1)
ログの蓄積が開始されたら、次にベヌスバックアップを取埗したす。

今回は、pg_basebackupコマンドを䜿っおネットワヌク経由でスタンバむサヌバ䞊にベヌスバックアップを取埗したす。ここでは、䞀旊 /var/lib/pgsql/9.2/receivebase ディレクトリにベヌスバックアップを保存したす。

-bash-4.1$ /usr/pgsql-9.2/bin/pg_basebackup -h 10.156.93.165 -p 6453 -U postgres -D /var/lib/pgsql/9.2/receivebase -v
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
pg_basebackup: base backup completed
-bash-4.1$
ベヌスバックアップを取埗したら、ベヌスバック取埗以降のWALを生成するために、デヌタを曎新凊理を行いたす。

[snaga@db01 ~]$ psql -p 6453 testdb
psql (9.2.1)
Type "help" for help.

testdb=> \d
List of relations
Schema | Name | Type | Owner
--------+--------------------+-------+----------
public | pg_stat_statements | view | postgres
public | pgbench_accounts | table | postgres
public | pgbench_branches | table | postgres
public | pgbench_history | table | postgres
public | pgbench_tellers | table | postgres
public | t1 | table | postgres
public | t2 | table | snaga
(7 rows)

testdb=> create table t3 ( uid integer primary key, uname text not null );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t3_pkey" for table "t3"
CREATE TABLE
testdb=> insert into t3 values ( 101, 'Jung, Nicole' );
INSERT 0 1
testdb=>
ここで、CTRL-Cでpg_receivexlogコマンドを停止したす。

bash-4.1$ /usr/pgsql-9.2/bin/pg_receivexlog -h 10.156.93.165 -p 6453 -U postgres -D /var/lib/pgsql/9.2/receivexlog -v
pg_receivexlog: starting log streaming at 0/19000000 (timeline 1)
pg_receivexlog: finished segment at 0/1A000000 (timeline 1)
^Cpg_receivexlog: received interrupt signal, exiting.
pg_receivexlog: not renaming "00000001000000000000001A", segment is not complete
bash-4.1$
pg_receivexlogコマンドの停止によっお、スタンバむからマスタヌぞの接続が完党に切断され、マスタヌサヌバからスタンバむサヌバぞのログのストリヌミングが停止したす。

■アヌカむブログリカバリを実斜

この状態で、先ほどたでに取埗しおあったバックアップセットを䜿っおスタンバむノヌド䞊にレストア、リカバリを実斜しおみたす。

䜿甚するのは、ベヌスバックアップず、リアルタむムにバックアップを取っおあったアヌカむブログです。

たず、取埗しおあったベヌスバックアップをスタンバむサヌバに展開したす。

-bash-4.1$ pwd
/var/lib/pgsql
-bash-4.1$ ls -F
9.2/
-bash-4.1$ cd 9.2/
-bash-4.1$ ls -F
backups/ data/ receivebase/ receivexlog/
-bash-4.1$ ls data/
-bash-4.1$ cp -r receivebase/* data/
-bash-4.1$ ls -F data
backup_label pg_ident.conf pg_snapshots/ PG_VERSION server.key
base/ pg_log/ pg_stat_tmp/ pg_xlog/
global/ pg_multixact/ pg_subtrans/ postgresql.conf
pg_clog/ pg_notify/ pg_tblspc/ postgresql.conf.orig
pg_hba.conf pg_serial/ pg_twophase/ server.crt
次に蓄積しおいたアヌカむブログをpg_xlog以䞋に配眮したす。「.partial」ず suffix の付いおいたWALセグメントファむルも、通垞のWALセグメントのファむル名に倉曎したす。

-bash-4.1$ cp receivexlog/0000000100000000000000* data/pg_xlog/
-bash-4.1$ cd data/pg_xlog/
-bash-4.1$ ls -F
00000001000000000000000A 000000010000000000000013
00000001000000000000000B 000000010000000000000014
00000001000000000000000C 000000010000000000000015
00000001000000000000000D 000000010000000000000016
00000001000000000000000E 000000010000000000000017
00000001000000000000000F 000000010000000000000018
000000010000000000000010 000000010000000000000019
000000010000000000000011 00000001000000000000001A.partial
000000010000000000000012
-bash-4.1$ mv 00000001000000000000001A.partial 00000001000000000000001A
-bash-4.1$ pwd
/var/lib/pgsql/9.2/data/pg_xlog
-bash-4.1$
ここたで準備ができたら、スタンバむサヌバのPostgreSQLを起動したす。

[root@ip-10-146-98-58 ~]# /etc/rc.d/init.d/postgresql-9.2 start
Starting postgresql-9.2 service: [ OK ]
[root@ip-10-146-98-58 ~]#
スタンバむサヌバ䞊でPostgreSQLサヌビスが正しく起動したら、デヌタベヌスむンスタンスに接続しおレコヌドを芋おみたす。

[root@ip-10-146-98-58 ~]# su - postgres
-bash-4.1$ psql testdb
psql (9.2.1)
Type "help" for help.

testdb=# \d
List of relations
Schema | Name | Type | Owner
--------+--------------------+-------+----------
public | pg_stat_statements | view | postgres
public | pgbench_accounts | table | postgres
public | pgbench_branches | table | postgres
public | pgbench_history | table | postgres
public | pgbench_tellers | table | postgres
public | t1 | table | postgres
public | t2 | table | snaga
public | t3 | table | snaga
(8 rows)

testdb=# select * from t3;
uid | uname
-----+--------------
101 | Jung, Nicole
(1 row)

testdb=#
先ほど、ベヌスバックアップ取埗以降に曎新を行ったレコヌド最埌にINSERTしたものたでリカバリできおいるこずが分かりたす。

この時のサヌバログを芋るず、

[2012-12-07 00:10:14 JST] 4197: LOG: database system was interrupted; last known up at 2012-12-07 00:00:16 JST
[2012-12-07 00:10:14 JST] 4197: LOG: creating missing WAL directory "pg_xlog/archive_status"
[2012-12-07 00:10:14 JST] 4197: LOG: database system was not properly shut down; automatic recovery in progress
[2012-12-07 00:10:14 JST] 4197: LOG: redo starts at 0/19000074
[2012-12-07 00:10:14 JST] 4197: LOG: record with zero length at 0/1A023DEC
[2012-12-07 00:10:14 JST] 4197: LOG: redo done at 0/1A023DC4
[2012-12-07 00:10:14 JST] 4197: LOG: last completed transaction was at log time 2012-12-07 00:04:39.920898+09
[2012-12-07 00:10:14 JST] 4194: LOG: database system is ready to accept connections
ずなっおいお、通垞のWALず同じようにリカバリするこずができおいるこずが分かりたす。

■たずめ

今回は、バヌゞョン9.2で新たに远加されたpg_receivexlogコマンドを䜿っお、ネットワヌク経由でのアヌカむブログのリアルタむムバックアップ、そしおそこからリカバリできるかどうかを詊しおみたした。

実際に実運甚するためには手順ずしお考えなければならないこずはもう少しあるかず思いたすが、機胜ずしお動䜜するずころたでは今回確認するこずができたした。

デヌタベヌスの最倧の圹割のひず぀は「デヌタの保護」にありたす少なくずも私はそう思っおいたす。そういった意味でも、このpg_receivexlogコマンドのような機胜は、今埌の掻躍が期埅される領域のひず぀のように思いたす。

では、たた。
↧

tablelogでテヌブルの曎新差分を取埗する

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 15です。

PostgreSQLに限らず、RDBMSにおいお「特定のテヌブルの曎新差分だけを取埗したい」ずいうのは、DBAの倢であるず蚀えたすよね。

トランザクションログの䞭には圓然ながら「曎新情報」が蚘録されおいるわけですが、それを再利甚可胜な圢で取り出す方法が無く、涙で枕を濡らした思い出を持぀DBAも倚いこずでしょう。倚分。

もう少し分かりやすく申したすず、PostgreSQLのメゞャヌバヌゞョンアップの際などにはpg_dumpによるexport/importが必芁ずなるわけですが、デヌタ量が倚くなっお来おいる今、デヌタのexportにも時間がかかりたすし、24x365のシステムも増えおいたすので、移行しおいる間にもデヌタの曎新が発生しおしたいたす。

なので、こういった䜜業の合間に発生する曎新情報少ないずは蚀えを、保存しおおいお、埌から再利甚できるず䟿利ですよね ね ずいう話でありたす。

■tablelogモゞュヌル


ずいうわけで、今回はPostgreSQLにおいお、テヌブルの曎新差分を取埗する方法をご玹介したす。

今回はtablelogずいうモゞュヌルを䜿いたす。

PgFoundry: Table Audit: Project Info
http://pgfoundry.org/projects/tablelog/

tablelogモゞュヌルは、テヌブルデヌタに曎新が発生した堎合に、トリガを䜿っおその曎新デヌタを別のログテヌブルに保存するモゞュヌルです。

ですので、特定のテヌブルにロギング甚のトリガを蚭定しおおくこずで、埌刻、ログテヌブルから曎新情報を時系列で取り出すこずが可胜になる、ずいうこずです。

䜙談ですが、「tablelog」をGoogleで怜玢するず「次の怜玢結果を衚瀺しおいたす: tabelog」ず蚀われるのですが、ちょっず違いたす。惜しいですが。

■tablelogモゞュヌルのむンストヌル


それでは、tablelogモゞュヌルをむンストヌルしおみたす。

tar.gzを展開しお、環境倉数USE_PGXSを1に蚭定し぀぀、make & make install を実行したす。

なお、今回はむンストヌルスクリプトやロギングの蚭定甚のSQL関数をいく぀か远加しおいたすので、パッチtable_log-0_4_4_snaga.diffも適甚したす。

table_log-0_4_4_snaga.diff
https://gist.github.com/4291369

[snaga@devsv02 pgsql]$ tar zxf table_log-0.4.4.tar.gz
[snaga@devsv02 pgsql]$ cd table_log-0.4.4
[snaga@devsv02 table_log-0.4.4]$ ls
Makefile table_log.c table_log_init.sql tests
README.table_log table_log.h table_log.sql.in
[snaga@devsv02 table_log-0.4.4]$ patch -p1 < ../table_log-0_4_4_snaga.diff
patching file Makefile
patching file table_log.sql.in
patching file table_log_uninstall.sql.in
[snaga@devsv02 table_log-0.4.4]$ env USE_PGXS=1 PATH=/usr/pgsql-9.1/bin:$PATH make
sed 's,MODULE_PATHNAME,$libdir/table_log,g' table_log.sql.in >table_log.sql
sed 's,MODULE_PATHNAME,$libdir/table_log_uninstall,g' table_log_uninstall.sql.in >table_log_uninstall.sql
gcc -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -I/usr/include/et -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -fpic -I. -I. -I/usr/pgsql-9.1/include/server -I/usr/pgsql-9.1/include/internal -I/usr/include/et -D_GNU_SOURCE -I/usr/include/libxml2 -I/usr/include -c -o table_log.o table_log.c
gcc -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -I/usr/include/et -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wformat-security -fno-strict-aliasing -fwrapv -fpic -L/usr/pgsql-9.1/lib -L/usr/lib64 -shared -o table_log.so table_log.o
rm table_log.o
[snaga@devsv02 table_log-0.4.4]$ su
Password:
[root@devsv02 table_log-0.4.4]# env USE_PGXS=1 PATH=/usr/pgsql-9.1/bin:$PATH make install
/bin/mkdir -p '/usr/pgsql-9.1/share/contrib'
/bin/mkdir -p '/usr/pgsql-9.1/lib'
/bin/mkdir -p '/usr/share/doc/pgsql/contrib'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 table_log.sql table_log_uninstall.sql '/usr/pgsql-9.1/share/contrib/'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 755 table_log.so '/usr/pgsql-9.1/lib/'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./README.table_log '/usr/share/doc/pgsql/contrib/'
[root@devsv02 table_log-0.4.4]# exit
exit
[snaga@devsv02 table_log-0.4.4]$
次に、tablelogを䜿甚したいデヌタベヌスに察しおむンストヌルを行いたす。

[snaga@devsv02 table_log-0.4.4]$ psql -f /usr/pgsql-9.1/share/contrib/table_log.sql testdb2
SET
CREATE FUNCTION
CREATE FUNCTION
(...snip...)
CREATE FUNCTION
CREATE FUNCTION
[snaga@devsv02 table_log-0.4.4]$
これでむンストヌルは完了です。

■タヌゲットずなるテヌブルにロギングを蚭定する


たず、タヌゲットずなるテヌブルのサンプルを䜜成したす。

testdb2=# CREATE table t1 ( uid integer primary key, uname text not null );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE TABLE
testdb2=#
次に、䞊蚘で䜜成したテヌブル t1 に察しおログ取埗を蚭定したす。

ログの蚭定はSQL関数の table_log_init() を䜿甚したす。最初の匕数はロギングのレベルで、35の倀を取りたす。

ロギングレベルの4はtrigger_idず呌ばれるIDも含めお保存し、ロギングレベル5は曎新したナヌザ名も含めお保存したす詳现は埌述。

ここではロギングレベルを3にしお蚭定を行いたす。

testdb2=# SELECT table_log_init(3, 't1');
table_log_init
----------------

(1 row)

testdb2=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+--------+-------+-------+---------+-------------
public | t1 | table | snaga | 0 bytes |
public | t1_log | table | snaga | 0 bytes |
(2 rows)

testdb2=#

■テヌブルを曎新しおログを取埗する


それでは、実際にテヌブルにレコヌドをINSERTしおみたしょう。

testdb2=# SELECT * FROM t1;
uid | uname
-----+-------
(0 rows)

testdb2=# INSERT INTO t1 VALUES ( 101, 'satoshi nagayasu' );
INSERT 0 1
testdb2=# SELECT * FROM t1;
uid | uname
-----+------------------
101 | satoshi nagayasu
(1 row)

testdb2=# SELECT * FROM t1_log;
uid | uname | trigger_mode | trigger_tuple | trigger_changed
-----+------------------+--------------+---------------+-------------------------------
101 | satoshi nagayasu | INSERT | new | 2012-11-26 13:34:01.218549+09
(1 row)

testdb2=#
䞊蚘のように、テヌブルt1に察しお、そのログテヌブルであるテヌブルt1_logに、カラムのデヌタに加えお、トリガの皮別INSRET/UPDATE/DELETE、タプルの皮別曎新前のデヌタoldか、曎新埌のデヌタnewかず、トリガが実行された時刻が保存されおいたす。

次に、デヌタのUPDATEを行っおみたす。ここでは、小文字を倧文字に倉換しおみたす。

testdb2=# UPDATE t1 SET uname = upper(uname);
UPDATE 1
testdb2=# SELECT * FROM t1;
uid | uname
-----+------------------
101 | SATOSHI NAGAYASU
(1 row)

testdb2=# SELECT * FROM t1_log;
uid | uname | trigger_mode | trigger_tuple | trigger_changed
-----+------------------+--------------+---------------+-------------------------------
101 | satoshi nagayasu | INSERT | new | 2012-11-26 13:34:01.218549+09
101 | satoshi nagayasu | UPDATE | old | 2012-11-26 13:34:38.38462+09
101 | SATOSHI NAGAYASU | UPDATE | new | 2012-11-26 13:34:38.38462+09
(3 rows)

testdb2=#
今床は、UPDATEのログのため、trigger_modeがUPDATEずなり、曎新前のレコヌドoldず曎新埌のレコヌドnewの䞡方が蚘録されおいたす。

■ロギングの停止


なお、ロギングを停止したい堎合には、disable_table_log()関数を䜿うこずで停止するこずができたす。これはオリゞナルには無く、今回適甚したパッチで远加されるSQL関数です

testdb2=# SELECT disable_table_log('t1');
disable_table_log
-------------------

(1 row)

testdb2=# \d t1+
Table "public.t1"
Column | Type | Modifiers
--------+---------+-----------
uid | integer | not null
uname | text | not null
Indexes:
"t1_pkey" PRIMARY KEY, btree (uid)

testdb2=#

■ログレベルの違い


ログレベルを4にするずtrigger_idず呌ばれる倀が远加され、ログレベルを5にするず曎新を実行したナヌザも蚘録されるようになりたす。

trigger_idずいうのは、ロギングの察象テヌブルにプラむマリキヌが存圚しない堎合に、プラむマリキヌの代替えずしおレコヌドを䞀意に識別するために蚭定される BIGSERIAL 倀です。

以䞋は、テヌブルt1, t2, t3に぀いお、それぞれログレベルを倉えお倉曎差分を取埗したものです。芋おいただくず分かりたすが、ログレベルによっおログテヌブルのカラム数が違っおいたす。

testdb2=# \x
Expanded display is on.
testdb2=# SELECT * FROM t1_log;
-[ RECORD 1 ]---+------------------------------
uid | 101
uname | satoshi nagayasu
trigger_mode | INSERT
trigger_tuple | new
trigger_changed | 2012-11-26 13:48:24.141572+09

testdb2=# SELECT * FROM t2_log;
-[ RECORD 1 ]---+------------------------------
uid | 101
uname | satoshi nagayasu
trigger_mode | INSERT
trigger_tuple | new
trigger_changed | 2012-11-26 13:48:24.142149+09
trigger_id | 1

testdb2=# SELECT * FROM t3_log;
-[ RECORD 1 ]---+------------------------------
uid | 101
uname | satoshi nagayasu
trigger_mode | INSERT
trigger_tuple | new
trigger_changed | 2012-11-26 13:48:24.142683+09
trigger_id | 1
trigger_user | snaga

testdb2=#

■たずめ


ずいうわけで、今回はテヌブルの曎新情報を取埗、保存するtablelogモゞュヌルを埡玹介したした。

デヌタベヌスのデヌタが倧きくなり、か぀メンテナンス時間を取りづらい昚今、テヌブルの曎新情報を取埗しおうたく䜿えば、運甚管理がもっず容易になるケヌスが倚くあるず思いたす。

ぜひ、こういったモゞュヌルもうたく䜿いこなしおいただければず思いたす。

ではたた。
↧

PostgreSQLのストレヌゞアヌキテクチャ基本線

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 16です。

PostgreSQLのアヌキテクチャやパフォヌマンスを議論する際、「ストレヌゞファむルが远蚘型のストレヌゞアヌキテクチャを採甚しおいる」ずいうこずは、PostgreSQL特有の倧きな特城ずしお認識しおいる方も倚いでしょう。

少し前にも、ネット䞊でPostgreSQLず他のRDBMSのストレヌゞのアヌキテクチャの違いに぀いお話題になったこずもありたした。

PostgreSQLずMySQLはどちらかに明確な優䜍性がありたすか - QA@IT
http://qa.atmarkit.co.jp/q/2395

優䜍性云々の議論はずりあえず眮いおおくずしお、たずはPostgreSQLの実際の仕組みをきちんず理解するために「远蚘型のストレヌゞ」ずいうものがどのように動いおいるのかを芗いおみたす。

■「远蚘型のストレヌゞアヌキテクチャ」ずは


PostgreSQLにおける「远蚘型のストレヌゞアヌキテクチャ」ずいうのは、簡単に蚀えば、「レコヌドの曎新凊理を行う際に、ブロック内の以前のレコヌドを䞊曞きするのではなく、別のレコヌドずしお䜜成する」ずいう仕組みのこずです。


以降では、この远蚘型のアヌキテクチャに぀いおに぀いお、テヌブル内のレコヌドがどのように倉化しおいくのか、実際の動䜜を远いながら解説しおいきたす。

■pageinspectモゞュヌルのむンストヌル


今回は、テヌブル内のレコヌドの状態を芋るためにpageinspectモゞュヌルを利甚したす。

pageinspectモゞュヌルは、PostgreSQLのテヌブルやむンデックスのブロックの、さらにその䞭にある「タプル内郚的にはitemず呌ばれる」の状態を取埗するための関数矀を提䟛するcontribモゞュヌルです。

F.20. pageinspect
http://www.postgresql.jp/document/9.0/html/pageinspect.html

9.0以前はむンストヌルスクリプトを䜿っおむンストヌル、9.1以降はEXTENSIONずしおむンストヌルするこずになりたすので、必芁に応じおDay2の゚ントリも参照にしおむンストヌルしおください。

■ブロック内郚における新芏レコヌドの状態


それでは、実際にテヌブルの曎新凊理においおブロック内のレコヌドがどのように倉化しおいくのかを芋おみたしょう。

たず、integerずtextのカラムを持぀テヌブルt1を䜜成し、レコヌドを䞀件INSERTしたす。

testdb=# CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE TABLE
testdb=#
testdb=# INSERT INTO t1 VALUES ( 101, 'insert 1' );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+----------
101 | insert 1
(1 row)

testdb=#
この状態でテヌブルのブロック内のレコヌドの状態を芋おみたしょう。

レコヌドの状態を芋るには、pageinspectモゞュヌルで提䟛される二぀の関数 get_raw_page() ず heap_page_items() を䜿いたす。get_raw_page() はテヌブルのブロックをbytea圢匏で取埗したす。heap_page_items()は、bytea圢匏のバむナリを受け取っお、その内郚にあるレコヌドの状態を衚瀺したす。

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 8152 | 1 | 37 | 1859 | 0
(1 row)

testdb=#
「lp」はブロック内のアむテムのオフセットID、「lp_off」はブロック内におけるレコヌド本䜓のオフセットアドレスです。「lp_len」はレコヌドの長さです。


なお、テヌブルファむルのブロック内郚は䞊蚘のような配眮になっおおり、デヌタ本䜓はブロック内の空き領域を埌ろの方から䜿甚したす。ですので、lp_offの倀が8152ず、8kBブロックのギリギリ埌ろの方になっおいたす。

たた、「t_xmin」はそのレコヌドを䜜成したトランザクションのトランザクションIDを瀺しおおり、「t_xmax」は逆にそのレコヌドを削陀したトランザクションのトランザクションIDを瀺しおいたす。

䞊蚘の堎合、t_xminの倀が1859で、t_xmaxの倀が0になっおいたすので、このレコヌドを䜜成したトランザクションのトランザクションIDが1859であり、か぀ただ削陀されおいない生きおいるレコヌドであるこずが分かりたす。

■レコヌドに察する曎新凊理


次に、このレコヌドを曎新しお、ブロックの内郚がどのように倉化するか芋おみたす。

testdb=# UPDATE t1 SET uname = 'update 1' WHERE uid = 101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 8152 | 1 | 37 | 1859 | 1860
2 | 8112 | 1 | 37 | 1860 | 0
(2 rows)

testdb=#
レコヌドを曎新するず、先ほどのレコヌドlp==1のt_xmaxが1860に蚭定され、新しいレコヌドlp==2が䜜成されたした。

この時、新しく䜜成されたレコヌドのt_xminの倀1860が叀いレコヌドのt_xmaxの倀1860ず同じになっおいるこずが分かりたす。぀たり、叀いレコヌドlp==1を削陀するのず同時に新しいレコヌドlp==2を远加しおいるのです。

このように、PostgreSQLのUPDATEの凊理では、叀いレコヌドにt_xmaxを蚭定するこずで「削陀したこずにしお」、新しいレコヌドを䜜成するこずによっお、あたかも「曎新凊理」を行っおいるように動䜜するのです。

これが、PostgreSQLの「远蚘型のストレヌゞアヌキテクチャ」の基本的な構造です。

この状態で曎新凊理を行うず、曎に新しいレコヌドが远加され、t_xminずt_xmaxを䜿っおチェヌンのように぀ながっおいきたす。

testdb=# UPDATE t1 SET uname = 'update 2' WHERE uid = 101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 8152 | 1 | 37 | 1859 | 1860
2 | 8112 | 1 | 37 | 1860 | 1861
3 | 8072 | 1 | 37 | 1861 | 0
(3 rows)

testdb=#

■䞍芁領域の解攟VACUUM凊理


このように、曎新凊理を続けおいるず、PostgreSQLでは削陀されたレコヌドの領域が増えおいきたす。この「削陀されたレコヌドの領域」のこずを俗に「䞍芁領域」ず呌んだりしたす。

この䞍芁領域が増えおくるず、「実際に生きおいるレコヌド数は少ないのにファむルサむズが倧きい」ずいう状況が発生し、パフォヌマンスが䜎䞋する原因になりたす。

この䞍芁領域を解攟回収する仕組みが、PostgreSQLで有名な「VACUUM」です。

VACUUMの基本的なしくみは䞊蚘の通りなのですが、実際にブロック内郚のレコヌドがどのように倉化するのかを芋おみたす。

先ほどのテヌブルt1に察しおVACUUMを行ったの結果が以䞋のものです。

testdb=# VACUUM t1;
VACUUM
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 3 | 2 | 0 | |
2 | 0 | 0 | 0 | |
3 | 8152 | 1 | 37 | 1861 | 0
(3 rows)

testdb=#
先ほどたで存圚しおいた叀いレコヌドlp==1ずlp==2のlp_lenがれロになり、t_xmin/t_xmaxも削陀されおlp_flagsが0に蚭定されおいたす。これが「テヌブルがVACUUMされた状態」になりたす。

なお、lp_flags==0の領域は「未䜿甚領域」ずなっおいお、すぐに再利甚できる領域であるこずを意味しおいたす。

この状態で、さらに曎新凊理UPDATEを行っおみたす。

testdb=# UPDATE t1 SET uname = 'update 3' WHERE uid = 101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 3 | 2 | 0 | |
2 | 8112 | 1 | 37 | 1862 | 0
3 | 8152 | 1 | 37 | 1861 | 1862
(3 rows)

testdb=#
するず今床は以前のレコヌドが䜿っおいたlp==2の領域が䜿われたした。

぀たり、VACUUM凊理で解攟回収したこずで領域が空き、新しいレコヌドがそこを利甚できるようになった、ずいうこずです。

■たずめ


今回はPostgreSQLの「远蚘型のストレヌゞアヌキテクチャ」に぀いお、実際の動䜜を远いかけながら、その基本的な仕組みを解説したした。

VACUUM凊理は、珟圚は自動VACUUMプロセスによっお自動的に実斜されるため、昔ほど気にする必芁は無くなっおきたした。

ずは蚀え、どのような仕組みで動いおいるのかを理解しおおくこずは、トラブルシュヌティングやパフォヌマンスチュヌニングの際には重芁になっおきたすので、PostgreSQLを䜿いこなしたいずいう方は、ぜひこの蟺りを理解しおおいおいただければず思いたす。

明日は、このPostgreSQLの「远蚘型のストレヌゞアヌキテクチャ」の匱点を克服するべく実装されおいる工倫に぀いお玹介したす。

では、たた。
↧
↧

PostgreSQLのストレヌゞアヌキテクチャPage Pruning線

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 17です。

昚日の゚ントリでは、「ストレヌゞアヌキテクチャ基本線」ずいうこずで、PostgreSQLの内郚でディスクがどのように䜿われおいるのか、その基本を解説したした。そしお、PostgreSQLの曎新凊理で「新しいレコヌドを远蚘し、叀いレコヌドからチェヌンのように぀なぐ」ずいう凊理をしおいる様子を実際に芳察したした。

今回は、この「远蚘型のストレヌゞアヌキテクチャ」の持぀課題を克服するために、どのような工倫がされおいるのかを実際の動䜜を芋ながら解説したす。

■远蚘型ストレヌゞにおけるファむルの肥倧化ずその抑制


PostgreSQLに察する指摘ずしお、「この远蚘型の曎新凊理がデメリットである。曎新が増えるずレコヌドがどんどん増えおいくのが匱点である」ずいう指摘がありたす。

原則論ずしおは、確かに曎新凊理を連続しお行うずレコヌドが远蚘されおデヌタのサむズが倧きくなっおいくのですが、実際の実装はそんなに単玔なものではなく、デヌタサむズが増えないように随所に工倫がされおいたす。

今回は、その䞭でも「Page Pruning」ず呌ばれる凊理に぀いお解説したす。

■「Page Pruning」ずは


「Page Pruning」ずは、ペヌゞ内に未䜿甚領域が少なくなった堎合に行われる凊理です。

ペヌゞ内に未䜿甚領域が無い堎合、曎新凊理を行う、぀たり新しいレコヌドタプルを远蚘するためには、他のペヌゞブロックを䜿うしかないず思われがちです。

が、実際には他のブロックを䜿う前に、今珟圚タプルが存圚しおいるペヌゞが本圓に䜿えないのか、再床敎理しお確認する、ずいう凊理が内郚で自動的に行われおいたす。これが「Page Pruning」ず呌ばれる凊理です。

簡単に蚀うず、そのペヌゞ内だけVACUUMをかけお䞍芁領域を削陀しおいる様子をむメヌゞしおもらえれば良いず思いたす。

このPage Pruning凊理があるこずによっお、特定のレコヌドに察する曎新凊理が連続的に行われおいるような堎合に、無闇に䜿甚するブロック数が増えるような事態を抑制するこずができおいるのです。

それでは実際にペヌゞ内郚の状態を具䜓的に芋おみたしょう。

■曎新凊理ずブロック内の未䜿甚領域の枛少


たず前回ず同じように曎新凊理をするためのテヌブルを䜜成し、1件のレコヌドを䜜成、そのレコヌドを連続的に曎新しおいきたす。分かりやすくするために、ここでは少し倧きめのレコヌドを䜿いたす。

testdb=# CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
testdb=# INSERT INTO t1 VALUES ( 101, 'insert 1-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------' );
INSERT 0 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 39586 | 0
(1 row)

testdb=# UPDATE t1 SET uname = 'update 0-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------' WHERE uid=101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 39586 | 39587
2 | 7568 | 1 | 309 | 39587 | 0
(2 rows)

testdb=# UPDATE t1 SET uname = 'update 1-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------' WHERE uid=101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 39586 | 39587
2 | 7568 | 1 | 309 | 39587 | 39588
3 | 7256 | 1 | 309 | 39588 | 0
(3 rows)

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
8192
(1 row)

testdb=#
このように曎新凊理を行っおいくず、前回芋た通り、叀いレコヌドのt_xmaxず新しいレコヌドのt_xminずの間でチェヌンの構造ができおいきたす。

このUPDATEをブロックの空き領域が無くなるたで続けるずどうなるのでしょうか。

以䞋が、UPDATEを続けたブロック内郚の状態です。

lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 39586 | 39587
2 | 7568 | 1 | 309 | 39587 | 39588
3 | 7256 | 1 | 309 | 39588 | 39589
4 | 6944 | 1 | 309 | 39589 | 39590
5 | 6632 | 1 | 309 | 39590 | 39591
6 | 6320 | 1 | 309 | 39591 | 39592
7 | 6008 | 1 | 309 | 39592 | 39593
8 | 5696 | 1 | 309 | 39593 | 39594
9 | 5384 | 1 | 309 | 39594 | 39595
10 | 5072 | 1 | 309 | 39595 | 39596
11 | 4760 | 1 | 309 | 39596 | 39597
12 | 4448 | 1 | 310 | 39597 | 39598
13 | 4136 | 1 | 310 | 39598 | 39599
14 | 3824 | 1 | 310 | 39599 | 39600
15 | 3512 | 1 | 310 | 39600 | 39601
16 | 3200 | 1 | 310 | 39601 | 39602
17 | 2888 | 1 | 310 | 39602 | 39603
18 | 2576 | 1 | 310 | 39603 | 39604
19 | 2264 | 1 | 310 | 39604 | 39605
20 | 1952 | 1 | 310 | 39605 | 39606
21 | 1640 | 1 | 310 | 39606 | 39607
22 | 1328 | 1 | 310 | 39607 | 39608
23 | 1016 | 1 | 310 | 39608 | 39609
24 | 704 | 1 | 310 | 39609 | 0
(24 rows)
最新のタプルが1件lp==24、および曎新前の叀いタプルlp==123でブロック内郚がほが䞀杯になっおいたす。

たた、lp==24のタプルのオフセットを芋るず、かなりブロックの前方たで埋たっおきおいるこずが分かりたす。前回解説した通り、ペヌゞブロックは埌ろの方から䜿われたす。

■Page Pruning凊理の実斜


ここたでの状態で再床UPDATEを行っおみたす。UPDATEが繰り返され、ブロック内が叀いタプルレコヌドで䞀杯になっおいる状態で、再床UPDATEを行うずどうなるのでしょうか。

以䞋は、次のUPDATE凊理を行った盎埌のペヌゞブロック内郚の状態です。

lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 24 | 2 | 0 | |
2 | 7568 | 1 | 310 | 39610 | 0
3 | 0 | 0 | 0 | |
4 | 0 | 0 | 0 | |
5 | 0 | 0 | 0 | |
6 | 0 | 0 | 0 | |
7 | 0 | 0 | 0 | |
8 | 0 | 0 | 0 | |
9 | 0 | 0 | 0 | |
10 | 0 | 0 | 0 | |
11 | 0 | 0 | 0 | |
12 | 0 | 0 | 0 | |
13 | 0 | 0 | 0 | |
14 | 0 | 0 | 0 | |
15 | 0 | 0 | 0 | |
16 | 0 | 0 | 0 | |
17 | 0 | 0 | 0 | |
18 | 0 | 0 | 0 | |
19 | 0 | 0 | 0 | |
20 | 0 | 0 | 0 | |
21 | 0 | 0 | 0 | |
22 | 0 | 0 | 0 | |
23 | 0 | 0 | 0 | |
24 | 7880 | 1 | 310 | 39609 | 39610
(24 rows)
前回の゚ントリで芋たように、lp_off==0の領域は未䜿甚領域ずしお解攟されたこずを衚しおいたすので、このブロックには空き領域がたくさんできおいるこずが分かりたす。これは、PostgreSQLが「もはや必芁ずされない削陀枈みタプル」を刀別しお、自動的にペヌゞ内を敎理したためです。

ですので、先ほどたで䞍芁領域削陀枈みレコヌドで䞀杯だったこのペヌゞは、明瀺的にVACUUMを実斜しおいないにも関わらず、自動的に䞍芁領域が解攟されお未䜿甚領域を確保できたこずになりたす。

もう少し詳现に芋おみるず、最埌に残ったタプルは2぀あり、lp==24のタプルの次にlp==2のタプルが぀ながっおいお、lp==2のタプルが最新の生きおいるタプルt_xmax==0であるこずが分かりたす。

この時、テヌブルファむルのサむズを芋るず、最初の8kBのたたです。

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
8192
(1 row)

testdb=#
぀たり、ペヌゞがほが䞀杯になっおしたったにも関わらず、自動的にそのペヌゞ内郚を敎理するこずによっお、他のブロックを䜿わずに圓該ペヌゞの䞭だけで曎新凊理を行うこずができたこずになりたす。

このように、PostgreSQLでは曎新凊理を行う際にペヌゞブロック内が䞀杯になっおいるず、「他のブロックに曞き蟌みに行く前」に、そのペヌゞブロック内を敎理しお再利甚できる領域が無いかどうかを確認したす。この凊理を行うこずによっお、やみくもに他のブロックを読み曞きしないようにしおいるのです。この凊理を内郚的には「Page Pruning」ず呌びたす。

このPage Pruningの凊理によっお、曎新が倚い堎合でも曎新凊理を単䞀のペヌゞブロック内で完結するようになっおいたす。

なお、Page Pruningの凊理は゜ヌスコヌド内では src/backend/access/heap/pruneheap.c で実装されおいたす。興味のある方はこの蟺りを参照しおください。

■たずめ


今回は、远蚘型のストレヌゞ構造を持぀PostgreSQLにおいお、曎新凊理によるブロック数の増加をどのような方法で抑制しおいるのか、その工倫のひず぀を、実際のペヌゞの内郚の状態を远いかけながら解説したした。

確かにPostgreSQLは远蚘型のストレヌゞアヌキテクチャを持぀ため、曎新凊理を行うこずによっおファむルサむズが肥倧化しおいくず思われがちですが、実際にはこのようにファむルの肥倧化ペヌゞブロックの増加を防ぐ工倫が実装されおいるこずがお分かりいただけたかず思いたす。

次回は、このようなテヌブル内郚におけるタプルの移動の凊理ずむンデックスずの関係に぀いお解説しおみようず思いたす。

では、たた。
↧

PostgreSQLのストレヌゞアヌキテクチャHOT線

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 18です。

前回の゚ントリでは、PostgreSQLのストレヌゞアヌキテクチャのうち、特にPage Pruningに぀いお実際の動䜜状況を芋ながら、その仕組みを解説したした。

ここたではテヌブルのみの解説をしおきたしたが、実際にはほずんどのテヌブルにはむンデックスもありたすので、レコヌドを曎新する堎合にはむンデックスに぀いおのケアも必芁になりたす。

ずいうわけで、今回はレコヌドに察する曎新が行われおいる間に、むンデックスがどのように動䜜しおいるのかを、具䜓的な動䜜䟋を亀えお芋おみたす。

■むンデックスの内郚構造

B-Treeのリヌフノヌドの内郚構造は以䞋のようになっおいたす。


前々回のストレヌゞアヌキテクチャの基本線で、pageinspectずいうcontribモゞュヌルを玹介したした。

pageinsectモゞュヌルで提䟛されおいるheap_page_items()関数でテヌブルのブロック内郚の状態を芋るこずができたわけですが、むンデックスB-Treeむンデックスに぀いおもbt_page_items()ずいう同様の関数があり、この関数を䜿うこずによっお、B-Treeむンデックスのリヌフノヌドのブロック内郚の状態を芋るこずができたす。

testdb=# INSERT INTO t1 VALUES ( 101, 'insert 1' );
INSERT 0 1
testdb=#
testdb=# SELECT * FROM bt_page_items('t1_pkey', 1);
itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 65 00 00 00
(1 row)

testdb=#
ここで確認しおおいおいただきたいのは、ctidカラムずdataカラムです。

dataカラムはむンデックスのキヌの倀で、盎前にプラむマリキヌずしお「101」ずいう倀をINSERTしおいたすので、dataカラムのデヌタが16進数で「65」、10進数で「101」ずなっおいたす。


䞊蚘の図の通り、むンデックスは該圓するレコヌドぞのポむンタを保持しおいるのですが、具䜓的にはこのctidカラムがむンデックス゚ントリに察応するポむンタ情報になりたす。

ここでは、ctidが「(0,1)」ずなっおいたすが、これは「テヌブルファむルの0番目のブロックのラむンポむンタ1番目に䜍眮しおいる」ずいうこずを意味しおいたす。

■むンデックスずレコヌドの関連付け

それでは、本圓にブロック0のラむンポむンタ1番目にレコヌドが存圚しおいるかを芋おみたしょう。

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 43737 | 0
(1 row)

testdb=#
芋おみるず、確かに0番ブロックの1番目のラむンポむンタにレコヌドが存圚しおいたした。

このように、むンデックスずテヌブルのレコヌドは、ctidずいう「ポむンタ」を介しお぀ながっおいるのです。

■レコヌドに察する曎新凊理HOT Update

それでは、前回ず同じように同䞀のレコヌドに曎新凊理を行いながら、「テヌブルブロックの内郚」ず「むンデックスブロックの内郚」がどのように倉化しおいくかを芋おみたしょう。

たず、1件目のレコヌドをINSERTするず以䞋のようになりたす。

testdb=# INSERT INTO t1 VALUES ( 101, 'insert 1 ' );
INSERT 0 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 42731 | 0
(1 row)

testdb=# select * from bt_page_items('t1_pkey', 1);
itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 65 00 00 00
(1 row)

testdb=#
テヌブルブロックおよびむンデックスブロック双方にアむテムが1件挿入されおいるこずが分かりたす。

次に、このレコヌドに察しお曎新凊理を行い、同じようにテヌブルブロックずむンデックスブロックの内郚の状態を芋おみたす。

testdb=# UPDATE t1 SET uname = 'update 0-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------' WHERE uid=101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 42731 | 42732
2 | 7568 | 1 | 309 | 42732 | 0
(2 rows)

testdb=# select * from bt_page_items('t1_pkey', 1);
itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 65 00 00 00
(1 row)

testdb=#
今床は、テヌブルブロックの方は叀いレコヌドが削陀され、新しいレコヌドが远蚘されおいるこずが分かりたすが、䞀方でむンデックスのブロックの方ぱントリが増えおいたせん。

これが「HOT: Heap Only Tuple」ず呌ばれる仕組みで、むンデックスのキヌが曎新されない限り、テヌブルのタプルは増えおいっおもむンデックスの゚ントリは増加しないこずになりたす。

これが、远蚘型のストレヌゞアヌキテクチャの匱点をカバヌするための工倫のひず぀であり、このような曎新凊理を「HOT Update」ず呌びたす。

䞊蚘の状態で、むンデックスのキヌから最新のレコヌドを取埗する堎合、
  • むンデックスキヌdataカラムの倀からレコヌドの䜍眮ctidカラムの倀を取埗
  • ctidカラムから読みだしたテヌブル内の䜍眮のレコヌドlp==1を取埗
  • 圓該レコヌドは削陀枈t_xmax==42732のため、t_xmaxずt_xminのチェヌンを蟿っおlp==2のレコヌドを芋぀ける
  • lp==2はただ削陀されおいないt_xmaxが空レコヌドなので、これを䜿う
ずいう流れになりたす。

なお、HOT Updateが有効になるのは「むンデックスの匵られおいないカラムを曎新する堎合」であり、むンデックスのあるカラムを曎新するずHOT Updateずなりたせんのでその点は泚意が必芁です。

■Page Pruning凊理ずむンデックス

それでは、前回芋たようなブロックの空き領域が少なくなっおPage Pruning凊理が行われた堎合、むンデックスずテヌブルレコヌドの状態はどうなるのでしょうか。実際に動䜜させお芋おみたしょう。

lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 309 | 42731 | 42732
2 | 7568 | 1 | 309 | 42732 | 42733
3 | 7256 | 1 | 309 | 42733 | 42734
4 | 6944 | 1 | 309 | 42734 | 42735
5 | 6632 | 1 | 309 | 42735 | 42736
6 | 6320 | 1 | 309 | 42736 | 42737
7 | 6008 | 1 | 309 | 42737 | 42738
8 | 5696 | 1 | 309 | 42738 | 42739
9 | 5384 | 1 | 309 | 42739 | 42740
10 | 5072 | 1 | 309 | 42740 | 42741
11 | 4760 | 1 | 309 | 42741 | 42742
12 | 4448 | 1 | 310 | 42742 | 42743
13 | 4136 | 1 | 310 | 42743 | 42744
14 | 3824 | 1 | 310 | 42744 | 42745
15 | 3512 | 1 | 310 | 42745 | 42746
16 | 3200 | 1 | 310 | 42746 | 42747
17 | 2888 | 1 | 310 | 42747 | 42748
18 | 2576 | 1 | 310 | 42748 | 42749
19 | 2264 | 1 | 310 | 42749 | 42750
20 | 1952 | 1 | 310 | 42750 | 42751
21 | 1640 | 1 | 310 | 42751 | 42752
22 | 1328 | 1 | 310 | 42752 | 42753
23 | 1016 | 1 | 310 | 42753 | 42754
24 | 704 | 1 | 310 | 42754 | 0
(24 rows)
䞊蚘が、Page Pruning凊理が起こる盎前のテヌブルブロックの状態です。曎新のチェヌンが長くなっおいるこずが分かりたす。

itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 65 00 00 00
(1 row)
䞀方、䞊蚘がむンデックスの状態です。テヌブルのレコヌドが䜕床も曎新されおいるにも関わらず、むンデックス゚ントリは最初に䜜成したものから倉わっおいない曎新されおいないこずが分かりたす。

この状態でレコヌドのUPDATEを行うず、Page Pruning凊理が実行されたす。

lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 24 | 2 | 0 | |
2 | 7568 | 1 | 310 | 42755 | 0
3 | 0 | 0 | 0 | |
4 | 0 | 0 | 0 | |
5 | 0 | 0 | 0 | |
6 | 0 | 0 | 0 | |
7 | 0 | 0 | 0 | |
8 | 0 | 0 | 0 | |
9 | 0 | 0 | 0 | |
10 | 0 | 0 | 0 | |
11 | 0 | 0 | 0 | |
12 | 0 | 0 | 0 | |
13 | 0 | 0 | 0 | |
14 | 0 | 0 | 0 | |
15 | 0 | 0 | 0 | |
16 | 0 | 0 | 0 | |
17 | 0 | 0 | 0 | |
18 | 0 | 0 | 0 | |
19 | 0 | 0 | 0 | |
20 | 0 | 0 | 0 | |
21 | 0 | 0 | 0 | |
22 | 0 | 0 | 0 | |
23 | 0 | 0 | 0 | |
24 | 7880 | 1 | 310 | 42754 | 42755
(24 rows)
この時、むンデックス゚ントリは倉化せず、盞倉わらず ctid=(0,1) を指しおいたす。

itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------
1 | (0,1) | 12 | f | f | 65 00 00 00
(1 row)
この時、テヌブルのlp==1のレコヌドはlp_flags==2ずなっおいたすが、このlp_flags==2ずいうのは「HOT曎新埌のリダむレクト」を意味し、その時のlp_offはブロック内のオフセットではなく、ラむンポむンタの番号を意味しおいたす詳现は゜ヌスコヌドの include/storage/itemid.h を参照しおください。

぀たり、レコヌドを探す順序ずしおは、
  • むンデックスの ctid==(0,1) からテヌブルのlp==1のレコヌドを芋る
  • lp==1がリダむレクトなので、lp_off==24からラむンポむンタ24番lp==24を芋る
  • lp==24は削陀枈t_xmax==42755なので、チェヌンをたどっお次の新しいレコヌドlp==2を探す
  • lp==2はただ生きおいるので、このレコヌドを䜿う
ずいう流れになりたす。

■たずめ

今回は、HOT Updateにおけるテヌブルのレコヌドずむンデックスの関係に぀いお解説したした。

PostgreSQLは远蚘型のストレヌゞアヌキテクチャを取るために、曎新凊理によっお叀いレコヌドが増えおいくこずはその通りなのですが、実際には、これたで芋おきた通り、レコヌドやブロックが極力増えないような工倫が随所に凝らされおいたす。

PostgreSQLがどのように動䜜しおいるのかを正しく理解し、その特城をうたく捉えお、蚭蚈やパフォヌマンスチュヌニングしおいただければず思いたす。

明日は、ストレヌゞアヌキテクチャの最終回ずしお「FILLFACTOR」を取り䞊げたす。

では、たた。
↧

PostgreSQLのストレヌゞアヌキテクチャFILLFACTOR線

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 19です。

昚日たでの゚ントリで、PostgreSQLの远蚘型アヌキテクチャの基本的な仕組みず、「远蚘型であるがゆえの課題」にどのように察凊しおいるのかを解説しおきたした。

ストレヌゞアヌキテクチャ小咄シリヌズの最終回である今回は、「FILLFACTOR」ず呌ばれる仕組みに぀いお、その動䜜しおいる様子を芋ながら解説したす。

他のRDBMSをご存じの方のボキャブラリヌに盎すず、OracleやDB2で蚀うずころのPCTFREE、SQL ServerのFILLFACTORず䌌たような圹割の機胜になりたす。

■ブロックに空き領域が無い時の曎新凊理


ブロック内郚が既にいっぱいで空き領域が無い時にレコヌドを远加たたは曎新しようずした堎合、特に前々回に解説したPage Pruningを実斜しおも空き領域が無かった堎合、PostgreSQLではどのように凊理されるのでしょうか。

結論から蚀うず、同䞀ブロック内に新しいレコヌドを远蚘する領域が無いため、他のブロックに新しいレコヌドを曞き蟌むこずになりたす。

テヌブルのブロック構造を思い出しながら、具䜓的な䟋で芋おみたしょう。


以䞋の䟋は、テヌブルに25件のレコヌドをINSERTしおいった状態のテヌブルです。

1぀のブロックに25件のレコヌドが含たれおいお、テヌブルがその1぀のブロックのみでできおいたす。既にブロック党䜓にレコヌドが配眮されおおり、これ以䞊レコヌドを远蚘する空き領域が無い状態になっおいたす。

testdb=# INSERT INTO t1 VALUES ( 125, 'insert 125 ' );
INSERT 0 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46798 | 0
2 | 7568 | 1 | 311 | 46799 | 0
3 | 7256 | 1 | 311 | 46800 | 0
4 | 6944 | 1 | 311 | 46801 | 0
5 | 6632 | 1 | 311 | 46802 | 0
6 | 6320 | 1 | 311 | 46803 | 0
7 | 6008 | 1 | 311 | 46804 | 0
8 | 5696 | 1 | 311 | 46805 | 0
9 | 5384 | 1 | 311 | 46806 | 0
10 | 5072 | 1 | 311 | 46807 | 0
11 | 4760 | 1 | 311 | 46808 | 0
12 | 4448 | 1 | 311 | 46809 | 0
13 | 4136 | 1 | 311 | 46810 | 0
14 | 3824 | 1 | 311 | 46811 | 0
15 | 3512 | 1 | 311 | 46812 | 0
16 | 3200 | 1 | 311 | 46813 | 0
17 | 2888 | 1 | 311 | 46814 | 0
18 | 2576 | 1 | 311 | 46815 | 0
19 | 2264 | 1 | 311 | 46816 | 0
20 | 1952 | 1 | 311 | 46817 | 0
21 | 1640 | 1 | 311 | 46818 | 0
22 | 1328 | 1 | 311 | 46819 | 0
23 | 1016 | 1 | 311 | 46820 | 0
24 | 704 | 1 | 311 | 46821 | 0
25 | 392 | 1 | 311 | 46822 | 0
(25 rows)

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
8192
(1 row)

testdb=#
この状態でレコヌドを曎新するず、どうなるのでしょうか。

testdb=# UPDATE t1 SET uname = 'update 101 ' WHERE uid=101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46798 | 46823
2 | 7568 | 1 | 311 | 46799 | 0
3 | 7256 | 1 | 311 | 46800 | 0
4 | 6944 | 1 | 311 | 46801 | 0
5 | 6632 | 1 | 311 | 46802 | 0
6 | 6320 | 1 | 311 | 46803 | 0
7 | 6008 | 1 | 311 | 46804 | 0
8 | 5696 | 1 | 311 | 46805 | 0
9 | 5384 | 1 | 311 | 46806 | 0
10 | 5072 | 1 | 311 | 46807 | 0
11 | 4760 | 1 | 311 | 46808 | 0
12 | 4448 | 1 | 311 | 46809 | 0
13 | 4136 | 1 | 311 | 46810 | 0
14 | 3824 | 1 | 311 | 46811 | 0
15 | 3512 | 1 | 311 | 46812 | 0
16 | 3200 | 1 | 311 | 46813 | 0
17 | 2888 | 1 | 311 | 46814 | 0
18 | 2576 | 1 | 311 | 46815 | 0
19 | 2264 | 1 | 311 | 46816 | 0
20 | 1952 | 1 | 311 | 46817 | 0
21 | 1640 | 1 | 311 | 46818 | 0
22 | 1328 | 1 | 311 | 46819 | 0
23 | 1016 | 1 | 311 | 46820 | 0
24 | 704 | 1 | 311 | 46821 | 0
25 | 392 | 1 | 311 | 46822 | 0
(25 rows)

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 1));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46823 | 0
(1 row)

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
16384
(1 row)

testdb=#
空き領域が無い状態でレコヌドを曎新UPDATEするず、曎新前の叀いレコヌドlp==1は削陀t_xmaxが蚭定されるされたすが、同じブロックに空き領域が無いPage Pruningをしおも空き領域を䜜れないため、新しいレコヌドは異なる新しい2番目のブロックに曞き蟌たれおいたす。新しいブロックが䜜成されたこずで、pg_relation_sizeで取埗されるテヌブルサむズが8kBから16kBに増加しおいるこずが分かりたす。

これは、もずもず25件のレコヌドが1぀のブロックに収たっおいたものの、曎新する際に远蚘するための空き領域が足りないため新しいブロックを䜜成したテヌブルファむルを拡匵したずいうこずを瀺しおいたす。

■「別ブロックぞの曎新凊理」の䜕が問題か


では、この「別ブロックぞの曎新凊理」の䜕が問題なのでしょうか。今たで芋おきた「同じブロック内での曎新」ず比べお䜕が違うのでしょうか。

それは、「曎新凊理の際に別ブロックぞのレコヌド远蚘を行うず、䜙分なI/O凊理が発生する」ずいうこずです。

同䞀のブロック内での曎新凊理であれば、共有バッファ内の8kBの読み曞きだけで凊理が完結するこずが担保されたす。デヌタベヌスのI/O凊理はブロック単䜍で行われるので、これは圓然のこずです。

それに察しお、異なるブロックぞの曎新凊理になるず、「異なるブロックを読み蟌む」ずいうI/O凊理が発生する可胜性がある、ずいうこずです。もう少し正確に蚀うず、
  • 空き領域のあるブロックを探す
  • 空き領域のあるブロックが芋぀からない堎合は、ファむルを拡匵しお新しいブロックを䜜る
  • ブロックをディスクから共有バッファに読み蟌む
  • 共有バッファぞ読み蟌んだブロックに察しお新しいレコヌドを曞き蟌む
ずいう凊理が行われるこずになり、パフォヌマンス的に最悪のケヌスではディスクをファむルを拡匵したり、読み蟌んだり、ずいったディスクI/Oが発生するこずになり、このこずがパフォヌマンスに倧きな悪圱響を䞎えたす。なぜなら、メモリぞの操䜜に察しおディスクI/Oは、3ケタ近く凊理速床が遅いからです。

そもそも、デヌタベヌスにおいおはストレヌゞが远蚘型構造であるかどうかに関わらず、ブロックぞのアクセスを劂䜕に削枛するか、䜙蚈なブロックアクセスを枛らすか、ずいうこずが重芁です。

にも関わらずブロックを䞀杯たで䜿っおしたうず、異なるブロックぞの曎新凊理が頻発し、ディスクI/Oが増えおしたう可胜性が高たりたす。远蚘型のストレヌゞアヌキテクチャを持぀PostgreSQLでは、この可胜性が特に高くなりたす。

たた、圓然ながら曎新の時だけではなく、前回たでに芋おきたように参照の時にもPostgreSQLの内郚では叀いタプルからチェヌンを蟿るようにしお最新のタプルを探したすので、曎新のチェヌンが異なるブロックにたたがっおいるずいうこずは、参照の時にも䜙蚈なI/O凊理を発生させるこずになり、パフォヌマンスに圱響を䞎えたす。

■匷制的に空き領域を確保しお曎新UPDATEに備える


ずいうわけで、前述のような状況別ブロックぞの远蚘が発生するず、「あぁ、同じブロックに空き領域があればなぁ」ず考えるのが人情ずいうものでしょう。この「UPDATEに備えお同じブロックに意図的に空き領域を確保しおおく」ための仕組みが「FILLFACTOR」ず呌ばれるものです。


これは、INSERTをする際には指定した割合の空き領域を確保しおおき、UPDATEをする時にその空き領域を䜿う、ずいう仕組みです。このように領域を予玄しおおくこずによっお、「曎新凊理の際に同じブロックの空き領域を䜿える」ずいう可胜性が飛躍的に高たりたす。

実際の動䜜を芋おみたしょう。

以䞋は、先の䟋ずたったく同じレコヌドを25ä»¶INSERTしたテヌブルの状態です。

先ほどず違うのは、同じ25件のレコヌドであるにも関わらず、今回は1ブロックに収たらず既に2ブロック䜿っおいるこず、そしお1ブロック目に少し空き領域があるこずです最埌のlp_offが1000バむト以䞊ありたす。

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46856 | 0
2 | 7568 | 1 | 311 | 46857 | 0
3 | 7256 | 1 | 311 | 46858 | 0
4 | 6944 | 1 | 311 | 46859 | 0
5 | 6632 | 1 | 311 | 46860 | 0
6 | 6320 | 1 | 311 | 46861 | 0
7 | 6008 | 1 | 311 | 46862 | 0
8 | 5696 | 1 | 311 | 46863 | 0
9 | 5384 | 1 | 311 | 46864 | 0
10 | 5072 | 1 | 311 | 46865 | 0
11 | 4760 | 1 | 311 | 46866 | 0
12 | 4448 | 1 | 311 | 46867 | 0
13 | 4136 | 1 | 311 | 46868 | 0
14 | 3824 | 1 | 311 | 46869 | 0
15 | 3512 | 1 | 311 | 46870 | 0
16 | 3200 | 1 | 311 | 46871 | 0
17 | 2888 | 1 | 311 | 46872 | 0
18 | 2576 | 1 | 311 | 46873 | 0
19 | 2264 | 1 | 311 | 46874 | 0
20 | 1952 | 1 | 311 | 46875 | 0
21 | 1640 | 1 | 311 | 46876 | 0
22 | 1328 | 1 | 311 | 46877 | 0
23 | 1016 | 1 | 311 | 46878 | 0
(23 rows)

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 1));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46879 | 0
2 | 7568 | 1 | 311 | 46880 | 0
(2 rows)

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
16384
(1 row)

testdb=#
この状態でUPDATE凊理を行っおみたす。

testdb=# UPDATE t1 SET uname = 'update 101 ' WHERE uid=101;
UPDATE 1
testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 0));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46856 | 46881
2 | 7568 | 1 | 311 | 46857 | 0
3 | 7256 | 1 | 311 | 46858 | 0
4 | 6944 | 1 | 311 | 46859 | 0
5 | 6632 | 1 | 311 | 46860 | 0
6 | 6320 | 1 | 311 | 46861 | 0
7 | 6008 | 1 | 311 | 46862 | 0
8 | 5696 | 1 | 311 | 46863 | 0
9 | 5384 | 1 | 311 | 46864 | 0
10 | 5072 | 1 | 311 | 46865 | 0
11 | 4760 | 1 | 311 | 46866 | 0
12 | 4448 | 1 | 311 | 46867 | 0
13 | 4136 | 1 | 311 | 46868 | 0
14 | 3824 | 1 | 311 | 46869 | 0
15 | 3512 | 1 | 311 | 46870 | 0
16 | 3200 | 1 | 311 | 46871 | 0
17 | 2888 | 1 | 311 | 46872 | 0
18 | 2576 | 1 | 311 | 46873 | 0
19 | 2264 | 1 | 311 | 46874 | 0
20 | 1952 | 1 | 311 | 46875 | 0
21 | 1640 | 1 | 311 | 46876 | 0
22 | 1328 | 1 | 311 | 46877 | 0
23 | 1016 | 1 | 311 | 46878 | 0
24 | 704 | 1 | 311 | 46881 | 0
(24 rows)

testdb=# SELECT lp,lp_off,lp_flags,lp_len,t_xmin,t_xmax FROM heap_page_items(get_raw_page('t1', 1));
lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax
----+--------+----------+--------+--------+--------
1 | 7880 | 1 | 311 | 46879 | 0
2 | 7568 | 1 | 311 | 46880 | 0
(2 rows)

testdb=# SELECT pg_relation_size('t1');
pg_relation_size
------------------
16384
(1 row)

testdb=#
UPDATE凊理を行った結果を芋おみるず、1番目のブロックのlp==1のレコヌドが削陀され、同じブロックにlp==24ずしお曎新されおいるこずが分かりたす。぀たり、1番目のブロックに空き領域が確保されおいたために、UPDATE凊理の際にその領域を䜿うこずができおいる、ずいうこずが分かりたす。

FILLFACTORの蚭定方法はCREATE TABLEにオプションを付ける方法、ALTER TABLEで蚭定する方法などがありたす。詳现はマニュアルを参照しおください。

CREATE TABLE
http://www.postgresql.jp/document/9.0/html/sql-createtable.html
ALTER TABLE
http://www.postgresql.jp/document/9.0/html/sql-altertable.html

なお、FILLFACTORの倀は「10100%」で指定したすが、テヌブルにおける蚭定のデフォルトは「100」、぀たり「空き領域無しで党郚詰め蟌む」ずいう蚭定になっおいたす。぀たり、䜕も蚭定しないずFILLFACTORの恩恵はたったく受けられたせんのでご泚意ください。

■たずめ


今回は、UPDATE凊理の際にできるだけ同䞀ブロック内の空き領域を䜿うための「FILLFACTOR」の仕組みに぀いお、その実際の動䜜を芋ながら解説しおきたした。

FILLFACTORは、特に曎新凊理の倚いワヌクロヌドで性胜の安定に寄䞎したす。䞀方で、「空き領域を確保しおおく」ずいう機胜の特性䞊、テヌブルファむル党䜓のサむズは倧きくなりたす。

このようなトレヌドオフが存圚したすので、自身のデヌタベヌスのワヌクロヌドのタむプをよく芋極めお、これらの機胜をうたく䜿いこなしおいただければず思いたす。

では、たた。
↧

次期バヌゞョンの9.3で実装された曎新可胜ビュヌを詊しおみる

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 20です。

先日、ネット䞊でも少し話題になっおいたしたが、開発䞭のPostgreSQLの次バヌゞョン9.3に「曎新可胜ビュヌ」をサポヌトするコヌドがコミットされたした。

pgsql: Support automatically-updatable views.
http://archives.postgresql.org/pgsql-committers/2012-12/msg00154.php

今回はこの曎新可胜ビュヌに぀いお、その制玄なども含めおどのようなものなのかを芋おみたいず思いたす。

なお、今回は開発䞭のバヌゞョンで詊したすので、詊しおみたい方はPostgreSQLのGitレポゞトリから゜ヌスコヌドを取埗しお自身でビルドしおください。

Working with Git - PostgreSQL wiki
http://wiki.postgresql.org/wiki/Working_with_Git

■「曎新可胜ビュヌ」ずは


「曎新可胜ビュヌ」ずはどういった機胜でしょうか。

RDBMSにおけるビュヌのしくみを理解しおいる方は、それを思い浮かべおいただければ分かるかず思いたすが、ビュヌの定矩が「十分に簡単」である堎合、ビュヌに察するINSERTやUPDATE、DELETE凊理は、元テヌブルに察する曎新凊理に曞き換えるこずが可胜になるはずです。

この「ビュヌに察する曎新凊理を、元テヌブルに察する曎新凊理ぞ自動的に曞き換える」ずいう機胜が、「automatically updatable、自動的に曎新できる」ビュヌず蚀われる機胜になりたす。

■曎新可胜ビュヌの制玄・前提条件


曎新可胜ビュヌが実装されたずは蚀え、「十分に簡単」ずいう条件が付いおいるように、すべおのビュヌに察しお曎新が可胜なわけではなく、その制玄・前提条件がありたす。

なぜなら、「ビュヌぞの曎新凊理」ずいうのは、ビュヌの定矩から参照元テヌブルのレコヌド構造、぀たり通垞のビュヌずは「逆方向」に展開・倉換する䜜業に他なりたせんので、論理的に元テヌブルのレコヌドを再構成できないず実行ができないのです。

曎新可胜ビュヌずしおの前提条件は、開発版のオフィシャルマニュアルに蚘茉がありたしたので、以䞋に簡単に日本語に翻蚳しおきたす。
  • 単䞀のテヌブル、たたは曎新可胜ビュヌのみをFROM句に持぀こず。結合をしおいないこず
  • ビュヌの定矩がWITH、DISTINCT、GROUP BY、HAVING、LIMIT、OFFSETを持たないこず。
  • ビュヌの定矩がUNION、INTERSECT、EXCEPTを持たないこず。
  • ビュヌの定矩のselectリストに出おくるカラムが、元テヌブルのカラムをそのたた参照しおいるこず。expressionやリテラル、関数であっおはならない。システムカラムであっおもならない。
  • 元テヌブルのカラムが二回以䞊ビュヌの定矩に出おこないこず。
  • ビュヌがsecurity_barrier属性を持たないこず。
この蟺りの制玄・前提条件は、他のRDBMSず䌌おいるずころかもしれたせん。

詳现に぀いおは、オフィシャルマニュアルの蚘茉を参照しおください。

PostgreSQL: Documentation: devel: CREATE VIEW
http://www.postgresql.org/docs/devel/static/sql-createview.html

■曎新可胜ビュヌを䜿っおみる


では最初に、もっずも基本的なビュヌを定矩しおレコヌドを曎新できるか詊しおみたす。

[snaga@devsv02 updatableview]$ /tmp/pgsql/bin/psql testdb
psql (9.3devel)
Type "help" for help.

testdb=# CREATE TABLE t1 ( uid INTEGER PRIMARY KEY, uname TEXT NOT NULL );
CREATE TABLE
testdb=# CREATE VIEW v1 AS SELECT * FROM t1;
CREATE VIEW
testdb=# select * FROM t1;
uid | uname
-----+-------
(0 rows)

testdb=#
たず、元テヌブルに察しお1件レコヌドをINSERTしたす。

testdb=# insert into t1 values ( 101, 'Park Gyu-Ri' );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+-------------
101 | Park Gyu-Ri
(1 row)

testdb=# select * from v1;
uid | uname
-----+-------------
101 | Park Gyu-Ri
(1 row)

testdb=#
圓然、元テヌブルからもビュヌからもレコヌドが芋えたす。

次に、今床はビュヌに察しお2件目のレコヌドをINSERTしおみたす。

testdb=# insert into v1 values ( 102, 'Han Seung-Yeon' );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
(2 rows)

testdb=# select * from v1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
(2 rows)

testdb=#
ビュヌに察するレコヌドのINSERTも成功し、元テヌブルに察しおレコヌドがINSERTされおいるこずが分かりたす。この時の実行プランを芋おみるず、INSERT文にビュヌv1を指定しおいるにも関わらず、内郚的にはテヌブルt1に曎新しようずしおいるこずが分かりたす。

testdb=# explain insert into v1 values ( 103, 'Nicole' );
QUERY PLAN
------------------------------------------------
Insert on t1 (cost=0.00..0.01 rows=1 width=0)
-> Result (cost=0.00..0.01 rows=1 width=0)
(2 rows)

testdb=#
ビュヌv1に察するUPDATEに぀いおも、同様にテヌブルt1ぞの曎新ずしお内郚的に曞き換えられおいたす。

testdb=# explain update v1 set uname = 'Nicole' where uid = 102;
QUERY PLAN
-------------------------------------------------------------------------
Update on t1 (cost=0.00..8.27 rows=1 width=10)
-> Index Scan using t1_pkey on t1 (cost=0.00..8.27 rows=1 width=10)
Index Cond: (uid = 102)
(3 rows)

testdb=#

■ビュヌのselectリストに含たれないカラム


次に、ビュヌのselectリストに含たれないカラムがあった堎合に、どのように扱われるのかを芋おみたす。

「ビュヌのselectリストに含たれない」ずいうこずは、ビュヌぞの曎新時に倀が存圚しおいないため元テヌブルに倀を枡せない、ずいう状況になるはずです。

以䞋の䟋では、uidずunameずいう元テヌブルのカラムのうち、uidカラムだけを䜿っおビュヌを定矩し、そのビュヌに察しお曎新INSERTを行おうずしおいたす。

testdb=# create view v2 as select uid from t1;
CREATE VIEW
testdb=# insert into v2 values ( 103 );
ERROR: null value in column "uname" violates not-null constraint
DETAIL: Failing row contains (103, null).
STATEMENT: insert into v2 values ( 103 );
testdb=#
このINSERT文ぱラヌになりたしたが、゚ラヌになっおいる理由を芋るず、「元テヌブルのunameカラムにnot-null制玄が付いおおり、それに違反しおいるので゚ラヌ」ず蚀われおいるこずが分かりたす。

ここで、元テヌブルのunameカラムにデフォルト倀を蚭定しお、再床ビュヌv2に察しお曎新しおみたす。

testdb=# ALTER TABLE t1 ALTER COLUMN uname SET DEFAULT '(noname)';
ALTER TABLE
testdb=# insert into v2 values ( 103 );
INSERT 0 1
testdb=# select * from t1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
103 | (noname)
(3 rows)

testdb=#
今床ぱラヌにはならず、unameカラムにはデフォルト倀の "(noname)" ずいう倀が蚭定されたした。

ここたで芋るず分かりたすが、ビュヌのselectリストに含たれないカラムは、
  • 元テヌブルのカラムにデフォルト倀があればデフォルト倀に蚭定。
  • デフォルト倀が無ければnullに蚭定。
  • その䞊で、元テヌブルの制玄でチェックされる。
ずいう動きになっおいるこずが分かりたす。

■たずめ


今回は、次のリリヌスで提䟛されるこずになった「曎新可胜ビュヌ」の機胜に぀いお、どのような機胜なのか、そしおどのように動くのかを、簡単にではありたすが芋おきたした。

前述した通り、すべおのビュヌが曎新可胜になるわけではありたせんし、実際に䜿うビュヌはここで瀺したより耇雑ですので、利甚にはいろいろ制玄があるかず思いたす。

しかし、堎合によっおは開発時に䟿利に䜿えるこずもあるかず思いたすので、機䌚があればぜひ詊しおみおいただければず思いたす。

では、たた。
↧
↧

pgTAPを䜿っおPostgreSQL䞊でデヌタベヌスの単䜓テストを行う

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 21です。

PostgreSQLはその拡匵性の高さが倧きな特城ずなっおおり、「プロシヌゞャ蚀語」、いわゆる「PL」ずしお、䞀般的なSQLやPL/pgSQLだけではなく、PerlやPython、RubyやV8なども䜿うこずができたす。

これらのPLを䜿うず、自分の銎染んだ蚀語、特に広く䞀般的に䜿われおいるLLで簡単にロゞックを曞き、これをデヌタベヌス内で実行するこずができるようになりたす。このこずが、最近PostgreSQLがアプリケヌション開発プラットフォヌムずしお泚目を集めおきおいる倧きな理由の䞀぀でしょう。

䞀方で、ロゞックを実装するずいうこずは、そのロゞックが正しく動くこずを確認するためのテストを行わなければなりたせん。

ずいうわけで、今回はPostgreSQL䞊で開発を行う堎合にナニットテストに䜿えるツヌル「pgTAP」を玹介したす。

■単䜓テストツヌル「pgTAP」ずは


pgTAPは、David E. Wheeler氏によっお開発されおいるPostgreSQL甚の単䜓テストツヌルです。

pgTAP: Unit Testing for PostgreSQL
http://pgtap.org/

単䜓テストを実行するのに必芁なさたざたなSQL関数を提䟛しおおり、テストスクリプト内でこれらのSQL関数を䜿うこずで、スキヌマの構造やプロシヌゞャナヌザ定矩関数のロゞックの正しさなどをテストするこずができたす。

デヌタベヌスの単䜓テストをする堎合、䞀般的にはスキヌマの構造、テヌブルの内容、プロシヌゞャやナヌザ定矩関数の動䜜が䞻なテスト察象ずなるず思われたすので、ここではこれらに絞っおテストの実斜方法を解説したす。

■むンストヌル方法


pgTAPはcontribモゞュヌルず同じようにむンストヌルするこずができたす。

コンパむルする際には、pg_configコマンド通垞はPostgreSQLをむンストヌルしたbinディレクトリにありたすにPATHが通っおいるこずを確認し、たた、環境倉数USE_PGXSを "1" に蚭定しおmakeしたす。

[snaga@devsv02 pgtap-0.90.0]$ ls
Changes doc META.json pgtap-schema.control src
compat getos.sh pgtap.control README.md test
contrib Makefile pgtap-core.control sql tocgen
[snaga@devsv02 pgtap-0.90.0]$ env PATH=/usr/pgsql-9.1/bin:$PATH USE_PGXS=1 make
cp sql/pgtap.sql.in sql/pgtap.sql
sed -e 's,MODULE_PATHNAME,$libdir/pgtap,g' -e 's,__OS__,linux,g' -e 's,__VERSION__,0.90,g' sql/pgtap.sql > sql/pgtap.tmp
mv sql/pgtap.tmp sql/pgtap.sql
(...snip...)
cp sql/pgtap.sql sql/pgtap--0.90.0.sql
cp sql/pgtap-core.sql sql/pgtap-core--0.90.0.sql
cp sql/pgtap-schema.sql sql/pgtap-schema--0.90.0.sql
[snaga@devsv02 pgtap-0.90.0]$
コンパむルできたら、rootになっおmake installを実行したす。

[snaga@devsv02 pgtap-0.90.0]$ su
Password:
[root@devsv02 pgtap-0.90.0]# env PATH=/usr/pgsql-9.1/bin:$PATH USE_PGXS=1 make install
/bin/mkdir -p '/usr/pgsql-9.1/share/extension'
/bin/mkdir -p '/usr/share/doc/pgsql/extension'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./pgtap.control ./pgtap-core.control ./pgtap-schema.control '/usr/pgsql-9.1/share/extension/'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./sql/pgtap--0.90.0.sql ./sql/pgtap-core--0.90.0.sql ./sql/pgtap-schema--0.90.0.sql ./sql/pgtap--unpackaged--0.26.0.sql ./sql/pgtap--0.90.0.sql ./sql/pgtap-core--0.90.0.sql ./sql/pgtap-schema--0.90.0.sql '/usr/pgsql-9.1/share/extension/'
/bin/sh /usr/pgsql-9.1/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./doc/pgtap.mmd '/usr/share/doc/pgsql/extension/'
[root@devsv02 pgtap-0.90.0]#
ここたで完了すれば、あずはcontribモゞュヌルず同じように、デヌタベヌスに察しお登録9.1以降ならCREATE EXTENSIONを行いたす。

[snaga@devsv02 pgtap-0.90.0]$ psql -U postgres testdb
psql (9.1.2)
Type "help" for help.

testdb=# CREATE EXTENSION pgtap;
CREATE EXTENSION
testdb=# \q
[snaga@devsv02 pgtap-0.90.0]$

■単䜓テストの構造


pgTAPでテストを曞く堎合には、「基本的なお䜜法」ず蚀えるスクリプトの構造がありたす。

たず、テストを開始する際には、トランザクションを開始し、plan()関数を呌び出しお、これから実行しようずするテストの数を指定したす。

わざわざplan()関数を呌び出しおテストの数を指定するのは、どうやら「これから実行しようずしおいるテストに぀いお、きちんず理解・把握しおいるべきだ」ずいう思想からのようです。ドキュメントを読む限り

䟋えば、13個のナニットテストを実行するスクリプトの堎合、

BEGIN;
SELECT plan(13);
のような圢でスクリプトを始めたす。

plan()関数を実行したら各皮テストを実行したす。各テストの曞き方は埌述したす。

必芁なテストを実行したら、最埌にfinish()関数を呌び出しおテストの完了を宣蚀し、トランザクションをロヌルバックしたす。

SELECT * FROM finish();
ROLLBACK;
これらを蚘述したテストスクリプトは以䞋のようになりたす。

BEGIN;
SELECT plan(1);

SELECT tables_are (
'public',
ARRAY[
't1'
]
);

SELECT * FROM finish();
ROLLBACK;
このようなpgTAP甚テストスクリプトをpsqlコマンドで実行するず、以䞋のような出力を埗られたす。

[snaga@devsv02 t]$ psql -f t1.sql testdb
BEGIN
plan
------
1..1
(1 row)

tables_are
-----------------------------------------------------
ok 1 - Schema public should have the correct tables
(1 row)

finish
--------
(0 rows)

ROLLBACK
[snaga@devsv02 t]$
ここでは、「tables_are」ずいうテストをひず぀だけ実行しおいたすが、圓該テストの結果は「Schema public should have the correct tables」ずなっお成功しおおり、finish()関数の結果も䜕も無いこずから、党䜓ずしお問題なくテストが通ったこずが分かりたす。

なお、テストに倱敗するず、finish()関数が以䞋のような結果を返すので、それを芋お刀別するこずができたす。

finish
-------------------------------------
# Looks like you failed 1 test of 1
(1 row)

■テヌブルずカラムのテスト


それでは、たずはテヌブルの存圚のチェックからテストを行っおみたす。テヌブルの存圚をチェックするためには tables_are() 関数を䜿いたす。

最初の匕数はスキヌマ名を、二番目の匕数にはテヌブル名のリスト配列を指定したす。

SELECT tables_are (
'pgperf',
ARRAY[
'snapshot',
'snapshot_pg_stat_bgwriter'
]
);
成功するず、

tables_are
-----------------------------------------------------
ok 1 - Schema pgperf should have the correct tables
(1 row)

倱敗するず、

tables_are
-----------------------------------------------------------------
not ok 1 - Schema pgperf should have the correct tables +
# Failed test 1: "Schema pgperf should have the correct tables"+
# Missing tables: +
# snapshot_pg_stat_user_tables +
# snapshot_pg_database_size
(1 row)

ずいったメッセヌゞが返されたす。

䞊蚘の゚ラヌメッセヌゞでは snapshot_pg_stat_user_tables ず snapshot_pg_database_size が足りないテヌブルMissing tablesずしお指摘されおいたす。

次にカラムのテストを行いたす。指定したテヌブル、指定した名前ず型のカラムが存圚しおいるかどうかを確認したす。このテストには col_type_is() ずいう関数を䜿いたす。

以䞋の䟋は、pgperfスキヌマのsnapshot_pg_stat_bgwriterテヌブルに、それぞれinteger型のsidカラム、bigint型のcheckpoints_timedカラムが存圚しおいるかどうかをテストしおいたす。

SELECT col_type_is('pgperf', 'snapshot_pg_stat_bgwriter', 'sid', 'integer', 'sid');
SELECT col_type_is('pgperf', 'snapshot_pg_stat_bgwriter', 'checkpoints_timed', 'bigint', 'checkpoints_timed');
カラムの有無のみを確認する型のチェックをしないhas_column()ずいう関数もあるのですが、col_type_is() 関数はカラムの有無もチェックしおくれるので、こちらを䜿えば十分でしょう。

成功するず、

col_type_is
-------------
ok 3 - sid
(1 row)
ずいう結果を返し、倱敗するず、

col_type_is
-------------------------
not ok 4 - sid +
# Failed test 4: "sid" +
# have: integer+
# want: bigint
(1 row)
ずいう結果を返したす。これは「bigintであるこずを期埅しおいるのにwant、実際にはintegerだったhave」ずいう意味です。

たた、カラムそのものが存圚しない堎合には、

col_type_is
------------------------------------------------------------------
not ok 4 - sid2 +
# Failed test 4: "sid2" +
# Column pgperf.snapshot_pg_stat_bgwriter.sid2 does not exist
(1 row)
ずいう゚ラヌを返したす。

■ク゚リ実行結果のテスト


次は、ク゚リの実行結果のテスト方法です。

以䞋のテヌブルに察するク゚リをサンプルずしおテストの曞き方を芋おみたす。

testdb=> SELECT * FROM t1;
uid | uname
-----+----------------
101 | Park Gyu-Ri
102 | Han Seung-Yeon
103 | Nicole
104 | Koo Ha-Ra
105 | Kang Ji-Young
(5 rows)
ク゚リを実行した結果をテストするためには、results_eq()関数を䜿いたす。

䞀぀目の匕数は実行するク゚リの文字列、二぀目のク゚リは結果を蚘述したす。

以䞋のように出力される結果が単䞀の倀の堎合、

testdb=> SELECT uname FROM t1 WHERE uid=103;
uname
--------
Nicole
(1 row)
出力は、単䞀の芁玠を持぀配列ずしお定矩したす。

SELECT results_eq (
'SELECT uname FROM t1 WHERE uid=103',
ARRAY[ 'Nicole' ]
);
たた、耇数の倀を返华するク゚リの堎合には、

testdb=> SELECT uname FROM t1 WHERE uid=103 OR uid=104;
uname
-----------
Nicole
Koo Ha-Ra
(2 rows)
以䞋のように配列芁玠を増やすこずで指定するこずができたす。

SELECT results_eq (
'SELECT uname FROM t1 WHERE uid=103 OR uid=104',
ARRAY[ 'Nicole', 'Koo Ha-Ra' ]
);
たた、レコヌドを返华するク゚リの堎合、

testdb=> SELECT * FROM t1 WHERE uid=103;
uid | uname
-----+--------
103 | Nicole
(1 row)
VALUESの衚蚘を甚いおレコヌドずしお蚘述したす。

SELECT results_eq (
'SELECT * FROM t1 WHERE uid=103',
$$ VALUES (103, 'Nicole') $$
);
レコヌドに぀いおも、耇数の結果が返华される堎合には、

testdb=> SELECT * FROM t1 WHERE uid=103 OR uid=104;
uid | uname
-----+-----------
103 | Nicole
104 | Koo Ha-Ra
(2 rows)
以䞋のように耇数のレコヌドを蚘述するこずができたす。

SELECT results_eq (
'SELECT * FROM t1 WHERE uid=103 OR uid=104',
$$ VALUES (103, 'Nicole'), (104, 'Koo Ha-Ra') $$
);

■プロシヌゞャ、ナヌザ定矩関数のテスト


プロシヌゞャやナヌザ定矩関数のテストも、ク゚リのテストず同じ方法で実行するこずができたす。

SELECTク゚リでプロシヌゞャや関数を呌び出し、その結果を確認したす。

SELECT results_eq(
'SELECT pgperf.create_snapshot_pg_stat_bgwriter(1)',
ARRAY[ true ]
);

■pg_proveコマンドによる䞀括実行ず集蚈


なお、pgTAPによるテストの実行を支揎するツヌルずしお、同じ䜜者からpg_proveずいうツヌルがCPANで提䟛されおいたす。

David E. Wheeler / TAP-Parser-SourceHandler-pgTAP - search.cpan.org
http://search.cpan.org/dist/TAP-Parser-SourceHandler-pgTAP/

このpg_proveは、pgTAPで䜿甚するテストスクリプトを䞀括しお実行しお、その䞭に含たれるナニットテストの結果を集蚈しおくれるツヌルで、実行するず「いく぀のテストを実行したか、そのうちいく぀倱敗したか、どのような倱敗だったか」ずいうこずをレポヌトしおくれたす。

以䞋はpg_proveコマンドを䜿っおテストを実行しおいる様子ですが、「Files=1, Tests=18」ずありたすので、1぀のファむルの䞭にある18のテストを実行し、その結果が「Result: PASS」、぀たりすべお成功であったこずを報告しおいたす。

[snaga@devsv02 t]$ pg_prove -d testdb test_pg_stat_bgwriter.sql
t/test_pg_stat_bgwriter.sql .. ok
All tests successful.
Files=1, Tests=18, 0 wallclock secs ( 0.02 usr + 0.01 sys = 0.03 CPU)
Result: PASS
[snaga@devsv02 t]$
䞀方、以䞋はテストに倱敗しおいるケヌスですが、「Failed tests: 4-5」、぀たり4番目ず5番目のテストに倱敗しおおり、どこの結果が間違っおいたのかもレポヌトされおいたす。

たた、結果ずしお圓然ながらテスト党䜓ずしおも倱敗Result: FAILであったこずをレポヌトしおいたす。

[snaga@devsv02 t]$ pg_prove -d testdb test_pg_stat_bgwriter.sql
t/test_pg_stat_bgwriter.sql .. 1/21
# Failed test 4: "sid"
# have: integer
# want: bigint
# Failed test 5: "sid2"
# Column pgperf.snapshot_pg_stat_bgwriter.sid2 does not exist
# Looks like you failed 2 tests of 21
t/test_pg_stat_bgwriter.sql .. Failed 2/21 subtests

Test Summary Report
-------------------
t/test_pg_stat_bgwriter.sql (Wstat: 0 Tests: 21 Failed: 2)
Failed tests: 4-5
Files=1, Tests=21, 0 wallclock secs ( 0.02 usr + 0.01 sys = 0.03 CPU)
Result: FAIL
[snaga@devsv02 t]$

■たずめ


今回は、PostgreSQLでアプリケヌションを開発する際に必芁ずなる単䜓テストを実行するためツヌルであるpgTAPの基本的な䜿い方を玹介したした。

最初に述べたように、PostgreSQLの倧きな特城は拡匵性であり、さたざたなプロシヌゞャ蚀語でアプリケヌションロゞックをPostgreSQL䞊で動䜜させるこずができたす。

たた、単䜓テストのみならず、JenkinsなどのCIツヌルず組み合わせれば、さらにテストを自動化しお開発プロセスの質を向䞊させるこずができるようになりたす。

ぜひ、こういったツヌルをうたく掻甚しお、アプリケヌション開発に圹立おおいただければず思いたす。

では、たた。
↧

デヌタブロックサむズの倉曎ず分析系ク゚リぞの性胜圱響SSD線

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 22です。

最近、PostgreSQL䞊でデヌタ分析凊理をよく行うようになっおきたした。たた、いろいろなずころでSSDも䜿うようになっおきたした。

PostgreSQLずデヌタ分析ずSSD、ずいう組合せを考えた時、どのようにチュヌニングするのが望たしいのか、あるいはどのようなチュヌニングができるのか、個人的にはただただ詊行錯誀䞭だったりしたす。

SSDはハヌドディスクず比べおブロックサむズが倧きいずいう特城がありたす。たた、DWH系のデヌタベヌスでは倧きいブロックサむズを䜿うずパフォヌマンス䞊のメリットがある、ず蚀われおいたす。

今回は、SSD䞊でPostgreSQLを䜿っおデヌタ分析系の凊理を行う時に、デヌタブロックのサむズを倉えるずク゚リのパフォヌマンスにどのような圱響を䞎えるのか、実際にク゚リを実行しながら芋おいきたす。

■ブロックサむズの倉曎方法ず確認方法


PostgreSQLでデヌタブロックのサむズを倉曎するには、PostgreSQLのブログラムバむナリをビルドする際にブロックサむズを指定する必芁がありたす。

具䜓的にはconfigureスクリプトのオプションに--with-blocksizeずいうオプションを䞎えお、ここでブロックサむズを指定したす。


./configure --prefix=/usr/local/pgsql --with-blocksize=32
configureで指定できるブロックサむズはキロバむト単䜍で指定する必芁があり、指定できる倀は「1, 2, 4, 8, 16, 32」のいずれかになりたす。これより倧きいサむズにするためには、コヌドをハックする必芁がありたすちょっず倉えただけでは make check で゚ラヌになりたした・・・

たた、皌働しおいるPostgreSQLのデヌタブロックのサむズを確認するためには、SQLコマンドずしおSHOW block_sizeを実行したす。

postgres=# show block_size;
block_size
------------
32768
(1 row)

postgres=#

■テスト環境ずデヌタ


今回テストを行ったのは以䞋の環境です。
  • NEC Express5800 GT110b
  • Xeon Intel Xeon X3440 2.53GHz (1P4C)
  • Unbeffered ECC 16GB
  • Intel 520 Series 180GB
  • Hitachi Deskstar 7K1000 HDS72101
  • Red Hat Enterprise Linux 6.3 (x86_64)
OS領域はハヌドディスク䞊に確保し、PostgreSQLのプログラムはOS領域に、PostgreSQLのデヌタベヌスクラスタはSSD䞊に配眮しおいたす。

/dev/mapper/vg_devsv03-lv_root on / type ext4 (rw)
/dev/mapper/vg_devsv03-lv_home on /home type ext4 (rw)
/dev/sda1 on /disk/disk1 type ext4 (rw,discard)
PostgreSQLの蚭定は、postgresql.confの以䞋のパラメヌタのみ倉曎しおいたす。

shared_buffers = 2048MB
wal_buffers = 32MB
checkpoint_segments = 128
checkpoint_timeout = 60min
autovacuum = off
今回、テストで䜿ったデヌタは、OSDL DBT-3のスキヌマずテストデヌタです。

テストデヌタはスケヌルファクタを「10」ずしお生成しおおり、デヌタサむズは、ロヌドする前のCSVファむルで10GB、ロヌド枈みデヌタベヌスサむズテヌブルおよびむンデックスで25GBずなっおいたす。

■デヌタのロヌディングずむンデックスの䜜成


たず、テヌブルぞのデヌタロヌディングず䞻キヌ制玄の远加、およびむンデックス䜜成のパフォヌマンスを蚈枬したす。

察象ずなるテヌブルは以䞋の8぀のテヌブルです。

dbt3=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------------+-------+-------+------------+-------------
public | customer | table | snaga | 276 MB |
public | lineitem | table | snaga | 8324 MB |
public | nation | table | snaga | 8192 bytes |
public | orders | table | snaga | 1979 MB |
public | part | table | snaga | 317 MB |
public | partsupp | table | snaga | 1335 MB |
public | region | table | snaga | 8192 bytes |
public | supplier | table | snaga | 17 MB |
(8 rows)

dbt3=#
テヌブルにデヌタをロヌドした埌、ALTER TABLEコマンドで各テヌブルに䞻キヌ制玄を远加し、か぀CREATE INDEXコマンドで15個のむンデックスを䜜成しおいたす。

これらの凊理を、PostgreSQLのデヌタブロックをそれぞれ8kBブロック、16kBブロック、32kBブロックに倉曎したデヌタベヌスに察しお実行した結果が以䞋のグラフです。


青の郚分はテヌブルにデヌタをロヌドするのに芁した時間、玫は䞻キヌ制玄の远加に芁した時間、クリヌム色の郚分は远加のむンデックス䜜成に芁した時間で、積み䞊げグラフで秒数で衚瀺しおいたす。

このグラフを芋るず、デヌタのロヌディングやむンデックスの䜜成などに぀いおは、あたりパフォヌマンス䞊の向䞊が芋られないこずが分かりたす。むしろ、ブロックサむズが8kBの時ず比べお、32kBの時にはパフォヌマンスが若干䜎䞋しおいたす。党䜓ずしおはさほどむンパクトはありたせんが

なお、今回は時間の関係䞊、デヌタのロヌディングずむンデックスの䜜成に぀いおは、䞀回しか蚈枬しおいたせん。耇数回蚈枬しお平均を取る、ずいった凊理は行っおいたせんので、その点はご承知おきください。

■テヌブルフルスキャン


デヌタベヌスの構築が完了したら、ク゚リの実行を行っおみたす。

たずは、もっずも単玔なテヌブルのフルスキャンを行いたす。

select count(*) from lineitem;
lineitemテヌブルぞのcount(*)の凊理は、8GB匷のテヌブルをシヌケンシャルスキャンするこずになり、実行プランは以䞋の通りです。

QUERY PLAN
---------------------------------------------------------------------------
Aggregate (cost=1279737.95..1279737.96 rows=1 width=0)
-> Seq Scan on lineitem (cost=0.00..1129772.56 rows=59986156 width=0)
(2 rows)
以䞋が、ブロックサむズ別のlineitemテヌブルぞの実行時間5回平均です。


これを芋るずブロックサむズを8kBから32kBぞず倧きくするに埓っおパフォヌマンスが向䞊しおいるように芋えたす。

ブロックサむズを倉えるこずで、本圓にこれだけパフォヌマンスが向䞊しおいるのでしょうか。

その点を確認するために、5回分の実行時間を個別に比范したものが以䞋のグラフになりたす。


これを芋るず䞀目瞭然ですが、8kBブロックの堎合、1回目から5回目たで実行時間がほずんど倉わっおいないのに察しお、16kBブロックの堎合は4回目から、32kBブロックの堎合は2回目から実行時間が倧幅に短瞮されおいたす。

この「実行時間が短くなっおいく」理由はただよく分かっおいたせんが、少なくずも共有バッファの状態ずはあたり関係がないようです。テスト甚のク゚リは、PostgreSQLのサヌビス起動盎埌、぀たり共有バッファが空の状態から5回連続しお実行しおいたす

以䞋は、lineitemテヌブルのフルスキャンを6457.410ミリ秒で実行した時に、lineitemテヌブルのブロックは、党1057132ブロックのうち8ブロックしか共有バッファに茉っおいなかったこずを瀺しおいたす。呌び出しおいるrpt_buf_cache.sqlはpg_buffercacheモゞュヌルの倀を集蚈するスクリプトです

dbt3=# \timing
Timing is on.
dbt3=# SELECT count(*) FROM lineitem;
count
----------
59986052
(1 row)

Time: 6457.410 ms
dbt3=# \i rpt_buf_cache.sql
name | num_buf | num_blks | pct
----------+---------+----------+------
lineitem | 8 | 1057132 | 0.00
<others> | 46 | |
(2 rows)

Time: 32.223 ms
dbt3=# SHOW block_size ;
block_size
------------
32768
(1 row)

Time: 0.223 ms
dbt3=#
䞊蚘の通り、Day7の゚ントリで玹介したpg_buffercacheモゞュヌルを䜿っお共有バッファの状態を確認しおみた限り、デヌタブロックがほずんど共有バッファに茉っおいなくおも、lineitemテヌブルのフルスキャンを6秒匷で実行できおいたす。

よっお、PostgreSQLではなくファむルシステムレベルに原因があるのかもしれたせん。

■GROUP BYによる集玄


次に、GROUP BYによる集玄凊理のパフォヌマンスを芋おみたす。

実行するク゚リは以䞋の通りで、月ごずの泚文金額総額を集蚈しおいたす。

select date_trunc('month', o_orderdate), sum(o_totalprice)
from orders
group by date_trunc('month', o_orderdate)
order by 1;
実行プランは以䞋の通りです。

QUERY PLAN
----------------------------------------------------------------------------------
Sort (cost=363007.84..363013.85 rows=2406 width=8)
Sort Key: (date_trunc('month'::text, (o_orderdate)::timestamp with time zone))
-> HashAggregate (cost=362836.62..362872.71 rows=2406 width=8)
-> Seq Scan on orders (cost=0.00..287837.71 rows=14999781 width=8)
(4 rows)
実行時間をブロックサむズ別に枬定したものが以䞋のグラフです。


このク゚リの堎合には、ほずんどパフォヌマンスが倉わりたせんでした。

■結合ず集玄


最埌に、もう少し耇雑な結合ず集玄を含むク゚リを実行しおみたす。

以䞋は、泚文時に玄束した期日たでに配達が終わらなかった泚文に぀いお、顧客ごずに集蚈しお倚い順に20件ランキングしおいたす。

select o.o_custkey,count(*)
from orders o, lineitem l
where l.l_commitdate < l.l_receiptdate
and l.l_orderkey = o.o_orderkey
group by o.o_custkey
order by 2 desc limit 20;
以䞋は、䞊蚘のク゚リのブロックサむズ別の実行時間です5回平均。


このク゚リもパフォヌマンスが向䞊しおいたすが、最初のlineitemテヌブルぞのシヌケンシャルスキャンの堎合ず違っお、1回目から5回目たで、ブロックサむズに応じお実行時間が短瞮されおいたす。


䜆し、8kBブロック、16kBブロックの時にはHash Joinが䜿われたのに察しお、32kBブロックの時にはMerge Joinが䜿われる、ずいった違いがありたした。

以䞋は8kBブロックの時の実行プランです。

QUERY PLAN
--------------------------------------------------------------------------------------------------------
Limit (cost=7004441.17..7004441.22 rows=20 width=4)
-> Sort (cost=7004441.17..7006622.04 rows=872351 width=4)
Sort Key: (count(*))
-> GroupAggregate (cost=6822539.33..6981228.22 rows=872351 width=4)
-> Sort (cost=6822539.33..6872527.79 rows=19995384 width=4)
Sort Key: o.o_custkey
-> Hash Join (cost=649382.08..3304284.73 rows=19995384 width=4)
Hash Cond: (l.l_orderkey = o.o_orderkey)
-> Seq Scan on lineitem l (cost=0.00..1815236.90 rows=19995384 width=4)
Filter: (l_commitdate < l_receiptdate)
-> Hash (cost=403281.59..403281.59 rows=15000359 width=8)
-> Seq Scan on orders o (cost=0.00..403281.59 rows=15000359 width=8)
(12 rows)
以䞋は32kBブロックの際の実行プランです。

QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Limit (cost=4970682.69..4970682.74 rows=20 width=4)
-> Sort (cost=4970682.69..4972685.24 rows=801019 width=4)
Sort Key: (count(*))
-> GroupAggregate (cost=4791392.62..4949367.86 rows=801019 width=4)
-> Sort (cost=4791392.62..4841380.97 rows=19995341 width=4)
Sort Key: o.o_custkey
-> Merge Join (cost=44.76..2093263.54 rows=19995341 width=4)
Merge Cond: (o.o_orderkey = l.l_orderkey)
-> Index Scan using pk_orders on orders o (cost=0.00..328663.08 rows=15000450 width=8)
-> Index Scan using i_l_orderkey on lineitem l (cost=0.00..1477217.99 rows=19995341 width=4)
Filter: (l_commitdate < l_receiptdate)
(11 rows)

■たずめ


今回は、PostgreSQLのデヌタブロックの倉曎方法ず、ブロックサむズの倉曎がク゚リのパフォヌマンス、特に集蚈系・分析系のク゚リにどのような圱響を䞎えるのかを、簡単なク゚リを実行しながら芋おきたした。

今回の実隓では、ク゚リのパフォヌマンスが向䞊した理由、あるいは向䞊しなかった理由を螏み蟌んで解明するずころたでには至りたせんでしたが、もう少し動䜜を理解した䞊で、うたくチュヌニングポむントを芋぀ければ、デヌタ分析のデヌタベヌスパフォヌマンスを最適化する方法論が芋぀かるかもしれたせん。

興味がある方は、ぜひ詊しおみおいただければず思いたす。私も継続的に調査しおいきたいず思いたす。

では、たた。
↧

テヌブルパヌティショニングを䜿っお実珟するパフォヌマンス向䞊

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 23です。

今回は、PostgreSQLにおけるテヌブルパヌティショニングの機胜を取り䞊げたす。

BigDataやデヌタ分析ずいったキヌワヌドが倚く聞かれるようになっおきたしたが、PostgreSQLでも倧きなデヌタに察する集蚈凊理のパフォヌマンスを向䞊させるための機胜が提䟛されおいたす。

■「テヌブルパヌティショニング」


RDBMSにおける「テヌブルパヌティショニング」ず呌ばれる機胜には、ディスクぞのアクセスを「局所化させる」、たたは「分散させる」ために提䟛されおいる機胜です。これらの機胜を䜿うこずによっお、倧芏暡なデヌタを察象ずした集蚈系の凊理のパフォヌマンスを向䞊させるこずができたす。

RDBMSにおけるパヌティションの皮類には倧きく分けお3぀の皮類がありたす。正確には、2぀ずそのコンビネヌションですが

・レンゞパヌティショニング

レンゞパヌティショニングずいうのは、連続的な倀ず連続的な意味を持぀カラム日付などをパヌティションキヌ分割のキヌずしお指定するこずで、テヌブルスキャンなどのアクセスを「局所化」させるこずを目的ずしたパヌティショニングです。「垂盎方向のパヌティショニング」ず呌ばれるこずもありたす。

・ハッシュパヌティショニング

ハッシュパヌティショニングは、パヌティションキヌにするカラム倚くは䞻キヌのハッシュ倀によっお分割する方法です。こちらは、アクセスを「分散」させるこずを目的ずしたパヌティショニングです。「氎平方向のパヌティショニング」ず呌ばれるこずもありたす。

・コンポゞットパヌティショニング

レンゞパヌティションずハッシュパヌティショニングの組合せです。デヌタぞのアクセスを分散させ぀぀、局所化するこずができたす。


なお、PostgreSQL単䜓で利甚できるのは、䞊蚘のうち「レンゞパヌティショニング」になりたす。

■PostgreSQLのテヌブルパヌティショニングConstraint Exclusion


PostgreSQLにおけるテヌブルパヌティショニングは、「Constraint Exclusion」ずいう機胜を䜿っお実珟されたす。

PostgreSQLは、「オブゞェクトリレヌショナルデヌタベヌス」ず呌ばれる通り、PostgreSQL内郚で䜿われるオブゞェクトの高い拡匵性ず柔軟性を提䟛しおいたすが、その「オブゞェクトリレヌショナルデヌタベヌス」ずしおのPostgreSQLの特城のひず぀に「テヌブルの継承」ずいう機胜がありたす。

PostgreSQL 9.0.4文曞継承
http://www.postgresql.jp/document/9.0/html/ddl-inherit.html

これは、䌌たようなテヌブルを定矩する際、芪テヌブルの定矩をベヌスにしお、新たなカラムを远加するなどしお「子テヌブル掟生テヌブル」を䜜成できるずいうもので、オブゞェクト指向プログラミングにおける「クラスの継承」の抂念に䌌たものです。


この「掟生テヌブル」の機胜を䜿っお、掟生テヌブルを耇数䜜成、その䞭にデヌタを分割しお配眮し、ク゚リを実行する際には必芁な掟生テヌブルこれがパヌティションずなるだけを察象にク゚リを実行する、ずいうのがPostgreSQLにおけるテヌブルパヌティショニングです。

この時、察象ずなる掟生テヌブルだけをク゚リの凊理察象ずする必芁がありたすが、これが「Constraint Exclusion」、日本語で「制玄による排他」ず呌ばれる機胜です。

制玄通垞はCHECK制玄を定矩するこずによっお、「どのパヌティションにどのようなデヌタどのようなCHECK制玄を満たすデヌタが入っおいるか」をオプティマむザが刀断するこずが可胜になるのです。

■パヌティションの䜜成


それでは、実際にテヌブルのパヌティション化を行っおみたしょう。

ここでは、以䞋のような「泚文テヌブル」を察象ずしお、泚文日o_orderdateをパヌティションキヌずしおパヌティション化しおみたす。

CREATE TABLE orders (
o_orderkey INTEGER PRIMARY KEY,
o_custkey INTEGER,
o_orderstatus CHAR(1),
o_totalprice REAL,
o_orderdate DATE,
o_orderpriority CHAR(15),
o_clerk CHAR(15),
o_shippriority INTEGER,
o_comment VARCHAR(79)
);
たず、通垞のテヌブル orders を䜜成し、むンデックスも䜜成したす。

testdb=# CREATE TABLE orders (
testdb(# o_orderkey INTEGER PRIMARY KEY,
testdb(# o_custkey INTEGER,
testdb(# o_orderstatus CHAR(1),
testdb(# o_totalprice REAL,
testdb(# o_orderDATE DATE,
testdb(# o_orderpriority CHAR(15),
testdb(# o_clerk CHAR(15),
testdb(# o_shippriority INTEGER,
testdb(# o_comment VARCHAR(79)
testdb(# );
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "orders_pkey" for table "orders"
CREATE TABLE
testdb=# CREATE INDEX orders_o_orderdate_idx ON orders(o_orderdate);
CREATE INDEX
testdb=# \d orders
Table "public.orders"
Column | Type | Modifiers
-----------------+-----------------------+-----------
o_orderkey | integer | not null
o_custkey | integer |
o_orderstatus | character(1) |
o_totalprice | real |
o_orderdate | date |
o_orderpriority | character(15) |
o_clerk | character(15) |
o_shippriority | integer |
o_comment | character varying(79) |
Indexes:
"orders_pkey" PRIMARY KEY, btree (o_orderkey)
"orders_o_orderdate_idx" btree (o_orderdate)

testdb=#
次に、このordersテヌブルを芪テヌブルずしお、これを継承しお子テヌブルを䜜成したす。

ここでは、泚文日o_orderdateをパヌティションキヌずしお分割したすので、1992幎の泚文情報を保持するパヌティション orders_1992 を䜜成しおみたす。

掟生しおパヌティションを䜜成するには、CREATE TABLE文でINHERITSオプションを䜿いたす。

testdb=# CREATE TABLE orders_1992 () INHERITS (orders);
CREATE TABLE
testdb=# \d
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | orders | table | postgres
public | orders_1992 | table | postgres
(2 rows)

testdb=# \d orders_1992
Table "public.orders_1992"
Column | Type | Modifiers
-----------------+-----------------------+-----------
o_orderkey | integer | not null
o_custkey | integer |
o_orderstatus | character(1) |
o_totalprice | real |
o_orderdate | date |
o_orderpriority | character(15) |
o_clerk | character(15) |
o_shippriority | integer |
o_comment | character varying(79) |
Inherits: orders

testdb=#
子テヌブルorders_1992が䜜成され、「Inherits: orders」ずあるようにordersテヌブルからの掟生テヌブルであるこずも確認できたしたが、テヌブルを掟生しお䜜成しただけでは、䞻キヌ制玄やむンデックスなどは䜜成されたせんので、必芁に応じおこれらを远加で䜜成したす。

testdb=# ALTER TABLE orders_1992 ADD PRIMARY KEY (o_orderkey);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "orders_1992_pkey" for table "orders_1992"
ALTER TABLE
testdb=#
掟生テヌブルを䜜成し、䞻キヌ制玄や必芁なむンデックスを䜜成したら、最埌にCHECK制玄を䜜成したす。

testdb=# ALTER TABLE orders_1992 ADD CHECK(o_orderdate >= '1992-01-01' AND o_orderdate < '1993-01-01');
ALTER TABLE
testdb=# \d orders_1992
Table "public.orders_1992"
Column | Type | Modifiers
-----------------+-----------------------+-----------
o_orderkey | integer | not null
o_custkey | integer |
o_orderstatus | character(1) |
o_totalprice | real |
o_orderdate | date |
o_orderpriority | character(15) |
o_clerk | character(15) |
o_shippriority | integer |
o_comment | character varying(79) |
Indexes:
"orders_1992_pkey" PRIMARY KEY, btree (o_orderkey)
Check constraints:
"orders_1992_o_orderdate_check" CHECK (o_orderdate >= '1992-01-01'::date AND o_orderdate < '1993-01-01'::date)
Inherits: orders2

testdb=#
このCHECK制玄を䜜成するこずで、このorder_1992テヌブルパヌティションには、泚文日が1992幎のデヌタのみが含たれおいるずいうこずをPostgreSQLのオプティマむザが知るこずができるようになりたす。

䞊蚘の凊理を他の幎に぀いおも繰り返し、必芁なパヌティションすべおを䜜成したのが以䞋の状態です。

testdb=# \d
List of relations
Schema | Name | Type | Owner
--------+-------------+-------+----------
public | orders | table | postgres
public | orders_1992 | table | postgres
public | orders_1993 | table | postgres
public | orders_1994 | table | postgres
public | orders_1995 | table | postgres
public | orders_1996 | table | postgres
public | orders_1997 | table | postgres
public | orders_1998 | table | postgres
(8 rows)

testdb=#

■パヌティションぞのデヌタのロヌディング


パヌティションぞのデヌタのロヌディング方法は、倧きく2皮類ありたす。
  • 芪テヌブルにINSERTトリガを蚭定し、芪テヌブルぞのINSERTを適切なパヌティション子テヌブルに自動的に振り分ける方法
  • パヌティションを盎接指定しおデヌタをロヌディングする方法
です。

UPDATEやDELETEに぀いおも同じこずが蚀えたすので、状況に応じお遞択するず良いでしょう。ある皋床事前にデヌタを分割できるのであれば、パヌティションに盎接ロヌドしおも良いかもしれたせん。簡単ですし

■パヌティションテヌブルに察する実行プラン


それでは、実際に通垞のテヌブルずパヌティションテヌブルで、凊理やパフォヌマンスがどのように異なっおくるのかを芋おみたす。

たずは、条件無しでテヌブルフルスキャンを実斜し、すべおの泚文金額を合蚈する凊理の実行プランを芋おみたす。ク゚リずしおは以䞋のようになりたす。

SELECT sum(o_totalprice) FROM orders;
以䞋は通垞のテヌブルに察するク゚リの実行プランです。

testdb=# EXPLAIN
SELECT sum(o_totalprice) FROM orders;
QUERY PLAN
----------------------------------------------------------------------
Aggregate (cost=44076.00..44076.01 rows=1 width=4)
-> Seq Scan on orders (cost=0.00..40326.00 rows=1500000 width=4)
(2 rows)

testdb=#
次にパヌティションテヌブルに察しお同じク゚リを実行しおみたす。

testdb=# EXPLAIN SELECT sum(o_totalprice) FROM orders2;
QUERY PLAN

--------------------------------------------------------------------------------
-------
Aggregate (cost=44079.01..44079.02 rows=1 width=4)
-> Append (cost=0.00..40329.00 rows=1500001 width=4)
-> Seq Scan on orders2 (cost=0.00..0.00 rows=1 width=4)
-> Seq Scan on orders_1992 orders2 (cost=0.00..6104.89 rows=227089 width=4)
-> Seq Scan on orders_1993 orders2 (cost=0.00..6093.45 rows=226645 width=4)
-> Seq Scan on orders_1994 orders2 (cost=0.00..6117.97 rows=227597 width=4)
-> Seq Scan on orders_1995 orders2 (cost=0.00..6146.37 rows=228637 width=4)
-> Seq Scan on orders_1996 orders2 (cost=0.00..6148.26 rows=228626 width=4)
-> Seq Scan on orders_1997 orders2 (cost=0.00..6124.83 rows=227783 width=4)
-> Seq Scan on orders_1998 orders2 (cost=0.00..3593.23 rows=133623 width=4)
(10 rows)

testdb=#
パヌティションテヌブルに察するスキャンでは、orders_1992からorders_1998たで、すべおのパヌティションをスキャンしおいるため、結果ずしお通垞のテヌブルをスキャンするコスト44076.01ずほずんど同じ実行コスト44079.02になっおいるこずが分かりたす。

これは、パヌティション化されたテヌブルであっおも、䞀郚のパヌティションだけではなくすべおのパヌティションをスキャンすれば、総実行コストは同じになる、ずいうこずを意味しおいたす。

■テヌブルパヌティショニングによるパフォヌマンス向䞊


では、次に条件を远加しお集蚈凊理をしおみたす。䟋えば、1993幎9月の泚文金額だけを合蚈しおみたす。ク゚リずしおは以䞋のようになりたす。

SELECT sum(o_totalprice) FROM orders
WHERE o_orderdate >= '1993-09-01'
AND o_orderdate <= '1993-09-30';
䞊蚘のク゚リを通垞のテヌブルに察しおEXPLAINしたものが以䞋です。

testdb=# EXPLAIN SELECT sum(o_totalprice) FROM orders
WHERE o_orderdate >= '1993-09-01'
AND o_orderdate <= '1993-09-30';
QUERY PLAN
-----------------------------------------------------------------------------------------------
Aggregate (cost=33690.55..33690.56 rows=1 width=4)
-> Seq Scan on orders (cost=0.00..33683.58 rows=2786 width=4)
Filter: ((o_orderdate >= '1993-09-01'::date) AND (o_orderdate <= '1993-09-30'::date))
(3 rows)

testdb=#
実行プランずしおは、ordersテヌブルに察するフルスキャンであり、実行コストは「33690.56」ず芋積もられおいたす。

䞀方で、パヌティションテヌブルに察しおEXPLAINした結果が以䞋です。

testdb=# EXPLAIN SELECT sum(o_totalprice) FROM orders
WHERE o_orderdate >= '1993-09-01'
AND o_orderdate <= '1993-09-30';
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Aggregate (cost=7270.49..7270.50 rows=1 width=4)
-> Append (cost=0.00..7226.67 rows=17525 width=4)
-> Seq Scan on orders (cost=0.00..0.00 rows=1 width=4)
Filter: ((o_orderdate >= '1993-09-01'::date) AND (o_orderdate <= '1993-09-30'::date))
-> Seq Scan on orders_1993 orders (cost=0.00..7226.67 rows=17524 width=4)
Filter: ((o_orderdate >= '1993-09-01'::date) AND (o_orderdate <= '1993-09-30'::date))
(6 rows)

testdb=#
実行プランずしおは、orders_1993パヌティションに察するスキャンであり、実行コストは「7270.50」ず芋積もられおおり、通垞のテヌブルに比べお、実行コストが1/5皋床に䜎枛しおいるこずが分かりたす。

これは、スキャンをする察象がテヌブル党䜓党パヌティションではなく、䞀郚のパヌティションに局所化するこずができたため、実行コストが小さくなっおいるこずを意味しおいたす。

このク゚リを、通垞のテヌブルずパヌティションテヌブルそれぞれに実行した結果が以䞋になりたす。

以䞋は通垞のテヌブルに察する実行結果です。

testdb=# SELECT sum(o_totalprice) FROM orders
WHERE o_orderdate >= '1993-09-01'
AND o_orderdate <= '1993-09-30';
sum
------------
2.8482e+09
(1 row)

Time: 405.432 ms
testdb=#
以䞋はパヌティションテヌブルに察する実行結果です。

testdb=# SELECT sum(o_totalprice) FROM orders
WHERE o_orderdate >= '1993-09-01'
AND o_orderdate <= '1993-09-30';
sum
------------
2.8482e+09
(1 row)

Time: 72.615 ms
testdb=#
通垞のテヌブルに察しお実行するず405ミリ秒、パヌティションテヌブルに察しお実行するず72ミリ秒ずなっおおり、実際の実行時間の芳点からも集玄凊理のパフォヌマンスを倧幅に向䞊できたこずが分かりたす。

■たずめ


今回は、PostgreSQLで利甚できるテヌブルパヌティショニングの機胜に぀いお簡単に玹介したした。

ただ少し手間がかかるのが難点ではありたすが、PostgreSQLでもテヌブルパヌティショニングの機胜を䜿うこずはできたすし、BigDataやデヌタ分析が倧きなトレンドずなり぀぀ある今、こういった機胜をうたく䜿いこなすこずも倧事になっおくるのではないかず思いたす。

ぜひ、機䌚を芋぀けお詊しおみおいただければず思いたす。

では、たた。
↧

テヌブルパヌティショニングツヌル「pg_part」を䜿っおみる

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 24です。

前回の゚ントリでは、PostgreSQLのテヌブルパヌティショニングの基本的なしくみずその䜿い方を解説しおきたした。

前回解説した通り、PostgreSQLのパヌティショニングの機胜は「理屈ずしおは」確かに動くのですが、実際にはそのためにいろいろなコマンドを実行したりしなければならず、なかなか手間がかかるのも事実です。

■テヌブルパヌティションを操䜜するpg_partパッケヌゞ


テヌブルパヌティショニングは、特に分析系の凊理をしおいる時には䟿利なのですが、PostgreSQLの堎合、䜜成や管理に結構手間がかかるため、なかなか手を出せない、ずいう方もいるのではないでしょうか。私も以前はそうでした

そのため、パヌティション操䜜のための凊理を䞀括しお実斜しおくれる関数矀を提䟛するpg_partパッケヌゞを䜜成したした。

uptimejp/pg_part
https://github.com/uptimejp/pg_part

今回は、このpg_partパッケヌゞに含たれるSQL関数の䜿い方を玹介したす。

pg_partを䜿うず、
  • パヌティションの䜜成デヌタの移行
  • パヌティションの解陀デヌタの移行
  • パヌティションの远加アタッチ
  • パヌティションの切り離しデタッチ
が簡単に行えるようになりたす。

なお、pg_partパッケヌゞはSQL関数の定矩の集たりSQLスクリプトですので、psqlコマンドを䜿っおデヌタベヌスに登録しお䜿甚したす。

[snaga@devsv03 ~]$ psql -f pg_part.sql dbt3
BEGIN
CREATE FUNCTION
CREATE FUNCTION
(...snip...)
CREATE FUNCTION
CREATE FUNCTION
COMMIT
[snaga@devsv03 ~]$

■パヌティションの䜜成


パヌティションの䜜成には、pgpart.add_partition()関数を䜿いたす。

SELECT pgpart.add_partition(
schema_name,
table_name,
partition_name,
check_condition,
temp_file);
schema_nameはテヌブルの存圚するスキヌマ名です。通垞は 'public' などでしょう。

table_nameは、パヌティションを䜜成する元ずなる芪テヌブルのテヌブル名です。

partition_nameは、䜜成するパヌティションの名前です。

check_conditionは、䜜成するパヌティションが保持しおいるレコヌドの範囲を衚すCHECK制玄の条件です。特定のカラムに぀いお範囲指定などの圢で蚘述したす。

temp_fileは、芪テヌブルからパヌティションにデヌタを移行させる時に利甚する䞀時ファむルのファむル名です。

これらのパラメヌタを指定しお実行するず、成功するずtrue、倱敗するずfalseを返したす。

dbt3=# SELECT pgpart.add_partition(
dbt3(# 'public',
dbt3(# 'orders',
dbt3(# 'orders_1992',
dbt3(# ' ''1992-01-01'' <= o_orderdate AND o_orderdate < ''1993-01-01'' ',
dbt3(# '/tmp/orders.tmp');
add_partition
---------------
t
(1 row)

dbt3=#
pgpart.add_partition()関数は、
  • 芪テヌブルを継承しお子テヌブルパヌティションを䜜成
  • 芪テヌブルからパヌティションに移動させるレコヌドを゚クスポヌト
  • 芪テヌブルからパヌティションに移動させるレコヌドを削陀
  • ゚クスポヌトしおおいたレコヌドをパヌティションにむンポヌト
  • パヌティションに芪テヌブルず同等の䞻キヌ制玄を远加
  • パヌティションに芪テヌブルず同等のむンデックスを远加
ずいう凊理を行いたす。

なお、pgpart.add_partition()関数を実行するず、以䞋のようにいく぀かNOTICEメッセヌゞが出力されたす。これは、パヌティションを䜜成するために内郚的に実行しおいるDDL文です。

psql:add_part.sql:8: NOTICE: add_partition: CREATE TABLE public.orders_1992( CONSTRAINT __orders_1992_check CHECK( '1992-01-01' <= o_orderdate AND o_orderdate < '1993-01-01' )) INHERITS (public.orders);
psql:add_part.sql:8: NOTICE: add_partition: COPY ( SELECT * FROM public.orders WHERE '1992-01-01' <= o_orderdate AND o_orderdate < '1993-01-01' ) to '/tmp/orders.tmp';
psql:add_part.sql:8: NOTICE: add_partition: DELETE FROM public.orders WHERE '1992-01-01' <= o_orderdate AND o_orderdate < '1993-01-01' ;
psql:add_part.sql:8: NOTICE: add_partition: COPY public.orders_1992 FROM '/tmp/orders.tmp';
psql:add_part.sql:8: NOTICE: add_partition: ALTER TABLE public.orders_1992 ADD PRIMARY KEY (o_orderkey);
psql:add_part.sql:8: NOTICE: add_partition: CREATE INDEX orders_1992_o_orderdate_idx ON public.orders_1992 USING btree (o_orderdate);
psql:add_part.sql:8: NOTICE: add_partition: CREATE INDEX orders_1992_o_custkey_idx ON public.orders_1992 USING btree (o_custkey);
pgpart.add_partition()関数だけではなく、pg_partパッケヌゞの提䟛する関数矀ではこのように内郚的に呌び出されるDDL文をNOTICEメッセヌゞずしお出力したすが、すべお蚘茉するず冗長になるためこの゚ントリでは省いおいたす。

■パヌティション䞀芧の取埗


指定したテヌブルが、どのようなパヌティションから構成されおいるかを取埗するための関数が pgpart.show_partition()関数です。

SELECT pgpart.show_partition(schema_name, table_name);
schema_nameはテヌブルの存圚するスキヌマ名で、table_nameは芪テヌブルのテヌブル名です。

䟋えば、ordersテヌブルをパヌティション化しおいる堎合、以䞋のように実行するこずで、ordersテヌブルを構成するパヌティションの䞀芧を取埗するこずができたす。

dbt3=# SELECT pgpart.show_partition('public', 'orders');
show_partition
----------------
orders_1992
orders_1993
orders_1994
orders_1995
orders_1996
orders_1997
(6 rows)

dbt3=#

■パヌティションのマヌゞ解陀


䜜成したパヌティションを、もずのテヌブルに戻すには pgpart.merge_partition()関数を䜿いたす。

SELECT pgpart.merge_partition(
schema_name,
table_name,
partition_name,
check_constraint,
temp_file);
匕数は、pgpart.add_partition()関数ずほずんど同じです。

schema_nameはテヌブルの存圚するスキヌマ名です。

table_nameはパヌティションのデヌタを戻す先ずなる芪テヌブルのテヌブル名です。

partition_nameは芪テヌブルにマヌゞするパヌティションの名前です。

check_conditionは、䜜成するパヌティションが保持しおいるレコヌドの範囲を衚すCHECK制玄の条件です。特定のカラムに぀いお範囲指定などの圢で蚘述したす䜆し、珟時点では未䜿甚です。

temp_fileは、パヌティションから芪テヌブルにデヌタを移行させる時に利甚する䞀時ファむルのファむル名です。

dbt3=# SELECT pgpart.merge_partition('public', 'orders', 'orders_1992', null, '/tmp/orders.tmp');
merge_partition
-----------------
t
(1 row)

dbt3=#

■パヌティションのアタッチ远加


芪テヌブルずたったく同じスキヌマ構造を持぀テヌブルを䜜成するず、そのテヌブルをパヌティションずしお远加するこずができたす。

SELECT pgpart.attach_partition (
schema_name,
table_name,
partition_name,
check_condition);
schema_nameはテヌブルの存圚するスキヌマ名です。

table_nameはアタッチするパヌティションの芪テヌブルのテヌブル名です。

partition_nameはアタッチするパヌティションの名前です。

check_conditionは、アタッチするパヌティションが保持しおいるレコヌドの範囲を衚すCHECK制玄の条件です。

以䞋は、パヌティション化されおいるordersテヌブルにorders_1998パヌティションを远加しおいる䟋です。

dbt3=# SELECT pgpart.show_partition('public', 'orders');
show_partition
----------------
orders_1992
orders_1993
orders_1994
orders_1995
orders_1996
orders_1997
(6 rows)

dbt3=# SELECT pgpart.attach_partition('public', 'orders', 'orders_1998', ' ''1998-01-01'' <= o_orderdate AND o_orderdate < ''1999-01-01'' ');
attach_partition
------------------
t
(1 row)

dbt3=# SELECT pgpart.show_partition('public', 'orders');
show_partition
----------------
orders_1992
orders_1993
orders_1994
orders_1995
orders_1996
orders_1997
orders_1998
(7 rows)

dbt3=#
パヌティションをアタッチする前には1997幎のパヌティションたでしか無かったものの、pgpart.attach_partition()関数で1998幎のパヌティションを远加した結果、以䞋のEXPLAINを芋るず、SELECTク゚リでordersテヌブルに怜玢を行う際に1998幎のパヌティションも怜玢察象になっおいるこずが分かりたす。

dbt3=# EXPLAIN SELECT count(*) FROM orders WHERE o_orderdate = '1998-01-01';
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Aggregate (cost=6044.09..6044.10 rows=1 width=0)
-> Append (cost=0.00..6027.42 rows=6667 width=0)
-> Seq Scan on orders (cost=0.00..0.00 rows=1 width=0)
Filter: (o_orderdate = '1998-01-01'::date)
-> Bitmap Heap Scan on orders_1998 orders (cost=72.27..6027.42 rows=6666 width=0)
Recheck Cond: (o_orderdate = '1998-01-01'::date)
-> Bitmap Index Scan on orders_1998_o_orderdate_idx (cost=0.00..70.61 rows=6666 width=0)
Index Cond: (o_orderdate = '1998-01-01'::date)
(8 rows)

dbt3=#

■パヌティションのデタッチ削陀


逆に、特定のパヌティションをテヌブルから切り離すデタッチするこずも可胜です。

SELECT pgpart.detach_partition (
schema_name,
table_name,
partition_name);
schema_nameはテヌブルの存圚するスキヌマ名です。

table_nameはデタッチするパヌティションの芪テヌブルのテヌブル名です。

partition_nameはデタッチするパヌティションの名前です。

以䞋は、ordersテヌブルを構成するパヌティションのひず぀であるorders_1992パヌティションをデタッチしおいる䟋です。

dbt3=# SELECT pgpart.show_partition('public', 'orders');
show_partition
----------------
orders_1992
orders_1993
orders_1994
orders_1995
orders_1996
orders_1997
orders_1998
(7 rows)

dbt3=# SELECT pgpart.detach_partition('public', 'orders', 'orders_1992');
detach_partition
------------------
t
(1 row)

dbt3=# SELECT pgpart.show_partition('public', 'orders');
show_partition
----------------
orders_1993
orders_1994
orders_1995
orders_1996
orders_1997
orders_1998
(6 rows)

dbt3=#

■パヌティション䜜成時の手順


テヌブルをパヌティション化するためには、pgpart.add_partition()関数を䜿っお必芁な党パヌティションを䜜成しおいきたす。

党パヌティションの䜜成が終わっお、芪テヌブルに残っおいるデヌタが無くなったら芪テヌブルが論理的に空になったら、芪テヌブルに察しおVACUUMないしCLUSTERを実行しおください。

パヌティション䜜成の際には、パヌティションに移動したレコヌドを芪テヌブルからDELETEしおいたすが、芪テヌブルにはただ削陀枈みレコヌドが残っおいるため、明瀺的に芪テヌブルを切り詰める䜜り盎す必芁がありたす。

以䞋はordersテヌブルをパヌティション化しおいる䟋ですが、VACUUMによっおordersテヌブルのサむズがれロになっおいるこずを確認しおください。

dbt3=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------------+-------+-------+---------+-------------
public | orders | table | snaga | 1964 MB |
public | orders_1992 | table | snaga | 299 MB |
public | orders_1993 | table | snaga | 298 MB |
(...snip...)

dbt3=# VACUUM orders;
VACUUM
dbt3=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------------+-------+-------+---------+-------------
public | orders | table | snaga | 0 bytes |
public | orders_1992 | table | snaga | 299 MB |
public | orders_1993 | table | snaga | 298 MB |
(...snip...)

dbt3=#

■たずめ


今回は、PostgreSQLのテヌブルパヌティションの機胜をより簡単に䜿うためのナヌティリティを玹介したした。

ただ機胜ずしおは必ずしも完璧ではないですが、PostgreSQLのパヌティショニングを気軜に詊しおいただくための機胜ずしおは䞀通り揃えおみた぀もりです。

䞖間の䞀郚的にはRDBMSは叀いテクノロゞヌだず思われおいるようですが、デヌタ分析のプラットフォヌムずしおは、ただただ可胜性を持っおいるず思いたす。むしろ、今たでのスキルや業務デヌタずの敎合性を考えるず、PostgreSQLはもっず掻甚できるはず、ず蚀えるでしょう。

これからは、PostgreSQLをデヌタ分析のプラットフォヌムずしお今たで以䞊に掻甚するノりハりを探っおいきたいず思いたす。

では、たた。
↧
↧

PostgreSQL甹MPPミドルりェア「Stado」の導入

$
0
0
PostgreSQL Advent Calendar 2012党郚俺のDay 25です。

Advent Calendar最終日の今回は、少し倧物ずしおPostgreSQLのMPPミドルりェア「Stado」の導入方法を玹介したす。

Stado: The Open Source MPP Solution
https://launchpad.net/stado

■「MPP」ずは䜕か


皆さんは「MPP」ずいう蚀葉を聞いたこずがあるでしょうか。

コンピュヌタの䞖界で「MPP」ず蚀えば「Massive Parallel Processing」、日本語で蚀うずころの「超䞊列凊理」のこずを指したす。

Massively parallel (computing) - Wikipedia, the free encyclopedia
http://en.wikipedia.org/wiki/Massive_parallel_processing

デヌタベヌスの䞖界で「MPP」ず蚀うず、通垞は「シェアヌドナッシング・アヌキテクチャ」のスケヌラブルな䞊列凊理甚のコンピュヌタアヌキテクチャのこずを指したす。ベンダ補品で蚀うず、Teradata、Netezza、Greenplumなどが有名どころでしょう。

MPPは昔からデヌタりェアハりスDWHず呌ばれる領域で採甚されおきたこずからも分かる通り、特に倧芏暡なデヌタの分析や集蚈凊理に嚁力を発揮したす。

実は、筆者は新入瀟員の頃、PostgreSQLを䜿ったオヌプン゜ヌスのMPPの研究開発プロゞェクトに参加しおいたこずもあり、MPPテクノロゞヌにはちょっず思い入れがあったりしたす。ちなみに、孊生の頃には今で蚀うHadoopのような分散凊理の研究をしおいたした。

■PostgreSQL甹MPPミドルりェア「Stado」


そのMPPのアヌキテクチャをPostgreSQLず組み合わせお実珟するためのオヌプン゜ヌスのミドルりェアが「Stado」です。

Stado: The Open Source MPP Solution
https://launchpad.net/stado

Stadoは、耇数のPostgreSQLサヌバを束ねお、あたかも単䞀のデヌタベヌスサヌバであるかのように芋せるこずができるミドルりェアです。


Stadoは、前々回の゚ントリで玹介したパヌティショニングのうち、「ハッシュパヌティショニング」の機胜を提䟛したす。Stadoを䜿うこずによっお、倧容量のデヌタに察しお䞊列凊理をするこずができ、単䜓のPostgreSQLず比べお実行時間を倧幅に短瞮するこずができたす。

Stadoの玹介をしおいるずこれもたた長くなりたすので、Stadoの抂芁に぀いおは䞊蚘の資料をご参照ください。

今回は、このStadoを実際に導入する手順を説明したす。

ずは蚀っおも、誰もが耇数台のサヌバを甚意できるわけでもないでしょうから、今回は1台のサヌバにマルチコアCPUず耇数のハヌドディスクを積んで、䞀台のサヌバで䞊列凊理をする環境を構築する方法を解説したす。

以䞋のように、ディスクを4本積んでいるサヌバで䞊列凊理をさせるこずを想定しおStadoの導入を行っおみたす。

■導入手順


Stadoの倧たかな導入手順は以䞋の通りです。
  • Stadoのむンストヌル
  • PostgreSQLのセットアップ
  • Stadoのセットアップ
  • ナヌザデヌタベヌスの䜜成
  • テヌブルスペヌスぞのパヌティションの移動
  • テヌブルの䜜成ずデヌタのロヌド
順を远っお説明しおいきたす。

■導入環境


今回Stadoを導入した環境は以䞋の通りです。
  • NEC Express5800 GT110b
  • Xeon Intel Xeon X3440 2.53GHz (1P4C)
  • Unbeffered ECC 16GB
  • Hitachi Deskstar 7K1000 HDS72101
  • Red Hat Enterprise Linux 6.3 (x86_64)
なお、Stadoを動䜜させるにはPostgreSQLおよびJDKを必芁ずしたす。

今回、PostgreSQLは9.2を、JDKはOpenJDKの1.6.0を䜿っおいたす。

[snaga@devsv03 ~]$ rpm -qa | grep postgresql
postgresql92-9.2.2-1PGDG.rhel6.x86_64
postgresql92-devel-9.2.2-1PGDG.rhel6.x86_64
postgresql92-server-9.2.2-1PGDG.rhel6.x86_64
postgresql92-contrib-9.2.2-1PGDG.rhel6.x86_64
postgresql92-libs-9.2.2-1PGDG.rhel6.x86_64
[snaga@devsv03 ~]$ rpm -qa | grep openjdk
java-1.6.0-openjdk-devel-1.6.0.0-1.45.1.11.1.el6.x86_64
java-1.6.0-openjdk-1.6.0.0-1.45.1.11.1.el6.x86_64
[snaga@devsv03 ~]$
PostgreSQLずOpenJDKのむンストヌルは完了しおいるものずしお、ここからはStadoの導入を進めおいきたす。

■Stadoのむンストヌル


たずは、Stadoの゜ヌスコヌドを取埗したす。

通垞は、分散゜ヌスコヌド管理システムであるBazaarを䜿っお以䞋から取埗したす。

https://code.launchpad.net/~sgdg/stado/stado

[snaga@devvm03 tmp]$ bzr branch lp:~sgdg/stado/stado
You have not informed bzr of your Launchpad ID, and you must do this to
write to Launchpad or access private data. See "bzr help launchpad-login".
Branched 55 revision(s).
[snaga@devvm03 tmp]$
ずは蚀え、Bazzarをむンストヌルしおいるナヌザが倚いずも思えないので、以䞋に゜ヌスコヌドずコンパむル枈のjarファむルのパッケヌゞを甚意したした。

http://www.uptime.jp/go/stado

䞊蚘のURLから stado-20121223.tar.gz ず install.sh スクリプトを取埗したす。

install.shスクリプトは、root暩限で実行するず、OSアカりントのstadoナヌザずstadoグルヌプを䜜成し、必芁なファむルを/usr/local/stadoにむンストヌルしたす。

[snaga@devsv03 stado]$ wget http://www.uptime.jp/downloads/stado/stado-20121223.tar.gz
(...snip...)

[snaga@devsv03 stado]$ wget http://www.uptime.jp/downloads/stado/install_stado.sh
(...snip...)

[snaga@devsv03 stado]$ ls
install_stado.sh stado-20121223.tar.gz
[snaga@devsv03 stado]$ tar zxf stado-20121223.tar.gz
[snaga@devsv03 stado]$ ls
install_stado.sh stado-20121223/ stado-20121223.tar.gz
[snaga@devsv03 stado]$ cd stado-20121223
[snaga@devsv03 stado-20121223]$ ls -F
bin/ build.xml docs/ lib/ README.TXT stado.config
build/ dist/ jars/ misc/ src/
[snaga@devsv03 stado-20121223]$ su
Password:
[root@devsv03 stado-20121223]# sh ../install_stado.sh
[root@devsv03 stado-20121223]# ls -l /usr/local/stado/
total 16
drwxr-xr-x. 2 stado stado 4096 Dec 23 18:06 bin/
drwxr-xr-x. 2 stado stado 4096 Dec 23 18:06 config/
drwxr-xr-x. 2 stado stado 4096 Dec 23 18:06 lib/
drwxrwxr-x. 2 stado stado 4096 Dec 23 18:06 log/
[root@devsv03 stado-20121223]#

■PostgreSQLのセットアップ


Stadoのむンストヌルが終わったら、たずはPostgreSQLの蚭定を行いたす。

・基本蚭定

postgresql.confの蚭定を倉曎した項目は以䞋の通りです。

listen_addresses = '*'
max_connections = 100
shared_buffers = 2GB
work_mem = 2GB
maintenance_work_mem = 2GB
wal_buffers = 32MB
checkpoint_segments = 128
checkpoint_timeout = 60min
log_filename = 'postgresql-%Y%m%d.log'
log_min_duration_statement = 0
log_checkpoints = on
log_connections = on
log_disconnections = on
log_line_prefix = '[%t] %p: '
log_temp_files = 0
autovacuum = off
PostgreSQLの蚭定を行ったら、PostgreSQLサヌバを起動したす。

・テヌブルスペヌスの䜜成

PostgreSQLサヌバを起動したら、デフォルトのデヌタベヌスクラスタ /var/lib/pgsql/9.2/data 以倖にテヌブルスペヌスを䜜成したす。

これらのテヌブルスペヌスは、デヌタベヌスクラスタずは別のディスク䞊に配眮し、それによっおI/O凊理を分散させる目的で䜿甚したす。

ここでは、/disk/disk2, /disk/disk3, /disk/disk4 に、合蚈3本のディスクをマりントしおいるものずしお、それらのディスクにそれぞれ tblspc2, tblspc3, tblspc4 ずいうテヌブルスペヌスを䜜成する蚭定を行いたす。

たず、テヌブルスペヌスずしお䜿甚するディレクトリを䜜成し、postgresナヌザ/postgresグルヌプを所有者ずしお暩限を蚭定したす。

[root@devsv03 pgsql]# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sda3 936146792 739057756 148768328 84% /
/dev/sda1 101086 12010 83857 13% /boot
tmpfs 8213720 0 8213720 0% /dev/shm
/dev/sdb1 961432072 135545524 777048548 15% /disk/disk2
/dev/sdc1 961432072 53769008 858825064 6% /disk/disk3
/dev/sdd1 961432072 134776980 777817092 15% /disk/disk4
[root@devsv03 pgsql]#
[root@devsv03 pgsql]# mkdir /disk/disk2/pgsql
[root@devsv03 pgsql]# mkdir /disk/disk3/pgsql
[root@devsv03 pgsql]# mkdir /disk/disk4/pgsql
[root@devsv03 pgsql]# chown postgres:postgres /disk/disk4/pgsql /disk/disk3/pgsql /disk/disk2/pgsql
次に、䜜成したディレクトリをテヌブルスペヌスずしおPostgreSQL䞊で登録したす。

postgres=# select * from pg_tablespace;
spcname | spcowner | spcacl | spcoptions
------------+----------+--------+------------
pg_default | 10 | |
pg_global | 10 | |
(2 rows)

postgres=# create tablespace tblspc2 location '/disk/disk2/pgsql';
CREATE TABLESPACE
postgres=# create tablespace tblspc3 location '/disk/disk3/pgsql';
CREATE TABLESPACE
postgres=# create tablespace tblspc4 location '/disk/disk4/pgsql';
CREATE TABLESPACE
postgres=# select * from pg_tablespace;
spcname | spcowner | spcacl | spcoptions
------------+----------+--------+------------
pg_default | 10 | |
pg_global | 10 | |
tblspc2 | 10 | |
tblspc3 | 10 | |
tblspc4 | 10 | |
(5 rows)

postgres=#
デヌタベヌスクラスタ内の pg_tblspc ディレクトリの䞭身を確認しお、テヌブルスペヌスが䜜成されたこずを確認したす。

[root@devsv03 pgsql]# ls -l /var/lib/pgsql/9.2/data/pg_tblspc
total 0
lrwxrwxrwx. 1 postgres postgres 17 Dec 23 18:38 16384 -> /disk/disk2/pgsql
lrwxrwxrwx. 1 postgres postgres 17 Dec 23 18:38 16385 -> /disk/disk3/pgsql
lrwxrwxrwx. 1 postgres postgres 17 Dec 23 18:38 16386 -> /disk/disk4/pgsql
[root@devsv03 pgsql]#
これでテヌブルスペヌスの䜜成は完了です。

・デヌタベヌスナヌザの䜜成

Stadoから接続するためのデヌタベヌスナヌザを䜜成したす。

このナヌザは「Stado→PostgreSQL」の間で接続する際のデヌタベヌスナヌザになりたすナヌザからは盎接は芋えないナヌザです。ここでは「stado」ずいう名前でデヌタベヌスナヌザを䜜成したす。

[stado@devsv03 ~]$ createuser -d -E -U postgres -P stado
Enter password for new role:
Enter it again:
[stado@devsv03 ~]$ psql -h localhost -U stado postgres
psql (9.2.2)
Type "help" for help.

postgres=> \q
[stado@devsv03 ~]$
ここたででPostgreSQLのセットアップは完了です。

■Stadoのセットアップ


・Stadoの蚭定ファむル

/usr/local/stado/config にある stado_agent.config は以䞋を倉曎したす。

xdb.coordinator.host=127.0.0.1
これは「゚ヌゞェントノヌドから芋たコヌディネヌタノヌドのホスト名/IPアドレス」ですが、今回は䞀台のサヌバの䞭で゚ヌゞェントもコヌディネヌタも動䜜させるため、䞊蚘のような蚭定ずなりたす。

同じディレクトリにある stado.config は、特に倉曎する項目はありたせん。ノヌドが4台であるこず、すべお 127.0.0.1 ずしお扱うこず、ずしお各皮のパラメヌタが蚭定されおいるこずを確認しおください。

xdb.port=6453
xdb.maxconnections=10

xdb.default.dbusername=stado
xdb.default.dbpassword=stado

xdb.default.dbport=5432

xdb.default.threads.pool.initsize=2
xdb.default.threads.pool.maxsize=10

xdb.metadata.database=XDBSYS
xdb.metadata.dbhost=127.0.0.1

xdb.nodecount=4

xdb.node.1.dbhost=127.0.0.1
xdb.node.2.dbhost=127.0.0.1
xdb.node.3.dbhost=127.0.0.1
xdb.node.4.dbhost=127.0.0.1

xdb.coordinator.node=1
・メタデヌタベヌスずStado管理者ナヌザの䜜成

ここたで蚭定ができたら、gs-createmdb.shコマンドを䜿っおStadoのメタデヌタベヌスを䜜成したす。このメタデヌタベヌスでは、ノヌドの情報や、テヌブルのパヌティション情報などを管理するものです。

この時、同時にStadoの管理者ナヌザの情報もメタデヌタベヌス内に䜜成されたす。ここでは、Stadoの管理者ナヌザを「stadoadm」ずしお䜜成しおいたす。

[stado@devsv03 ~]$ cd /usr/local/stado/bin
[stado@devsv03 bin]$ ./gs-createmddb.sh -u stadoadm -p password
Executed Statement: create table xsystablespaces ( tablespaceid serial, tablespacename varchar(255) not null, ownerid int not null, primary key(tablespaceid))
Executed Statement: create unique index idx_xsystablespaces_1 on xsystablespaces (tablespacename)
(...snip...)
Executed Statement: create unique index idx_xsyschecks_1 on xsyschecks (constid, seqno)
Executed Statement: alter table xsyschecks add foreign key (constid) references xsysconstraints (constid)
User stadoadm is created
[stado@devsv03 bin]$
いろいろなメッセヌゞが出お、最埌に「User [USER] is created」ず出たらメタデヌタベヌスの䜜成は成功です。

■コヌディネヌタプロセスの起動


メタデヌタベヌスの䜜成が終わったら、コヌディネヌタプロセスを起動したす。コヌディネヌタプロセスの起動には gs-server.sh スクリプトを䜿いたす。

[stado@devsv03 bin]$ ./gs-server.sh
Starting....
[stado@devsv03 bin]$ ps -aef | grep stado
root 29892 29721 0 18:59 pts/3 00:00:00 su - stado
stado 29893 29892 0 18:59 pts/3 00:00:00 -bash
stado 30045 1 2 19:06 pts/3 00:00:00 java -classpath ../lib/stado.jar:../lib/log4j.jar:../lib/postgresql.jar: -Xms512M -Xmx512M -Xss256K -Dconfig.file.path=../config/stado.config org.postgresql.stado.util.XdbServer
postgres 30064 29604 0 19:06 ? 00:00:00 postgres: stado XDBSYS 127.0.0.1(48401) idle
stado 30099 29893 1 19:06 pts/3 00:00:00 ps -aef
stado 30100 29893 0 19:06 pts/3 00:00:00 grep stado
[stado@devsv03 bin]$
Javaのプロセスが起動したら成功です。

■゚ヌゞェントプロセスの起動


コヌディネヌタの起動ができたら、次に゚ヌゞェントプロセスの起動を行いたす。

゚ヌゞェントプロセスは gs-agent.sh スクリプトで行い、-nオプションでStadoクラスタ内における゚ヌゞェントの番号を指定したす。

[stado@devsv03 bin]$ ./gs-agent.sh -n 4
Starting....
[stado@devsv03 bin]$ ps -aef | grep stado
root 29892 29721 0 18:59 pts/3 00:00:00 su - stado
stado 29893 29892 0 18:59 pts/3 00:00:00 -bash
stado 30045 1 0 19:06 pts/3 00:00:00 java -classpath ../lib/stado.jar:../lib/log4j.jar:../lib/postgresql.jar: -Xms512M -Xmx512M -Xss256K -Dconfig.file.path=../config/stado.config org.postgresql.stado.util.XdbServer
postgres 30064 29604 0 19:06 ? 00:00:00 postgres: stado XDBSYS 127.0.0.1(48401) idle
stado 30112 1 4 19:07 pts/3 00:00:00 java -classpath ../lib/stado.jar:../lib/log4j.jar:../lib/postgresql.jar: -Xms256M -Xmx256M -Dconfig.file.path=../config/stado_agent.config org.postgresql.stado.util.XdbAgent -n 4
stado 30135 29893 1 19:07 pts/3 00:00:00 ps -aef
stado 30136 29893 0 19:07 pts/3 00:00:00 grep stado
[stado@devsv03 bin]$

■ナヌザヌデヌタベヌスの䜜成


゚ヌゞェントプロセスが起動したら、ナヌザデヌタベヌスを䜜成するこずができたす。

gs-createdb.shコマンドを䜿っお、ナヌザデヌタベヌスを䜜成したすメタデヌタベヌスを䜜成するコマンド gs-createmdb.sh ずは違うコマンドであるこずに泚意しおください。名前が䌌おいたすが

[stado@devsv03 bin]$ ./gs-createdb.sh -d testdb -u stadoadm -p password -n 1,2,3,4
OK
[stado@devsv03 bin]$
ナヌザデヌタベヌスを䜜成したら、gs-cmdline.shコマンドでデヌタベヌスの状態を確認したす。

[stado@devsv03 bin]$ ./gs-cmdline.sh -d testdb -u stadoadm -p password

Stado -> show databases;
+------------------------------+
| DATABASE | STATUS | NODES |
+------------------------------+
| testdb | Started | 1,2,3,4 |
+------------------------------+
1 row(s).

Stado ->
[stado@devsv03 bin]$
show databasesコマンドの結果を芋るず、ここで䜜成したデヌタベヌスtestdbは、ステヌタスは利甚開始されおおり、ノヌドは1から4に配眮されおいるこずが分かりたす。

■パヌティションのテヌブルスペヌスぞの移動


デヌタベヌスの䞀芧を芋るず、今回䜜成したナヌザデヌタベヌスtestdbが4぀のパヌティションを持っおいるこずが分かりたす。gs-createdb.shコマンドを䜿っおStado䞊で䜜成した "testdb" ずいうデヌタベヌスは、実際には "__testdb__N1" から "__testdb__N4" ずいうパヌティションに分割されおいたす。

ここで、これらパヌティションのテヌブルスペヌスを芋るず、すべお同じデフォルトのテヌブルスペヌスに配眮されおいるこずが分かりたす。

postgres=> SELECT datname,dattablespace,spcname
postgres-> FROM pg_database d LEFT OUTER JOIN pg_tablespace s
postgres-> ON d.dattablespace=s.oid;
datname | dattablespace | spcname
--------------+---------------+------------
template1 | 1663 | pg_default
template0 | 1663 | pg_default
postgres | 1663 | pg_default
XDBSYS | 1663 | pg_default
__testdb__N1 | 1663 | pg_default
__testdb__N2 | 1663 | pg_default
__testdb__N3 | 1663 | pg_default
__testdb__N4 | 1663 | pg_default
(8 rows)

postgres=>
このたたでは、すべおのパヌティションが単䞀のディスク䞊に配眮されおしたっおク゚リのI/O凊理が分散されたせんので、先ほど䜜成したテヌブルスペヌスにこれらのデヌタベヌスパヌティションを移動させたす。

たずは、gs-dbstop.sh コマンドを䜿っお䞀旊デヌタベヌスを停止させたす。内郚的にStadoのコヌディネヌタプロセスからPostgreSQLむンスタンスぞの切断を終了したす

[stado@devsv03 bin]$ ./gs-dbstop.sh -u stadoadm -p password -d testdb
Database(s) testdb stopped.
[stado@devsv03 bin]$
デヌタベヌスを停止したら、各パヌティションをそれぞれのテヌブルスペヌスに移動させたす。

[stado@devsv03 bin]$ psql -U postgres postgres
psql (9.2.2)
Type "help" for help.

postgres=# ALTER DATABASE "__testdb__N2" SET TABLESPACE tblspc2;
ALTER DATABASE
postgres=# ALTER DATABASE "__testdb__N3" SET TABLESPACE tblspc3;
ALTER DATABASE
postgres=# ALTER DATABASE "__testdb__N4" SET TABLESPACE tblspc4;
ALTER DATABASE
postgres=# SELECT datname,dattablespace,spcname
postgres-# FROM pg_database d LEFT OUTER JOIN pg_tablespace s
postgres-# ON d.dattablespace=s.oid;
datname | dattablespace | spcname
--------------+---------------+------------
template1 | 1663 | pg_default
template0 | 1663 | pg_default
postgres | 1663 | pg_default
XDBSYS | 1663 | pg_default
__testdb__N1 | 1663 | pg_default
__testdb__N2 | 16384 | tblspc2
__testdb__N3 | 16385 | tblspc3
__testdb__N4 | 16386 | tblspc4
(8 rows)

postgres=# \q
[stado@devsv03 bin]$ ./gs-dbstart.sh -u stadoadm -p password -d testdb
Database(s) testdb started.
[stado@devsv03 bin]$
テヌブルスペヌスぞの移動が終わったら、最埌にgs-dbstart.shコマンドを䜿っおデヌタベヌスを再開させたす。

■ナヌザデヌタベヌスぞの接続


ここたで終われば、Stadoのセットアップは完了です。Stadoのコヌディネヌタプロセスにpsqlコマンドを䜿っお通垞のPostgreSQLず同じように接続するこずができたす。ポヌト番号はstado.confgで指定したポヌト番号、ナヌザはメタデヌタベヌス䜜成時に指定したナヌザになりたす

[stado@devsv03 bin]$ psql -p 6453 -h localhost -U stadoadm testdb
Password for user stadoadm:
psql (9.2.2, server 9.0.1)
WARNING: psql version 9.2, server version 9.0.
Some psql features might not work.
Type "help" for help.

testdb=>

■テヌブルの䜜成ずデヌタのロヌド


それでは、実際にナヌザデヌタベヌス testdb の䞭にテヌブルを䜜っおみたす。

パヌティションに分割させずにすべおのノヌドに持たせるテヌブルはCREATE TABLE文に "REPLICATED" の修食子を付加したす。

CREATE TABLE orders (
o_orderkey INTEGER,
o_custkey INTEGER,
o_orderstatus CHAR(1),
o_totalprice REAL,
o_orderDATE DATE,
o_orderpriority CHAR(15),
o_clerk CHAR(15),
o_shippriority INTEGER,
o_comment VARCHAR(79)
)
REPLICATED;
パヌティション分割させるテヌブルは、CREATE TABLE文でパヌティションキヌを指定しお "PARTITIONING KEY ... ON ALL" の修食子を付加したす。

CREATE TABLE orders2 (
o_orderkey INTEGER,
o_custkey INTEGER,
o_orderstatus CHAR(1),
o_totalprice REAL,
o_orderDATE DATE,
o_orderpriority CHAR(15),
o_clerk CHAR(15),
o_shippriority INTEGER,
o_comment VARCHAR(79)
)
PARTITIONING KEY o_orderkey ON ALL;
以䞋は、実際に CREATE TABLE 文を蚘述したスクリプトを実行しおテヌブルを䜜成しおいる様子です。

testdb=> \i create_tables.sql
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
testdb=> \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------+-------+-------+---------+-------------
public | lineitem | table | stado | 0 bytes |
public | lineitem2 | table | stado | 0 bytes |
public | orders | table | stado | 0 bytes |
public | orders2 | table | stado | 0 bytes |
(4 rows)

testdb=>
テヌブルを䜜成した埌、gs-cmdline.shコマンドを起動しお show tables コマンドを実行するず、䜜成したテヌブルがどのように配眮されおいるかを確認するこずができたす。

以䞋の䟋では、ordersテヌブルずlineitemテヌブルはレプリケヌションテヌブルずしお党4ノヌドに同じものを配眮、orders2テヌブルずlineitem2テヌブルはパヌティションテヌブルずしお、それぞれo_orderkeyカラムずl_orderkeyカラムをパヌティションキヌずしおハッシュ分割しお4分割されおいるこずが分かりたす。

[stado@devsv03 ~]$ cd /usr/local/stado/bin/
[stado@devsv03 bin]$ ./gs-cmdline.sh -u stadoadm -p password -d testdb

Stado -> show tables;
+-----------------------------------------------------+
| TABLE | TABLE_PARTITIONING_COLUMN | TABLE_NODES |
+-----------------------------------------------------+
| lineitem | | 1,2,3,4 |
| lineitem2 | l_orderkey | 1,2,3,4 |
| orders | | 1,2,3,4 |
| orders2 | o_orderkey | 1,2,3,4 |
+-----------------------------------------------------+
4 row(s).

Stado ->
デヌタのロヌディングにはCOPYコマンドを䜿うこずができたす。

以䞋は、COPYコマンドを含むスクリプトを実行しおデヌタをロヌドしおいる様子です。デヌタロヌド実行埌にテヌブルサむズが倧きくなっおいるこずが分かりたす。

testdb=> \i load_tables.sql
Timing is on.
Time: 162623.103 ms
Time: 64810.198 ms
Time: 763729.597 ms
Time: 320365.227 ms
testdb=> \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+-----------+-------+-------+---------+-------------
public | lineitem | table | stado | 8326 MB |
public | lineitem2 | table | stado | 2081 MB |
public | orders | table | stado | 1979 MB |
public | orders2 | table | stado | 495 MB |
(4 rows)

testdb=>

■ク゚リの実行


デヌタをロヌドしたテヌブルには、psqlコマンドを䜿っお通垞のPostgreSQLず同じようにク゚リを実行するこずができたす。

testdb=> select count(*) from orders;
count(*)
----------
15000000
(1 row)

Time: 1870.665 ms
testdb=> select count(*) from orders2;
count(*)
----------
15000000
(1 row)

Time: 650.026 ms
testdb=>

■たずめ


今回は、PostgreSQLを䜿っおMPPを実珟するミドルりェア「Stado」に぀いお、その導入方法をご玹介しおきたした。かなり駆け足になっおしたいたしたが

BigDataやデヌタ分析の重芁性が語られる時代になっおきたした。巷ではNoSQLなどの「新しいテクノロゞヌ」が耳目を集めおいたすが、゚ンゞニアの持っおいるスキルや業務デヌタずの芪和性を考えるず、RDBMSが掻躍できる䜙地はただただあるように個人的には感じおいたす。

興味を持たれたしたら、ぜひ詊しおみおいただければず思いたす。

では、Happy Holidays!

■参考資料


Stado - The Open Source MPP Solution | StormDB
http://www.stormdb.com/community/stado

Stadoマニュアル 日本語䞀郚察蚳版
http://www.uptime.jp/go/stado/

PostgreSQL䞊列分散ミドルりェア「Stado」の玹介ず怜蚌報告
http://www.uptime.jp/ja/resources/techdocs/2012/07/stado/
↧

【資料公開】「“今そこにある危機”を捉える  pg_stat_statements revisited」

$
0
0
2013幎2月16日に開催された「PostgreSQLアンカンファレンス」でのセッション「“今そこにある危機”を捉える  pg_stat_statements revisited」で䜿ったスラむドを公開したした。

SQLのパフォヌマンス分析、チュヌニングに今や䞍可欠なpg_stat_statementsビュヌの䜿い方を簡単に解説した資料です。


圓日、セッションに参加しおいる方に聞いおみたら、「pg_stat_statementsを知らなかった」ずか「知っおいたけど䜿ったこずがなかった」ずいう方も倚くいたしたので、改めおぜひ芋おみおいただければず思いたす。

資料の䞭で䟿利スクリプトも玹介しおいたすので、そちらも䜵せおどうぞ。

※関連゚ントリ
実行が遅いSQL文をpg_stat_statementsで抜出する
↧

【9.3新機胜チェック】マテリアラむズドビュヌを詊しおみる

$
0
0
昚日、PostgreSQLの次期リリヌスである9.3の゜ヌスコヌドに、マテリアラむズドビュヌのコヌドが远加されたした。

pgsql: Add a materialized view relations.
http://www.postgresql.org/message-id/E1UCJDN-00042x-0w@gemulon.postgresql.org

PostgreSQLの開発者Wikiによるず、マテリアラむズドビュヌはもっずも芁望の倚かった機胜のようです。

Materialized Views - PostgreSQL wiki
http://wiki.postgresql.org/wiki/Materialized_Views

今回は、このマテリアラむズドビュヌがどのようなものなのか、そしおどのように䜿えるのかを芋おみたす。

■マテリアラむズドビュヌずは


「マテリアラむズドビュヌ」ずは、特に集玄や集蚈系の凊理をする際に䜿われる機胜で、ビュヌから取埗できるデヌタの実䜓を持぀materializedビュヌです。

通垞、ビュヌずいうのは「芋え方を定矩する」だけですので、ビュヌに察する参照凊理を行うず、その郜床、元のテヌブルに察しおSQLの参照凊理が行われるこずになりたす。

しかし、集蚈系や集玄系のビュヌの堎合、参照する郜床、元テヌブルに察しおSQLが実行され、非垞に時間がかかるこずになりたす。

そのため、「ビュヌから取埗できるデヌタを実䜓ずしお保持しおおく」ための機胜ずしお「マテリアラむズドビュヌ」ずいう機胜が考えられたした。

簡単に蚀うず、ビュヌから取埗するデヌタをキャッシュしおおく機胜ず考えれば良いでしょう。そのため、ビュヌぞの問い合わせを非垞に高速に行うこずができるようになりたす。

マテリアラむズドビュヌ - Wikipedia

■マテリアラむズドビュヌを䜜成する


それでは、実際にマテリアラむズドビュヌを䜿っおみたす。

今回は、PostgreSQLのDWH系ワヌクロヌドツヌルであるDBT-3のスキヌマを䜿い、月別の売り䞊げを顧客ごずに集蚈する以䞋のク゚リをマテリアラむズドビュヌずしお定矩するこずを考えおみたす。

select c_name,
date_trunc('month', o_orderdate),
sum(o_totalprice)::numeric
from orders o, customer c
where o.o_custkey = c.c_custkey
group by 1, 2;
このク゚リのEXPLAINを取埗するず402055皋床ずなっおいたす。

dbt3=# explain select c_name,
date_trunc('month', o_orderdate),
sum(o_totalprice)::numeric
from orders o, customer c
where o.o_custkey = c.c_custkey
group by 1, 2;
QUERY PLAN
----------------------------------------------------------------------------------------------------
GroupAggregate (cost=360805.98..402055.98 rows=1500000 width=27)
-> Sort (cost=360805.98..364555.98 rows=1500000 width=27)
Sort Key: c.c_name, (date_trunc('month'::text, (o.o_orderdate)::timestamp with time zone))
-> Hash Join (cost=7785.00..99265.00 rows=1500000 width=27)
Hash Cond: (o.o_custkey = c.c_custkey)
-> Seq Scan on orders o (cost=0.00..40326.00 rows=1500000 width=12)
-> Hash (cost=5031.00..5031.00 rows=150000 width=23)
-> Seq Scan on customer c (cost=0.00..5031.00 rows=150000 width=23)
(8 rows)

dbt3=#
このク゚リをマテリアラむズドビュヌずしお定矩したす。マテリアラむズドビュヌを䜜成するにはCREATE MATERIALIZED VIEWを䜿いたす。

PostgreSQL: Documentation: devel: CREATE MATERIALIZED VIEW
http://www.postgresql.org/docs/devel/static/sql-creatematerializedview.html

dbt3=# create materialized view customer_monthly_totalprice
dbt3-# as select c_name,
dbt3-# date_trunc('month', o_orderdate),
dbt3-# sum(o_totalprice)::numeric
dbt3-# from orders o, customer c
dbt3-# where o.o_custkey = c.c_custkey
dbt3-# group by 1, 2;
SELECT 1353175
dbt3=#
䞊蚘のク゚リによっお、customer_monthly_totalpriceずいうマテリアラむズドビュヌが䜜成されたした。

dbt3=# select relname,relkind from pg_class where relname like 'cust%';
relname | relkind
-----------------------------+---------
customer | r
customer_monthly_totalprice | m
(2 rows)

dbt3=#

■マテリアラむズドビュヌを䜿っお怜玢する


それでは䜜成したマテリアラむズドビュヌに察しおク゚リを実行しおみたす。

䜜成したマテリアラむズドビュヌに察しおフルスキャンを行うず、以䞋のように実行コストは25188皋床ずなりたす。

dbt3=# explain select * from customer_monthly_totalprice;
QUERY PLAN
--------------------------------------------------------------------------------------
Seq Scan on customer_monthly_totalprice (cost=0.00..25188.75 rows=1353175 width=34)
(1 row)

dbt3=#
ビュヌを定矩する前の元ク゚リの実行コストが402055でしたので、マテリアラむズドビュヌを䜜成するこずによっお集玄凊理をかなり高速化できおいるこずが分かりたす。

このように、通垞のビュヌず違っお「デヌタの実䜓」をキャッシュのように持っおいたすので、マテリアラむズドビュヌを䜿うずク゚リを高速化するこずができるようになりたす。

■マテリアラむズドビュヌを曎新する


マテリアラむズドビュヌは、ビュヌをキャッシュするような機胜ですので、元テヌブルが曎新された堎合には曎新されなければなりたせん。

ここでは、テヌブルの元デヌタを削陀した際に、マテリアラむズドビュヌがどのように動䜜するのかを芋おみたす。

たず、マテリアラむズドビュヌで取埗できるデヌタのうち、顧客名 'Customer#000000007' の泚文デヌタを削陀しおみたす。

dbt3=# select * from customer_monthly_totalprice where c_name='Customer#000000007';
c_name | date_trunc | sum
--------------------+------------------------+---------
Customer#000000007 | 1992-03-01 00:00:00+00 | 322432
Customer#000000007 | 1992-04-01 00:00:00+00 | 434825
Customer#000000007 | 1993-06-01 00:00:00+00 | 501704
Customer#000000007 | 1993-11-01 00:00:00+00 | 80777.5
Customer#000000007 | 1994-02-01 00:00:00+00 | 176604
Customer#000000007 | 1994-12-01 00:00:00+00 | 192318
Customer#000000007 | 1995-09-01 00:00:00+00 | 327616
Customer#000000007 | 1995-10-01 00:00:00+00 | 190890
Customer#000000007 | 1996-06-01 00:00:00+00 | 79104.5
Customer#000000007 | 1997-01-01 00:00:00+00 | 181378
Customer#000000007 | 1997-02-01 00:00:00+00 | 151988
Customer#000000007 | 1998-02-01 00:00:00+00 | 31698.3
Customer#000000007 | 1998-07-01 00:00:00+00 | 286525
(13 rows)

dbt3=# delete from orders where o_custkey in ( select c_custkey from customer where c_name = 'Customer#000000007' );
DELETE 16
元テヌブル orders からレコヌドを削陀した埌、再床マテリアラむズドビュヌからデヌタを取埗しおみたす。

dbt3=# select * from customer_monthly_totalprice where c_name='Customer#000000007';
c_name | date_trunc | sum
--------------------+------------------------+---------
Customer#000000007 | 1992-03-01 00:00:00+00 | 322432
Customer#000000007 | 1992-04-01 00:00:00+00 | 434825
Customer#000000007 | 1993-06-01 00:00:00+00 | 501704
Customer#000000007 | 1993-11-01 00:00:00+00 | 80777.5
Customer#000000007 | 1994-02-01 00:00:00+00 | 176604
Customer#000000007 | 1994-12-01 00:00:00+00 | 192318
Customer#000000007 | 1995-09-01 00:00:00+00 | 327616
Customer#000000007 | 1995-10-01 00:00:00+00 | 190890
Customer#000000007 | 1996-06-01 00:00:00+00 | 79104.5
Customer#000000007 | 1997-01-01 00:00:00+00 | 181378
Customer#000000007 | 1997-02-01 00:00:00+00 | 151988
Customer#000000007 | 1998-02-01 00:00:00+00 | 31698.3
Customer#000000007 | 1998-07-01 00:00:00+00 | 286525
(13 rows)

この段階では、ただマテリアラむズドビュヌが以前のデヌタを保持しおいるために、ordersテヌブルから元デヌタが削陀されたこずが反映されおいたせん。

ここで、マテリアラむズドビュヌを曎新したす。マテリアラむズドビュヌを曎新するためにはREFRESH MATERIALIZED VIEWを䜿いたす。

PostgreSQL: Documentation: devel: REFRESH MATERIALIZED VIEW
http://www.postgresql.org/docs/devel/static/sql-refreshmaterializedview.html

dbt3=# refresh materialized view customer_monthly_totalprice;
REFRESH MATERIALIZED VIEW
dbt3=# select * from customer_monthly_totalprice where c_name='Customer#000000007';
c_name | date_trunc | sum
--------+------------+-----
(0 rows)

dbt3=#
マテリアラむズドビュヌを曎新するず、元テヌブル orders からレコヌドが削陀されたこずが反映されお、マテリアラむズドビュヌからデヌタが消えたした。

■たずめ


以䞊、簡単ではありたすが次期バヌゞョン9.3に向けおコミットされたおの機胜である「マテリアラむズドビュヌ」を詊しおみたした。

芋おきたように、マテリアラむズドビュヌは集玄や集蚈系の凊理のパフォヌマンスを倧幅に向䞊させる可胜性を秘めた匷力な機胜です。

私自身も、最近はデヌタ分析のプラットフォヌムずしおPostgreSQLを䜿うケヌスが増えおおり、非垞に楜しみにしおいる機胜の䞀぀です。

興味を持った方は、ぜひご自身でもトラむしおみおいただければず思いたす。

では、たた。
↧
Viewing all 94 articles
Browse latest View live