2016年12月20日火曜日

PostgreSQL 9.6 パラレルクエリに関する小ネタ

よく考えたら当たり前なんだけど、いざ気付くと「えーっ!」となった衝撃的な話。

「パラレルクエリは、マテリアライズド・ビューのリフレッシュに使えない」疑惑

パラレルクエリの制限事項って結構あるんですね。そのうちの1つを9.6.1時点のマニュアルから抜粋すると 

クエリがデータを書き込むか、データベースの行をロックする場合。 クエリがデータ更新操作をトップレベルあるいはCTE内で含むと、そのクエリに対するパラレルプランは生成されません。 これは現在の実装の制限で、将来のリリースでは解除される可能性があります。 

要はDMLはダメと言っているように見える。じゃあマテビュー作るのもダメじゃね?という当然の話ではあるのですが。夜間に巨大なテーブルの加工や集計(そんな処理を数十分とか、数時間かけて)した結果をマテビューにして、なんとか日中はサクサク動くように頑張るわけじゃないですか。
で、パラレルクエリが入ったときに、「くそ重いロックをとるマテビューのリフレッシュが、2時間かかってたやつが1分になったら(キラキラ)」などと夢をいだいたものです。ところがそれが現状のパラレルクエリでは何も変わらないのです。がびーん。

そんなわけで、実際に試してみることにします。 わざわざ実行計画を見るのはめんどいマテビューでは内部で実行されたクエリの実行計画は覆い隠されてしまうので、単に所要時間で(psqlの\timingで)パラったか判断します。

今回使う表(今回、表の中身は重要ではない。件数と所要時間だけ注目)
demo=# \timing
demo=# SELECT count(*) FROM tab1;
  count
----------
 28864000
(1 行)
時間: 669.988 ms   --- パラレルの時は1秒以下、非パラレルだと4秒強かかることを確認済み。

/* 実行計画はこんな感じ */
demo=# EXPLAIN ANALYZE
demo-# SELECT count(*) FROM tab1;
                                                                 QUERY PLAN

--------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=333435.96..333435.97 rows=1 width=8) (actual time=1015.604..1015.604 rows=1 loops=1)
   ->  Gather  (cost=333435.33..333435.94 rows=6 width=8) (actual time=1015.510..1015.594 rows=7 loops=1)
         Workers Planned: 6
         Workers Launched: 6
         ->  Partial Aggregate  (cost=332435.33..332435.34 rows=1 width=8) (actual time=1008.983..1008.983 rows=1 loops=7)
               ->  Parallel Seq Scan on tab1  (cost=0.00..320408.67 rows=4810667 width=0) (actual time=0.043..622.811 rows=4123429 loops=7)
 Planning time: 0.126 ms
 Execution time: 1017.730 ms

/* テーブルサイズはこれぐらい */
demo=# SELECT pg_size_pretty(pg_relation_size('tab1'));
 pg_size_pretty
----------------
 2127 MB

マテリアライズド・ビューを作成してリフレッシュ

demo=# CREATE MATERIALIZED VIEW mv1 AS
demo-# SELECT count(*) FROM tab1;
SELECT 1
時間: 4510.077 ms   --- む、この時間は・・・。

demo=# REFRESH MATERIALIZED VIEW mv1;
REFRESH MATERIALIZED VIEW
時間: 4451.020 ms   --- む、この時間は・・・。
うーん、パラられない。残念でならない!
というわけで、何かそれに代わる策がないか試してみます。

VIEWの場合

demo=# CREATE VIEW v2 AS
SELECT count(*) FROM tab1;
CREATE VIEW
時間: 2.151 ms
demo=# SELECT * FROM v2;
  count
----------
 28864000
(1 行)

時間: 682.303 ms --- viewならOKだよねうん。
まあ、これは大丈夫か。SELECTだけだし。でも結果を保存して再利用できない。

普通の表をつくってINSERT SEELCT

demo=# CREATE TABLE t3 (count numeric);
CREATE TABLE

demo=# INSERT INTO t3
demo-# SELECT count(*) FROM tab1;
INSERT 0 1
時間: 4506.088 ms --- だめ
だめ。デスヨネー。

外部テーブルを介してMビュー。

外部テーブルで問合せた先は、リモートのサーバー側でよしなに実行計画を作ってくれるはずなので淡い期待を込めてやってみるが・・・
demo=# CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw
demo-# OPTIONS (dbname 'demo',host 'localhost',port '5432'); -- 自分自身を見に行くFDW
CREATE SERVER
時間: 1.331 ms
demo=# CREATE USER MAPPING FOR public SERVER loopback
demo-# OPTIONS (user 'postgres',password 'postgres');
CREATE USER MAPPING
時間: 0.914 ms
demo=# IMPORT FOREIGN SCHEMA public LIMIT TO (tab1)
demo-# FROM SERVER loopback
demo-# INTO loopback;
IMPORT FOREIGN SCHEMA
時間: 7.039 ms
demo=# EXPLAIN ANALYZE
demo-# SELECT * FROM loopback.tab1 WHERE sales_id = 0; -- 一応ループバックしてFOREIGN SCANしてることを確認
                                                   QUERY PLAN

----------------------------------------------------------------------------------------------------------------
 Foreign Scan on tab1  (cost=100.00..117.59 rows=3 width=112) (actual time=396.211..4527.848 rows=1152 loops=1)
 Planning time: 0.286 ms
 Execution time: 4528.583 ms
(3 行)

時間: 4529.705 ms

demo=# CREATE MATERIALIZED VIEW mv2 AS
demo-# SELECT count(*) FROM loopback.tab1;
SELECT 1
時間: 71687.426 ms  ---パラレルかどうかよりaggrigate push downが効いてないせいかな。

demo=# REFRESH MATERIALIZED VIEW mv2;
REFRESH MATERIALIZED VIEW
時間: 68418.958 ms
ダメ。むしろダメ。Aggrigateのpush downはまだ実装されてないんだった。全件もってきて集計してるんだとしたら、仮にリモートでパラレルで動いてくれても、ネットワークが重すぎる。


\copy と COPY

demo=# \copy (SELECT count(*) FROM tab1) TO demo.copy;
COPY 1
時間: 4518.860 ms --ダメ

demo=# COPY (SELECT count(*) FROM tab1) TO '/home/enterprisedb/demo.copy';
COPY 1
時間: 4524.612 ms --ダメ
だめ。うーん、これぐらいはイケると思ったんだが。

\oでファイルに出力

demo=# \o demo2.copy
demo=# SELECT count(*) FROM tab1; -- OK
時間: 699.871 ms
OK!うれしい!

psqlの引数としてクエリを投げ、結果をファイルに出力

[postgres@demo ~]$ time psql -U postgres demo -At -c 'SELECT count(*) FROM tab1' -o demo3.copy

real    0m0.725s -- OK
user    0m0.003s
sys     0m0.009s

[postgres@demo ~]$ psql -U postgres demo -At -c "COPY t3 FROM '/home/enterprisedb/demo3.copy'"
 -- これで格納もできる。(t3は上部検証で作成したやつ)
  -- もちろん、集計ずみの1行だけ格納するので一瞬で終わる。
OK!うれしい!これだ感!これができればシェルとかプログラムで簡単にマテビューチックなことができる、はず。

2016年12月16日金曜日

PostgreSQL 9.6のパラレルが間違う件!

本投稿は、PostgreSQL Advent Calendar 2016 の 12/16 担当です。
昨日は「.NET Core で PostgreSQL を使ってみたら結構いけていたという話」を @creativewebjp さんが書いてくださいました。


■パラレルクエリと戯れて


さて、Ver9.6の登場までポスグレ界の話題を総ざらいしていたパラレルクエリですが、みなさん使ってますか?
大量データの領域で真価を発揮するものあって、なかなか現実的なサイズで試せていないのが現状で、採用に二の足を踏んでしまう、なんて話もちらほら聞いています。私は仕事でEnterpriseDB社が開発するPostgreSQLの商用版、EDB Postgresを担当していますが、そのβテストを兼ねてOSS版PostgreSQLのパラレル動作をみていたのですが、やはり試してみてわかることが結構ありました。
採用するかどうか迷っている方がいたら、とっかかりは非常に簡単なので、是非トライして、自身のアプリケーションでどの程度使えるかを評価してみるべきだと思います。バッチリとハマったときの効果は抜群ですよ。

とは言え、良いときの性能評価はそこそこ出回っていると思うので、今回は、案外うまくいかないパラレルクエリの話を紹介します。


■いろいろ検証して得られた「間違うケース」


間違うケース1 でかい表同士をJOINするとき


前提知識として、PostgreSQLのパラレル・クエリは、表のサイズで並列度が決定されます。
適当な例で言うなら、10GBの表は8並列、500MBの表は4並列ぐらいで動きます。
んまあ、これはあくまでも単一の表をFROM句に書いてSELECTした場合に、これぐらいの並列度が良さそうと判断されたにすぎないです。
10GBを 
1プロセスで読むコスト > 8プロセスで分担して、後でGather(集約)するコスト
と判断したので、そういう実行計画が選択されるということですね。

これが、JOINの場合はどうやら変わってくるようです。
2つの表をパラレルで読んで、Gatherするとなると、 そのオーバーヘッドの方が大きくなってしまって、せっかくパラレルにしたのに所要時間はむしろ長くなってしまいます。

どちらもパラレルが効くぐらい大きい表なので、適切にパラレル処理して欲しいところではありますが、単純に表のサイズだけでは決められず、どちらか一方だけパラレルに動いてくれた方が良い結果になるのです。もちろんそういう目論見で、プランナはどちらか一方の表をパラレルで読もうとします。
問題は、PostgreSQLだと「どちらの表をパラレルで処理するか?」の調整が非常に難しいこと。約10GBのlineoder表と、約500MBのpart表をごく単純に結合してみると、このような実行計画になりました。
 /* 実行したクエリ(ごく単純なJOIN) */
EXPLAIN ANALYZE
SELECT lo_partkey,sum(lo_quantity)
FROM   lineorder
JOIN   part
       ON    lo_partkey = p_partkey 
WHERE  p_name LIKE 'k%'
GROUP BY lo_partkey;
 /* いかにもパラレルが効きそうなlineorderが普通にSeqScanされてる */
->  Hash Join  (cost=28661.34..3682122.56 rows=1111820 width=9) 
               (actual time=67.061..57496.965 rows=1286337 loops=1)
->  Seq Scan on lineorder  (cost=0.00..3214594.56 rows=114066256 width=9)
                         ★(actual time=0.010..27750.208 rows=114066226 loops=1)
      ->  Hash  (cost=28490.76..28490.76 rows=13646 width=4) 
                (actual time=66.904..66.904 rows=15731 loops=1)
            Buckets: 16384  Batches: 1  Memory Usage: 682kB
            ->  Gather  (cost=1000.00..28490.76 rows=13646 width=4) 
                        (actual time=0.342..61.640 rows=15731 loops=1)
                  Workers Planned: 3
                  Workers Launched: 3
                  ->  Parallel Seq Scan on part  (cost=0.00..26126.16 rows=4402 width=4) 
                                               ★(actual time=0.029..59.519 rows=3933 loops=4)
                        Filter: ((p_name)::text ~~ 'k%'::text)
                        Rows Removed by Filter: 346067

今回のケースでは、どうしても小さい方の表をパラレルにしたがる(先に読むからだろうか?)で、大きい方の表をシーケンシャルに読んでしまいました。

効果は未確認ですが、解決策としては、ALTER TABLEでそのテーブルを読むときに使うワーカーの数を指定できるので、それでpartテーブルの方は0に固定してしまうことでしょうか。
ただし、「こっちクエリではpart表をパラレルに読んでほしいんだよね~」という要望に応えるのが難しくなります。


そこでEDB Postgresです。EDB Postgresでは、オプティマイザ・ヒントを指定でき、2017年1月にリリース予定のEDB 9.6ではパラレル・ヒントに対応します。そこで実行計画をいじくるとこんな感じです。

 /* EDB版の実行したクエリ(パラレル・ヒントで各表の並列度を指定) */
EXPLAIN ANALYZE
SELECT /*+ PARALLEL(part 0) PARALLEL(lineorder 4) */ lo_partkey,sum(lo_quantity)
FROM   lineorder
JOIN   part
       ON    lo_partkey = p_partkey 
WHERE  p_name LIKE 'k%'
GROUP BY lo_partkey;
 /* スキャンの並列度が変わり、Hash Joinまでパラレル実行している */
HashAggregate  (cost=2624706.98..2636997.29 rows=983225 width=36) 
               (actual time=17414.085..17421.061 rows=11272 loops=1)
   Group Key: lineorder.lo_partkey
   ->  Gather  (cost=39151.57..2619147.88 rows=1111820 width=9) 
               (actual time=181.809..16260.314 rows=1286337 loops=1)
         Workers Planned: 4
         Workers Launched: 4
         ->  Hash Join  (cost=38151.57..2506965.88 rows=1111820 width=9) 
                      ☆(actual time=233.851..16886.113 rows=257267 loops=5)
               Hash Cond: (lineorder.lo_partkey = part.p_partkey)
               ->  Parallel Seq Scan on lineorder  (cost=0.00..2359097.64 rows=28516564 width=9) 
                                                 ★(actual time=0.034..8187.008 rows=22813245 loops=5)
               ->  Hash  (cost=37981.00..37981.00 rows=13646 width=4) 
                         (actual time=233.525..233.525 rows=15731 loops=5)
                     Buckets: 16384  Batches: 1  Memory Usage: 682kB
                     ->  Seq Scan on part  (cost=0.00..37981.00 rows=13646 width=4) 
                                         ★(actual time=0.026..225.321 rows=15731 loops=5)

狙った表をパラレルスキャンさせることで、PostgreSQLで生じていた問題をバッチリ解決することができました。


間違うケース2 Window関数なのか、ソートなのか。原因不明(ヲイ!)


PostgreSQLでは、Windows関数つかって、それでないと1クエリで得ることがなかなか難しい複雑な集計を任せることができます。
とは言えその複雑さはなかなかなもので、さぞかしプランナは頭を悩ませることでしょう。

以前に本ブログで書いた「PostgreSQLらしい集計クエリ」で紹介しているテーブルで、データを何重にもいれまくって、2GBぐらいの表を用意しました。それを集計してみます。

 /* window関数 */
EXPLAIN ANALYZE
SELECT *,
    rank() OVER (PARTITION BY "ブランド" ORDER BY "売上" DESC) AS "順位"
FROM (
    SELECT
        sales_date::date as "売上日"
       ,shop             as "ブランド"
       ,sum(price)       as "売上"
    FROM  tab1
    GROUP BY "売上日","ブランド") AS "集計";

 /* 結果イメージ。ブランド毎に売上の多かった日ランキング */
   売上日   |  ブランド  |   売上    | 順位
------------+------------+-----------+------
 2016-05-11 | DIESEL     | 632141504 |    1
 2016-07-31 | DIESEL     | 622096896 |    2
 2016-11-29 | DIESEL     | 620818688 |    3
  :
 2016-04-06 | Dior       | 962435003 |    1
 2016-03-23 | Dior       | 889015872 |    2
    :
 /* この時の実行計画がこちら。パラレルになってる。約9秒。 */
 WindowAgg  (cost=3430456.39..3454473.19 rows=1200840 width=52) (actual time=9776.175..9777.199 rows=1095 loops=1)
   ->  Sort  (cost=3430456.39..3433458.49 rows=1200840 width=44) (actual time=9776.165..9776.300 rows=1095 loops=1)
         Sort Key: "集計"."ブランド", "集計"."売上" DESC
         Sort Method: quicksort  Memory: 134kB
         ->  Subquery Scan on "集計"  (cost=3115228.89..3235312.89 rows=1200840 width=44)
                                      (actual time=9764.055..9773.283 rows=1095 loops=1)
               ->  Finalize GroupAggregate  (cost=3115228.89..3223304.49 rows=1200840 width=44)
                                            (actual time=9764.054..9773.062 rows=1095 loops=1)
                     Group Key: ((tab1.sales_date)::date), tab1.shop
                     ->  Sort  (cost=3115228.89..3133241.49 rows=7205040 width=44)
                               (actual time=9764.012..9765.509 rows=7665 loops=1)
                           Sort Key: ((tab1.sales_date)::date), tab1.shop
                           Sort Method: quicksort  Memory: 1270kB
                           ->  Gather  (cost=1064650.75..1851272.12 rows=7205040 width=44)
                                       (actual time=6412.121..9757.054 rows=7665 loops=1)
                                 Workers Planned: 6
                                 Workers Launched: 6
                                 ->  Partial GroupAggregate  (cost=1063650.75..1129768.12 rows=1200840 width=44)
                                                             (actual time=6518.572..9655.286 rows=1095 loops=7)
                                       Group Key: ((tab1.sales_date)::date), tab1.shop
                                       ->  Sort  (cost=1063650.75..1075676.95 rows=4810477 width=19)
                                                 (actual time=6516.858..8298.955 rows=4123429 loops=7)
                                             Sort Key: ((tab1.sales_date)::date), tab1.shop
                                           ☆Sort Method: external merge  Disk: 125680kB
                                             ->  Parallel Seq Scan on tab1  (cost=0.00..332432.96 rows=4810477 width=19)
                                                                          ★(actual time=0.071..1195.131 rows=4123429loops=7)

お、パラレルになった。
しかし、これ、Disk Sortになっていますね。メモリソートになればもっと早くなるんじゃ。SET work_mem to '2GB' などとして再実行

あれ?
 /* 実行計画がこちら。メモリソートになったらなぜか非パラレルに。約20秒かかっている。 */
 WindowAgg  (cost=1000838.70..1024855.50 rows=1200840 width=52) (actual time=21083.366..21084.609 rows=1095 loops=1)
   ->  Sort  (cost=1000838.70..1003840.80 rows=1200840 width=44) (actual time=21083.347..21083.521 rows=1095 loops=1)
         Sort Key: "集計"."ブランド", "集計"."売上" DESC
         Sort Method: quicksort  Memory: 134kB
         ->  Subquery Scan on "集計"  (cost=849559.20..879580.20 rows=1200840 width=44)
                                      (actual time=21075.482..21080.224 rows=1095 loops=1)
               ->  HashAggregate  (cost=849559.20..867571.80 rows=1200840 width=44)
                                  (actual time=21075.480..21080.024 rows=1095 loops=1)
                     Group Key: (tab1.sales_date)::date, tab1.shop
                     ->  Seq Scan on tab1  (cost=0.00..633087.75 rows=28862860 width=19)
                                         ★(actual time=0.064..7888.289 rows=28864000 loops=1)
 Planning time: 0.234 ms
 Execution time: 21097.712 ms
なんと、ソートが軽量に済むやいなや、パラレル動くことをやめちゃいました。なんてこった。ディスクソートしていたときのほうが圧倒的に早いので、このケースこそプランナが間違えてるとしか思えません。

しかもこれ、上記のJOINのケースのようにパラレルさせないという選択ではなく、パラレルして欲しい方向なので、これ以上PostgreSQLでは調整のやりようがないです。困った。


そんなときはEDB Postgres
ヒントを使って・・・・
EXPLAIN ANALYZE
SELECT /*+ PARALLEL(tab1 6) */ *,
    rank() OVER (PARTITION BY "ブランド" ORDER BY "売上" DESC) AS "順位"
FROM (
    SELECT
        sales_date::date as "売上日"
        ,shop            as "ブランド"
        ,sum(price)      as "売上"
    FROM  tab1
    GROUP BY "売上日","ブランド") AS "集計";
あれ。

悩むこと数十分。サブクエリの側にヒントを入れて、無事解決しました。
 /* EDBでヒントをいれてみる。正解はこの位置 */
EXPLAIN ANALYZE
SELECT *,
    rank() OVER (PARTITION BY "ブランド" ORDER BY "売上" DESC) AS "順位"
FROM (
    SELECT  /*+ PARALLEL(tab1 6) */
        sales_date::date as "売上日"
        ,shop            as "ブランド"
        ,sum(price)      as "売上"
    FROM  tab1
    GROUP BY "売上日","ブランド") AS "集計":
 /* EDBでヒントを使うと、パラレルスキャンし、メモリソートもできた */
 WindowAgg  (cost=1327297.76..1351662.32 rows=1218228 width=56) (actual time=9667.291..10095.048 rows=441334 loops=1)
   ->  Sort  (cost=1327297.76..1330343.33 rows=1218228 width=48) (actual time=9667.270..9746.242 rows=441334 loops=1)
         Sort Key: "集計"."ブランド", "集計"."売上" DESC
         Sort Method: quicksort  Memory: 46768kB
         ->  Subquery Scan on "集計"  (cost=1176747.00..1204157.13 rows=1218228 width=48) (actual time=8986.449..9216.731 rows=441334 loops=1)
               ->  Finalize HashAggregate  (cost=1176747.00..1191974.85 rows=1218228 width=48) (actual time=8986.447..9129.055 rows=441334 loops=1)
                     Group Key: tab1.sales_date, tab1.shop
                     ->  Gather  (cost=357488.67..1103653.32 rows=7309368 width=48) (actual time=3751.793..5775.296 rows=3081355 loops=1)
                           Workers Planned: 6
                           Workers Launched: 6
                           ->  Partial HashAggregate  (cost=356488.67..371716.52 rows=1218228 width=48) (actual time=3736.607..4506.717 rows=440194 loops=7)
                                 Group Key: tab1.sales_date, tab1.shop
                                 ->  Parallel Seq Scan on tab1  (cost=0.00..320408.67 rows=4810667 width=23) (actual time=0.041..870.314 rows=4123429 loops=7)
ぶっちゃけactual timeを見ると元々9秒だったのが10秒と、むしろ若干悪いのだが・・・ディスクソートを排除したいという欲望を満たすべく試したらこうなった、と言う話。たぶんデータ量とかでこの辺はセンシティブに最適解は変わってしまうのだと思います。

間違・・・わないケース3 グループ集計(GROUPING SETS)


結論から言うと、いろいろ頑張ったけど思ったより早くならないのでプランナが正しかった!説があります。以下のクエリを実行してみると、PostgreSQLで一向にパラレルになってくれません。

 /* グループ集計のクエリ */
EXPLAIN ANALYZE
SELECT
    shop            AS "ブランド"
    ,kind           AS "種類"
    ,count(*)       AS "数量"
FROM tab1
GROUP BY GROUPING SETS (("ブランド"),("種類"),());

 /* 結果のイメージ */
  ブランド  |    種類    |   数量
------------+------------+----------
 DIESEL     |            |  8448000
 Paul Smith |            | 15488000
 dior       |            |  4928000
            |            | 28864000
            | シャツ     | 15488000
            | ジャケット |  4224000
            | パンツ     |  9152000
 /* PostgreSQLでの実行計画 パラレルにならない */
 GroupAggregate  (cost=4137591.06..8147200.27 rows=14 width=26)
                 (actual time=27350.631..48641.465 rows=7 loops=1)
   Group Key: shop
   Group Key: ()
   Sort Key: kind
     Group Key: kind
   ->  Sort  (cost=4137591.06..4209751.06 rows=28864000 width=18)
             (actual time=22549.349..26069.469 rows=28864000 loops=1)
         Sort Key: shop
         Sort Method: quicksort  Memory: 2953433kB
         ->  Seq Scan on tab1  (cost=0.00..560942.00 rows=28864000 width=18)
                               (actual time=0.025..5461.728 rows=28864000 loops=1)
 Planning time: 0.160 ms
 Execution time: 49108.929 ms
EDBでヒントをいれてみます。
 GroupAggregate  (cost=6784457.73..10794066.94 rows=14 width=26)
                 (actual time=45959.909..70152.520 rows=7 loops=1)
   Group Key: shop
   Group Key: ()
   Sort Key: kind
     Group Key: kind
   ->  Sort  (cost=6784457.73..6856617.73 rows=28864000 width=18)
             (actual time=39595.620..44537.665 rows=28864000 loops=1)
         Sort Key: shop
         Sort Method: quicksort  Memory: 2953433kB
         ->  Gather  (cost=1000.00..3207808.67 rows=28864000 width=18)
                     (actual time=0.442..18630.270 rows=28864000 loops=1)
               Workers Planned: 6
               Workers Launched: 6
               ->  Parallel Seq Scan on tab1  (cost=0.00..320408.67 rows=4810667 width=18)
                                              (actual time=0.052..1111.439 rows=4123429 loops=7)
 Planning time: 0.130 ms
 Execution time: 70680.047 ms
パラレル度が上がったが早くならないケースもある。ということで。

■総評


パラレルクエリで効果を得づらいケースを3パターン紹介しました。

これはたまたま私が検証する中で気付いたもので、他にもあるかもしれないし、賢い解決策があるのかもしれません。
本投稿では触れていませんが、同じ検証の中で、1クエリ実行するのに50分かかっていた処理が、パラレルクエリとテーブル・パーティショニングの組合せで8秒まで短縮していますから、ハマれば効果は抜群です。
しかしながら、間違うケースもこうやってポンポン出てきたということは、これから採用を考えている人は、自身のアプリケーションでどの程度性能がでるか、どんな書き方をすれば効果がでるか、(そしてEDBにすればどう変わるか。ぐへへ!)是非試してみて欲しいと思います。
策をいろいろ考えるのはパズルみたいで楽しかったです。

明日はぬこ@横浜さんが書いてくれます。楽しみです!

2016年12月3日土曜日

【PGConf.asia】PostgreSQLの初期設定

PostgreSQLのインストールができて、initdbが終わると、PostgreSQLサーバーを起動してデータベースとして使い始めることができますが、当たり前にここまではやっておいたほうが良い、といういくつかの初期設定がありますので、ここではそれを紹介します。

  • パラメータ設定(postgresql.conf)
  • 認証設定(pg_hba.conf)
  • ユーザー作成
  • データベース作成
  • (必要に応じて)スキーマ作成

を扱います。

■パラメータ設定
PostgreSQLの動作設定は、すべてpostgresql.confというテキスト形式のパラメータファイルに記載されています。
その中でも、設定しておかないと使えないものがlisten_addressesです。
デフォルトは「loclhost」となっていて、外部からのアクセスを一切受け付けません。
この値を「*」または、自サーバーのIPアドレスを設定します。NICが二つ以上ある場合に「*」はすべてだし、IPを指定していれば、そのIPアドレスに対するアクセスが来た時だけ受け付けます。

 ※ちなみに、インストーラを使った場合はデフォルトからよしなに変更されているようです。

そのほかにも、現代のサーバースペックを考えるとごく小さいデフォルト値が採用されているメモリ関連の設定などがありますので、実用を考えると5個ぐらい見直したほうが良い値があるのですが、「PostgreSQL データベースチューニング」などで検索すると情報がたくさん公開されています。

■認証設定
PostgreSQLは、ユーザーからの接続要求を受け取ると、pg_hba.confというテキスト形式の認証設定に従って接続の許可・拒否を判断します。
このファイルは、接続元のホスト、接続先のデータベース/ユーザー、認証方法の組み合わせで書かれたリストになっており、上から順に評価されることに注意してください。

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
host    demo        kkida          192.168.200.0/24     md5



サービスからPstgreSQLサーバーを再起動して、ここまでの設定を反映します。

■ユーザー作成、データベース作成
PostgreSQLを操作するには、psqlというコマンドラインツール、またはpgAdminというGUIツールで対象のデータベースに接続します。

$ psql -U kkida -p 5432 -d demo

このとき、kkidaユーザーをまだ作成していないので当然接続に失敗します。demoデータベースもありません。
デフォルトでは、postgresユーザーとpostgresデータベースが作られているので、それを指定してアクセスし、自分用のユーザーやデータベースを作成します。(これも、インストーラーを使用した場合は、OSユーザーと同名でDBユーザーが作られている場合があります。)

$ psql -U postgres postgres
postgres=#

または、すでにユーザーがいる場合は、以下のように確認できます。セキュリティ的にゆるゆるな初期の今だからこそできることですが。
$ psql postgres (ユーザー名の入力を省略してログイン)
postgres=# \du
名前 |属性
------+---------------
kkida   |  スーパーユーザ


ここでCREATE ROLEやCREATE DATABASEといったSQLを実行し、任意のユーザー、データベースを作成します。

postgres=# CREATE ROLE kkida SUPERUSER LOGIN;
postgres=# \c postgres kkida
postgres=# CREATE DATABASE demo;
postgres=# \c demo kkida
demo=# \q

これで、初期設定は完了です。
$ psql -U kkida demo
demo=# CREATE TABLE demo (no int , val text);
demo=# INSERT INTO demo VALUES (1,'aaa');
demo=# SELECT * FROM demo;
no     | val
-----+----------
1      | aaa

demo=# \q


【PGConf.Asia】Windows版PostgreSQLのインストール

ここでは、PostgreSQLを初めてインストールする方向けに、Windows環境での手順を説明します。

■インストーラーの準備と対象バージョンについて


現時点の最新版、PostgreSQL 9.6.1を対象にします。
Windowsへの対応は、64bitのサーバーOS(最近だとWin 2008以降ぐらいだろうか?)とか、32bitのWindows 7,8とか10では実績があると思います。これといって細かいOS側に対する要件は無くて、PostgreSQLの開発コミュニティでは構築テストをして実績のあるものが公開されているような感じです。

ただし、商用版PostgreSQLであるEDB PostgresはサーバーOSのみに対応し、つまり現行の64bit機のみに対応するように、ここ数年で変わってきています。インストーラーが提供されているかどうかというレベルで、32bit環境では将来を気にしておくべきだと思います。

Windows版のインストーラーは、上記のEDB Postgresを作っていてPostgreSQL自体の機能・性能向上にも大きく貢献しているEnterpriseDB社が作成し、公開しています。ここから自分が該当するインストーラーをダウンロードしておきます。
また、日本PostgreSQLユーザ会のwebサイトでは、各OS向けのインストーラ、yum(rpm)やaptパッケージ、ソースコードへのリンクを記載しており、こちらから辿るのも簡単です。



■インストーラの実行


Windows機では、Administrator権限を持ったOSユーザでインストーラーを実行します。
このとき、UAC(ユーザーアクセス制御:Windowsのセキュリティ機能)は弱くしておかないと起動に失敗することがあります。(正確にどうしたらいいか、試していないので忘れた・・・まあ、失敗したら変更すればよいよね?)

インストーラーを実行すると、以下のように次々値を指定していくことになるのですが・・・



画面キャプチャを次々と載せるのでも良いのですが、各項の解説をしておくほうが大事だと思いますので、PostgreSQL用語満載ですが、ご容赦ください。勉強しましょう!

インストール先 (Installation Directory)

PostgreSQLをインストールするフォルダです。デフォルトはProgram Filesの下。この何階層か下にbinディレクトリがあり、そこにPostgreSQLのコマンドが配置されますので、後でbinに環境変数PATHを通しておくとよいでしょう。

データディレクトリ(Data Directory)

PostgreSQLのデータファイルおよび管理ファイルの配置先です。デフォルトはインストールしたフォルダ配下が使われますが、データ領域であるためディスク容量や性能の問題で専用のストレージ領域を指定することが多いです。

WALについて

これまでのバージョンでは、データディレクトリと併せてWALディレクトリ位置を聞かれていました。
WALはデータベースに対する変更の歴史を記録するファイルで、これをある時点の断面に次々適用することで最新状態まで戻す用途で保持されます。
WALがデータディレクトリと物理的に同じディスクに配置されていると、ディスク障害時の復旧に必要なWALまで失われており泣き寝入りするしかありません。というわけなのですが、最近のディスク領域はRAID構成になっていたりするのが普通なので入力を省いたのでしょうか。
WALの位置を変更するには、最後まで構築が済んだ後にごにょごにょします。(ここでは扱わない。)

ユーザーとパスワード

インストーラで入力を求められるのは、「postgres」というデーベース内に一人必ず作成される管理者ユーザーのパスワードです。
ただし、同名/同パスワードのOSユーザーを勝手につくろうとするので、OS側のセキュリティポリシー設定が厳しかったりすると、パスワードがポリシーに違反してる旨のメッセージが出てしまい、それはOS側の制約だったりするのでインストール中の作業者には心当たりがなく、ハマることがあります。

ポート番号

PostgreSQLがクライアントからの通信を待ち受けるポートを指定します。デフォルトは5432です。
あとからでも変更できるので影響は小さいです。既存のポートとバッティングしないようチェックが入りますので、警告がでなければ進めて問題ありません。

上位設定(Advanced Options)

ロケールはデフォルトではなく、「C」にすることを推奨します。
ロケールで地域を指定すると、その言語の並び順でソートされたりして、特にソート時のパフォーマンスでは大きな差が生じるようです。「C」でのソート順は、UTF-8など使用している格納文字コードのコード順になります。

全て入力して、「Next」を押すとインストールが開始されます。


■データベースクラスタの再作成(必要に応じて)


出来上がったデータベースは、環境によって格納文字コードがASCIIになっている場合があり、日本語を格納できません。その場合、日本語を格納するにはデータベースクラスタ作成時に指定するエンコードを「UTF-8」などにしなければなりません。
インストーラが内部で勝手に実行してくれたデータベースクラスタ作成時のオプション指定がイケてないんですね。というわけで再作成します。

データベースの停止

インストーラーが終了した時点で、データベースが起動していますので、不正なステータスのまま壊してしまわないよう停止しておきます。Windowsでは「サービス」からデータベースの起動・停止を操作します。

データディレクトリの削除

インストーラーで指定したデータディレクトリ配下には、データファイル、WALファイル、各種パラメータ設定ファイル、DBの状態を管理するファイルなどが一式格納されていて、でも再作成したいいまとなってはこれが邪魔な存在なのでフォルダごと一括で消します。

データディレクトリの初期化

同じ意味の言葉で、「initdb」「データベースクラスタの初期化」 などと表現されますが、削除したデータディレクトリをまるっと新たに作り直してくれるinitdbというコマンドがありますので、それを実行してデータディレクトリを作ります。この時に指定するオプションで、問題のエンコードを指定したり、WALファイルの位置を指定したりカスタマイズが効きます。

> initdb -D <データディレクトリ位置> -E UTF8 --no-locale

データベースの起動

手動でinitdbしたあとは、データベースの起動が必要です。
WindowsのサービスからPostgreSQLを探し、起動します。

この時、データディレクトリの位置が最初と変わっていると、サービススクリプトの作り替えも必要になりますが、応用なので今回はそのようなケースは扱いません。


これで、PostgreSQLのインストールは完了です。
テーブルを作ってデータを入れるには、もう少し作業があって、ユーザやデータベースの作成、認証のための設定が必要です。 以降の手順はOSプラットフォームやディストリビューションによらず、PostgreSQLの初期設定一般になりますので、別記事に整理します。

【PGConf.Asia】 チュートリアルセッション序文

今、ユーザーコミュニティたる我々が発信していくべきことは何かと考えると、初めの一歩を踏み出す人を後押しすることではないか。などと唐突に書いてみる。
この投稿は、2016年12月3日 PGConf.asiaチュートリアル枠について書いた非技術的な話です。

■これまでのPostgreSQL勉強会


過去数年、日本PostgreSQLユーザ会(JPUG)で開催した「初級者向け勉強会」は、それでもデータベース経験者というか実務で扱ってる人を対象にした、バックアップ入門、チューニング入門などなど、すでにデータベースに興味を持ってる人が知識を整理し、中上級者になるとか・実務を担当できるようになるという方向性であったように思う。
それはとても良いことで、ある話題の最低限を1コマに凝縮しモジュール化した講義内容は再利用できるし、そのテーマで調べた人がたどり着いて利用するのにもっともよい形になっていると思う。
それらは今でもスライド資料+録画が公開されていて、利用したい人の需要にこたえることができる一つの方法として価値あるものを提供できていると思う。
(コミュニティの場で発表することは、発表者にとっても、苦労に対してメリットが非常に大きく、知識を整理したことで自身がレベルアップできることはもちろん、そこでの経験は仕事の評価につながったり、転職活動に明確に有利になったりする。)

上級者向けは言わずもがな。中国地方DB勉強会とか、Database Loungeでは、新機能やデータベースの新たな領域での活用という話が繰り広げられている。特にこれまでにそのレベルに達している人(≒既にその製品・分野の勉強してきた人)にとってもう一歩先の話はとても興味深く、自慢の機能を作った開発者と、新しいことを得たい受講者の需要と供給が一致して半端ないエネルギーを生んでいるように見える。

■苦労を伴う初心者育成


良い意味で勝手に回っていく上記のような話とは違い、初めの一歩を踏み出す人に向けてスキルを伝えていくことは、双方に努力が必要な話である。どちらかが話を持ち掛けて、相手に努力を強いることになる。

  • 経験者が「○○は大事だから絶対勉強しとくべき」とか、「俺が教えてやるよ」なんて非常に上から目線すぎる話だけど、本当に大事な技術はそうでもして伝承していかないといけない。それにつきあって新たなことを学ぶ初心者の苦労は相当のものかもしれない。
  • 初心者が「わからないので教えてよ」って言うのは、有能で成果を上げてる経験者の時間を消費することで、若気の至りにしたって厚かましいかもしれない。そのために時間を割き、経験値を形に変えて教えてくれる経験者の損失は実は計り知れない。その上、わかりにくいと叩かれたりする。

苦労のデッドロックである。(上手いこと言った。←)


それでも、長い目でみたら製品の利用者が増え、知の総和が増え、製品クオリティの向上にもつながるし、各個人はそこでなければ得られなかった技術領域を習得し、個としてのバリューを発揮できるようになる。
”生み出したい価値” と ”育成に費やすコスト” を天秤にかけて、結果、技術力をものにできたところが最終的に勝つのだと思う。それをお金と上司命令で実現するのが企業だとしたら、我々コミュニティがやるべきことは何か。
お金と苦労の等価交換ではなく、自分がこれまで経験した楽しさを喜んで人に伝えるとか、コミュニティで得たものを還元するとか、そういう気持ちが原料になってデッドロック状態を解放することができるのがコミュニティなのだと思う。

■さて、自分にできることは


ということを意識しはじめた今年の6月、このブログをはじめた。(更新サボってるけど。それでも死んではいない。)自分がコミュニティの中の人として何ができるか、何を発信できるかを考えてきた結果の一つが今回のチュートリアルになった。
この文脈から、「教えてやるよ」と言っちゃってるようなものなんだけど、自分のコンプレックスでもあるプログラミング経験のなさとかは世の技術者と比べたら一目瞭然で、「出来ることは教える、出来ないことは教えてもらう」を体現できる存在として、自分が、JPUGが、誰かが一歩踏み出すお手伝いをするのだ!と決意をもって始めるのである。

っていうか、これまでのJPUGの構成員は何から何まで優秀すぎるっていうか。だからこそ学ばせていただいたことがめちゃくちゃあるんだけど。
このチュートリアルを実現できたことをきっかけに、イチ庶民として、やれることをやっていきたいと思います。

2016年11月27日日曜日

OSC2016 広島に参加しました

秋の広島遠征!
中国地方DB勉強会OSC広島に参加してきました。

本エントリは、11月27日(日)のOSC広島についてです。


■発表資料


PostgreSQLへの移行入門
商用データベースからPostgreSQLへ まず知っておいて欲しいまとめ


その他のことは後日追記します。

2016年11月26日土曜日

第18回 中国地方DB勉強会 に参加しました

秋の広島遠征!
中国地方DB勉強会OSC広島に参加してきました。

本エントリは、11月26日(土)の中国地方DB勉強会についてです。

■はじめに

私はPostgreSQLの人として、Schooさんで講座を持たせてもらった「PostgreSQLで学ぶデータベース技術」を元ネタにツギハギし、2コマしゃべらせてもらいました。(Schooさんでの講座は、Webアプリ開発者向けに、PostgreSQLを使う人に知ってほしいことを全5回にまとめたもので、今回全く触れていない運用管理の話とか、PostgreSQLの使いどころをより詳しく説明しています!)

で、今回のネタ(特に1コマ目)は、データベース初級者に向けて、データベースの勉強をするきっかけになればと思ってつくったもので、初級者の方には純真な気持ちで受け止めてもらい、中上級者には、もっとこうしたらわかりやすい、的なご意見をいただいて、より良いものにして"大切な概念を易しく伝える "講義として息の長いものにしていけたらなーと思っています。

■発表資料

1コマ目:アプリ開発とデータベース、アプリ開発とPostgreSQL

アプリ開発者(プログラムを書く人、SQLを書く人)が、データベースってこういうところが便利、PostgreSQLってこう使える、と理解し、データベースの大事さを理解し、もう一段階成長するためにデータベースの勉強してみよう!と思っていただけることを目指しています。
その中での重要な話題として、「リレーショナルデータベース」「トランザクション」に絞って易しく解説しています。



2コマ目:SQL開発入門 - SELECTとパフォーマンス

仕事でSQLを書くけど、普段からそればっかりというワケじゃないし、ぶっちゃけ得意とは言えないみたいな方へ。
SQLを書く上でのちょっとしたポイントと、中級者になるために索引の構造やJOINのことを知り、パフォーマンスを意識しましょう、という話をしました。


Chugokudb18_2 from Kosuke Kida
 

だからといって、じゃあどうすればパフォーマンス良くなるの?と言われると、実データで実行例なんかを出せなかったのでイメージがしにくいかと思いますが、JPUGでこれまでに開催してきたパフォーマンスチューニング系の勉強会資料なんかを見て行く上で、大前提となるような話ですので、ここからより詳しい話を肉付けしていくのにお役立ていただければなと思っています。

2016年9月21日水曜日

【schoo連動】PostgreSQLらしい集計クエリ

schooのオンライン授業でPostgreSQLの話をすることになりました。

PostgreSQLで学ぶデータベース技術という全五回の授業で、9/25(日)と9/28(水)に生放送されます。(有料会員の方はアーカイブも見れるそうなので、そちらでも。)


■本記事について

授業の第3回では、「SQL開発入門 -PostgreSQLを選ぶ理由-」として、SQLの中でもPostgreSQLならではの便利なところ、拡張が豊富でアプリ開発をより高度に行えるところ、運用管理性の向上や、SQL開発のレベルでは無く、データ連携のようなDBサーバーとしての機能まで拡張できるところを取り上げました。
その中には、PostgreSQLらしいクエリがたくさん登場し、SQLの実習講座ではないが、試せるものなら是非ためして欲しいものがたくさん登場しますので、ここに実行例を細かく書くことにします。

ここでは、テーブルを作ってデータを入れて集計してみるという一連の流れを紹介します。

■データ生成まで

・テーブルの作成
授業のデモ用なので、正規化してなかったり構造はイケてないのですが。

メインのテーブル。
アパレルの店舗(=ここではブランド名) いくらのどの商品が売れたかを格納してるテーブルだと思ってください。

テーブル作成
CREATE TABLE sales_result (
   sales_id     int,
   sales_date   timestamp,
   shop         text,
   kind         text,
   price        numeric,
   color        int);

外部キー制約のために関連する表も作成しておきますが、JOINの練習ではないので、この表は以降は登場しません。

関連オブジェクト作成
CREATE TABLE color (color_code int,color_name text);
INSERT INTO color VALUES (1,'BLACK'),(2,'RED'),(3,'WHITE');
ALTER TABLE color ADD PRIMARY KEY (color_code);
・制約の定義
PostgreSQLでは、入力されるデータを正しく保つ制約が作れます。

制約の定義
ALTER TABLE sales_result ADD PRIMARY KEY (sales_id);
ALTER TABLE sales_result ADD CHECK (shop in ('dior','Paul Smith','DIESEL'));
ALTER TABLE sales_result ADD FOREIGN KEY (color) REFERENCES color(color_code);


テーブル定義を確認するとこんな感じです。
demo=# \d sales_result
           テーブル "public.sales_result"
     列     |             型              |  修飾語
------------+-----------------------------+----------
 sales_id   | integer                     | not null
 sales_date | timestamp without time zone |
 shop       | text                        |
 kind       | text                        |
 item       | numeric                     |
 color      | integer                     |
インデックス:
    "sales_result_pkey" PRIMARY KEY, btree (sales_id)
検査制約:
    "sales_result_shop_check" CHECK (shop = ANY (ARRAY['dior'::text, 'Paul Smith'::text, 'DIESEL'::text]))
外部キー制約:
    "sales_result_color_fkey" FOREIGN KEY (color) REFERENCES color(color_code)

・データの投入
バルクインサートと、ON CONFLICTを含めたINSERTの例です。このようなデータを一括で投入します。
demo=# INSERT INTO sales_result VALUES
demo-#    (5280,2016-07-11,'DIESEL'    ,'パンツ'    ,22000,2)
demo-#   ,(6368,2016-02-15,'DIESEL'    ,'シャツ'    ,21000,1)
demo-#   ,(5018,2016-01-27,'Paul Smith','シャツ'    ,13000,3)
demo-#   ,(7022,2015-09-29,'dior'      ,'ジャケット',22000,2)
demo-# ON CONFLICT DO NOTHING
demo-# ;
なのですが、この後のクエリではある程度の行数を集計したいので、INSERTの行数を増やして、かつ手動で30回ほど実行しておきます。
数値や日付はランダムな値を入れるようになっていますので、そのまま30回繰り返しコピペで良いです。それぐらいならすぐ終わります。(とは言え、手作業でいくスタイルです。がんばってくださいw 一応言い訳しておくと、今回はスクリプト化とかするより、今やってることを体験するコーナーです。)

データ投入
INSERT INTO sales_result VALUES
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','ジャケット',10000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','ジャケット',10000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','パンツ'    ,10000+2001+(random()*100)::int%3,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','パンツ'    ,10000+2001+(random()*100)::int%3,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'Paul Smith','シャツ'    ,10000+3001+(random()*100)::int%8,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'ジャケット',20000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'パンツ'    ,20000+2001+(random()*100)::int%5,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'DIESEL'    ,'シャツ'    ,20000+3001+(random()*100)::int%3,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'ジャケット',30000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'ジャケット',30000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'ジャケット',30000+1001+(random()*100)::int%2,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'パンツ'    ,30000+2001+(random()*100)::int%3,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'シャツ'    ,30000+3001+(random()*100)::int%1,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'シャツ'    ,30000+3001+(random()*100)::int%1,(random()*100)::int%3+1),
((random()*10000)::int,(now()-(random()*1000)::int%365*interval'1day'),'dior'      ,'シャツ'    ,30000+3001+(random()*100)::int%1,(random()*100)::int%3+1)
ON CONFLICT DO NOTHING
;

お疲れ様でした★

■クエリを実行する

・普通のSELECT文
まずは、INSERTした件数と、どんなデータが入ったか上位5件とか、テーブルの物理サイズを見ておきましょう。
demo=# SELECT count(*) FROM sales_result;
 count
-------
  1159
(1 行)

demo=# SELECT * FROM sales_result LIMIT 5;
 sales_id |         sales_date         |    shop    |    kind    | price | color
----------+----------------------------+------------+------------+-------+-------
     9396 | 2015-09-26 16:23:57.248001 | Paul Smith | ジャケット | 11001 |     3
     5179 | 2016-05-13 16:23:57.248001 | Paul Smith | ジャケット | 11002 |     3
     3925 | 2016-01-24 16:23:57.248001 | Paul Smith | パンツ     | 12003 |     1
     6720 | 2016-01-17 16:23:57.248001 | Paul Smith | パンツ     | 12001 |     2
     9054 | 2016-01-29 16:23:57.248001 | Paul Smith | シャツ     | 13006 |     3
(5 行)

demo=# SELECT pg_size_pretty(pg_relation_size('sales_result'));
 pg_size_pretty
----------------
 88 kB
(1 行)

・集計してみる
さて集計編。まずは基本のGROUP BYです。2種類試します。
集計する項目を「売上」か「件数」かで見比べます。単価が高いものがぽつぽつとでも売れたほうが、売上額では勝るんですね。世の中って複雑ですw

売上で集計するクエリ
SELECT
    sales_date::date as "売上日"
   ,shop             as "ブランド"
   ,sum(price)       as "売上"
FROM sales_result
GROUP BY "売上日","ブランド"
ORDER BY "売上" DESC
LIMIT 10;


販売点数で集計するクエリ
SELECT
    sales_date::date as "売上日"
   ,shop             as "ブランド"
   ,count(*)         as "件数"
FROM sales_result
GROUP BY "売上日","ブランド"
ORDER BY "件数" DESC
LIMIT 10;

demo=# SELECT
demo-#     sales_date::date as "売上日"
demo-#    ,shop             as "ブランド"
demo-#    ,sum(price)       as "売上"   --★ここだけ異なる
demo-# FROM sales_result
demo-# GROUP BY "売上日","ブランド"
demo-# ORDER BY "売上" DESC
demo-# LIMIT 10;
   売上日   |  ブランド  |  売上
------------+------------+--------
 2016-03-12 | dior       | 129007
 2016-09-08 | dior       |  99003
 2015-10-12 | dior       |  97003
 2016-04-21 | DIESEL     |  89015
 2016-03-03 | DIESEL     |  88018
 2015-10-22 | DIESEL     |  88011
 2016-09-02 | DIESEL     |  87009
 2016-07-16 | Paul Smith |  78029
 2016-06-20 | DIESEL     |  67011
 2016-06-21 | DIESEL     |  67009
(10 行)

demo=# SELECT
demo-#     sales_date::date as "売上日"
demo-#    ,shop             as "ブランド"
demo-#    ,count(*)         as "件数"   --★ここだけ異なる
demo-# FROM sales_result
demo-# GROUP BY "売上日","ブランド"
demo-# ORDER BY "件数" DESC
demo-# LIMIT 10;
   売上日   |  ブランド  | 件数
------------+------------+------
 2016-07-16 | Paul Smith |    6
 2016-06-14 | Paul Smith |    5
 2015-12-28 | Paul Smith |    5
 2016-02-09 | Paul Smith |    5
 2016-06-05 | Paul Smith |    5
 2016-09-21 | Paul Smith |    5
 2016-06-16 | Paul Smith |    5
 2016-04-07 | Paul Smith |    5
 2016-07-31 | Paul Smith |    5
 2016-09-15 | Paul Smith |    5
(10 行)

・ランキング(window関数)
次はいきなりPostgreSQL力の高い例題ですが、window関数を試します。
上記の集計結果にランキングをつけます。 あるブランドでは、何月何日が一番売れたかが見れます。結果はめちゃくちゃ長いので頑張ってスクロールしましょうw

ランキングを取得するクエリ
SELECT *,
    rank() OVER (PARTITION BY "ブランド" ORDER BY "売上" DESC) AS "順位"
FROM (
    SELECT
        sales_date::date as "売上日"
        ,shop            as "ブランド"
        ,sum(price)      as "売上"
    FROM  sales_result
    GROUP BY "売上日","ブランド") AS "集計"
;
demo=# SELECT *,
demo-#     rank() OVER (PARTITION BY "ブランド" ORDER BY "売上" DESC) AS "順位"
demo-# FROM (
demo(#     SELECT
demo(#         sales_date::date as "売上日"
demo(#         ,shop            as "ブランド"
demo(#         ,sum(price)      as "売上"
demo(#     FROM  sales_result
demo(#     GROUP BY "売上日","ブランド") AS "集計"
demo-# ;
   売上日   |  ブランド  |  売上  | 順位
------------+------------+--------+------
 2016-04-21 | DIESEL     |  89015 |    1
 2016-03-03 | DIESEL     |  88018 |    2
 2015-10-22 | DIESEL     |  88011 |    3
 2016-09-02 | DIESEL     |  87009 |    4
 2016-06-20 | DIESEL     |  67011 |    5
 2016-06-21 | DIESEL     |  67009 |    6
       :
 (結果は省略)

・ピボットテーブル(GROUPING集計)
もうちょっと簡単な集計を例にします。どのブランドのどの服が何個売れたかを出します。
しかし、RDBの結果はいかんせん見づらい。アプリでステキに表示するために、よく行列変換を施します。
demo=# SELECT
demo-#     shop            AS "ブランド"
demo-#     ,kind           AS "種類"
demo-#     ,count(*)       AS "数量"
demo-# FROM sales_result
demo-# GROUP BY "ブランド","種類";
  ブランド  |    種類    | 数量
------------+------------+------
 Paul Smith | シャツ     |  538
 dior       | シャツ     |   86
 DIESEL     | シャツ     |   30
 dior       | ジャケット |   89
 dior       | パンツ     |   30
 DIESEL     | ジャケット |   29
 DIESEL     | パンツ     |  299
 Paul Smith | ジャケット |   61
 Paul Smith | パンツ     |   61
(9 行)

この結果をまるっとExcelに張り付け、「ピボット変換」機能を使うと、各中計と合計を出して見やすくしてくれます。



PostgreSQLでは、この中計や合計の値を一回のクエリで取ることができます。従来のGORUP BYでは、合計で一回、ブランド別の集計で一回、種類別の集計で一回のクエリを発行する必要がありました。最新のPostgreSQLでは、GROUPING SETS、ROLLUP、CUBEといくつか構文があり、取得したい集計によって使い分けます。その一番基本となるGROUPING SETSを今回は例にしています。ちなみに、CUBEやROLLUPを使うと、中計、合計だけでなく、普通にGROUP BYした結果も一緒に取れるので、Excelのピボット変換したのと同じ結果が1クエリで取れます。

中計と合計を取得するクエリ
SELECT
    shop            AS "ブランド"
    ,kind           AS "種類"
    ,count(*)       AS "数量"
FROM sales_result
GROUP BY GROUPING SETS (("ブランド"),("種類"),());

結果は以下のようになります。上記のEXCELのピボット変換して得た値(中計や合計)がSQLで取れています。(例えば「合計」の1223という値がちゃんと取れています。)

demo=# SELECT
demo-#     shop            AS "ブランド"
demo-#     ,kind           AS "種類"
demo-#     ,count(*)       AS "数量"
demo-# FROM sales_result
demo-# GROUP BY GROUPING SETS (("ブランド"),("種類"),());
  ブランド  |    種類    | 数量
------------+------------+------
 DIESEL     |            |  358
 Paul Smith |            |  660
 dior       |            |  205
            |            | 1223 --★この合計はExcelで計算されたものと一致
            | シャツ     |  654
            | ジャケット |  179
            | パンツ     |  390
(7 行)

・行列の表示変換(crosstab)
なお、これはオマケなのですが、これをさらに見やすく整形し、本当にピボット変換したのと同じ表をつくる整形用の拡張もあります。

拡張を入手できるかはインストールした環境次第なので、ここではそのまま実行例を貼っておきます。EXCELの例とおなじ結果SQLを整形だけしたものです。(この時点で中計や合計は計算していない)
postgres=# CREATE EXTENSION tablefunc;
CREATE EXTENSION

demo=# SELECT * FROM crosstab(
demo(#   'SELECT
demo'#        shop
demo'#       ,kind
demo'#       ,count(*)
demo'#    FROM sales_result
demo'#    GROUP BY 1,2
demo'#    ORDER BY 1,2'
demo(#  ,
demo(#   'SELECT
demo'#        DISTINCT kind FROM sales_result
demo'#    ORDER BY kind'
demo(# )
demo-# AS results(
demo(#           "ブランド"    text,
demo(#           "シャツ"      numeric,
demo(#           "ジャケット"  numeric,
demo(#           "パンツ"      numeric
demo(# );
  ブランド  | シャツ | ジャケット | パンツ
------------+--------+------------+--------
 DIESEL     |     30 |         29 |    299
 Paul Smith |    538 |         61 |     61
 dior       |     86 |         89 |     30
(3 行)

すげー複雑!と思った方が多いと思いますが、SQLが複雑ということは、その裏でデータベースはもっと頑張って、取得する行数の見積もり、実行計画の策定、スキャン、集計、などをこなしているのです。複雑なSQLを書けるということは、賢いデーターベースであることの証でもあります。

PostgreSQL 9.6では、\crosstabviewという整形用メタコマンドが追加されました。直前に実行されたSQLを整形してくれます。
メタコマンドなのでpsql大好きっ子的には嬉しいこの機能。一般ウケはし無さそう。
schoo=#
SELECT
    shop            AS "ブランド"
    ,kind           AS "種類"
    ,count(*)       AS "数量"
FROM sales_result
GROUP BY CUBE( "ブランド", "種類");
  ブランド  |    種類    |  数量
------------+------------+--------
 DIESEL     | シャツ     |   9856
 DIESEL     | ジャケット |   9856
 DIESEL     | パンツ     |  93568
 DIESEL     |            | 113280
 Paul Smith | シャツ     | 180352
 Paul Smith | ジャケット |  20096
 Paul Smith | パンツ     |  20096
 Paul Smith |            | 220544
 dior       | シャツ     |  29696
 dior       | ジャケット |  30592
 dior       | パンツ     |  10496
 dior       |            |  70784
            |            | 404608
            | シャツ     | 219904
            | ジャケット |  60544
            | パンツ     | 124160
(16 行)

schoo=# \crosstabview "ブランド"
  ブランド  | シャツ | ジャケット | パンツ |
------------+--------+------------+--------+--------
 DIESEL     |   9856 |       9856 |  93568 | 113280
 Paul Smith | 180352 |      20096 |  20096 | 220544
 dior       |  29696 |      30592 |  10496 |  70784
            | 219904 |      60544 | 124160 | 404608
(4 行)

あと、こういういかにも重いクエリが、パラレルクエリでチョッパヤになります。夢が膨らむ!
schoo=# explain analyze
SELECT /*+ PARALLEL(sales_result 8) */
    shop            AS "ブランド"
    ,kind           AS "種類"
    ,count(*)       AS "数量"
FROM sales_result
GROUP BY CUBE( "ブランド", "種類");
                                                                  QUERY PLAN

------------------------------------------------------------------------------------------------------------------------------------
----------
 GroupAggregate  (cost=83465.04..128227.47 rows=32 width=26) (actual time=447.751..883.691 rows=16 loops=1)
   Group Key: shop, kind
   Group Key: shop
   Group Key: ()
   Sort Key: kind
     Group Key: kind
   ->  Sort  (cost=83465.04..84476.56 rows=404608 width=18) (actual time=441.059..503.051 rows=404608 loops=1)
         Sort Key: shop, kind
         Sort Method: quicksort  Memory: 42643kB
         ->  Gather  (cost=1000.00..45783.56 rows=404608 width=18) (actual time=1.582..192.417 rows=404608 loops=1)
               Workers Planned: 8
               Workers Launched: 7
               ->  Parallel Seq Scan on sales_result  (cost=0.00..4322.76 rows=50576 width=18) (actual time=0.017..11.969 rows=50576
 loops=8)
 Planning time: 0.245 ms
 Execution time: 891.296 ms
(15 行)

ということで、単にSQLをぺたぺた貼って行っただけですが、このぐらいで。

【schoo連動】PostgreSQLで学ぶデータベース技術

※ 本記事はポエムかつ炎上するような物でも無いのでまったく読む必要ないやつです ※


schooのオンライン授業でPostgreSQLの話をすることになりました。

PostgreSQLで学ぶデータベース技術という全五回の授業で、9/25(日)と9/28(水)に生放送されます。(有料会員の方はアーカイブも見れるそうなので、そちらでも。)

■データベース技術とは

タイトルに使った「データベース技術」という言葉について。

授業の内容を考えながら、SQLの解説では言葉だけの説明では限界があるし、全体で10話したとしても、PostgreSQLならではのことって3以下ぐらいにしかならないよなーとか思っていました。schooさんではこの夏、数回にわたりSQLやデータベース理論に関する授業が公開されていたそうなので、PostgreSQLに絞っていっても相当に重複するであろうという懸念もありました。
それでも、SQLは学んだはずなのに、(むしろ普段から書いてるのに)得意とは言えない、思うようにパフォーマンスがでない、という相談は私も日々いただいているのです。特に、schooさんで対象としている学生の多くは「webアプリ開発者を目指す(または駆け出し)」とか「社会人数年目」だったりするので、ここにまだ学びの余地があると考えました。
データベースを使う人の立場で、
  • 最初にこれを勉強しておけば苦労が減るだろうな
  • 何かあったときにググるために、脳内のインデックスを作れてるといいのにな
というゴールを設定して、広く浅く易しく、「RDBのことを勉強した気になれる」講座にしたいと思いました。こういうテーマでは、RDBが本来満たすべき領域を質実剛健に、しっかりとカバーしているPostgreSQLは最適です。

つまり、「RDBが本来満たすべき領域」のことをひっくるめて「データベース技術」としました。


■授業をつくってみて

「データベース技術」の中には、トランザクションのこともあるし、SQL言語のこともあるし、セキュリティのこと、バックアップのこと、いろいろあるでしょう。そしてそれらは、きちんと実装されているPostgreSQLにとっては当たり前のことだし、おそらく意識しなくてもある程度使うことはできてしまっていることでもあります。これらに光をあて、でも、難しさよりも、勉強した感を大事に、易しく扱う内容を目指しました。

さて、そんな感じで準備をしてきたのですが、 PostgreSQLを使う人(=アプリ開発者)にとって、RDBを知っているとどんなところが有利なのか、PostgreSQLならではのどういうところが便利なのか、そういう点をちゃんと紹介するのは、私にとっても普段から(DB管理者よりで、移行、障害対応やDBA教育をやっている)身近に扱う話題ではないので、とても勉強になりました。

作っていると、 細かい言葉の意味だったり、他のDBのことが気になってきたり、図の細かいところが気になったりw それでいて、説明に最適な実行例を考えるのはめんどくさくなってみたり。いろいろ雑念が生まれてくるのですが、そういうのもだんだん楽しくなってきて、最終的には手抜きしない内容にできたと思います。
仕事ではOpen Office、Libre Office慣れして数年やってきたのですが、Power Point on Windows10 対応(自分がw)したことも良かった点です。

■と、いうわけで。

そんなシルバーウィークを過ごして、今は資料を提出した喜びでこんなポエムを詠っているわけですが、せっかく作ったのでいろんな人に見てもらいたいし、ツッコミどころがあったら指摘してもらってより良くしていきたいし、良くなったものを今後もどこかで話したりして、PostgreSQLの普及、製品問わずRDBスキルの向上、Webアプリだけじゃなくていろんなシステムへの応用など、いろんなところに少しでもお役立ちできたらなーと思っています。

それでは、本番まであと数日、私も楽しみにしています!

2016年9月10日土曜日

第8回 PostgreSQLアンカンファレンスに参加しました

PostgreSQLアンカンファレンスに参加してきました。
参加っていうか運営?これに関しては参加者気分もとてもありつつ、運営も楽しくやらせていただいているのです。何より日程が会場優先で、俺得になっていてもう幸せ☆


※ところでこのゾウの看板を連日つくっていたら、急激に可愛く見えてきたんですがどっかの病気でしょうか?目つきが悪いとか良く言われているが、前から肯定的な気持ちでは見ていました。でもこんな気持ちになるなんて。


■PostgreSQLアンカンファレンスについて


PostgreSQLアンカンファレンスは、何が出るかは当日来てのお楽しみで、いろんな興味の範囲をそれぞれに持った人たちがPostgreSQLという共通点に導かれて集まり(大げさ?)、発表し、聞きあう場所です。
「PostgreSQLについてしゃべりたいことがある、聞いてほしいことがある!」
「PostgreSQLについて聞いてみたい、相談したい!」
「PostgreSQLに興味があるのでとりあえず参加してみたい」
そんな、色んな人たちを受け入れてくれるイベントです。
今回はそのPostgreSQLアンカンファレンスの第8回でした。
毎年秋はPostgreSQLの新バージョンがリリース目前で期待が高まっている時期であり、更に次のバージョンの機能デザインが決まり始めていたりで、かつもう少し後ろになるとITイベント目白押しになってしまうので、だいたいこの時期にやっている気がします。

1~2月

次バージョンで取り込む機能を決める数回のCommit Festを経て、PostgreSQL本体開発しているみなさまが燃えている時期。一番ホットな話が聞けたり

5~7月

新バージョンのα?βがでるか?ぐらいの時期で、つまりあらかた固まってきた新バージョンをいち早く試した人たちの発表が聞ける。仕事にしている身としてはとても勉強になる回

9~10月

今回のように、新バージョンのリリースが迫ってきて、そろそろユーザー目線の「新機能はこうやって使うといいよ」みたいな話が出てきたり。

あとは一年を通して、実際にサービスで使っている方がシステム開発/運用/アップグレード/性能などの話題で経験談を話してくれたりしています。


■今日のセッション


どーん!



・PostgreSQLはグラフデータベースの夢を見るか(ぬこ@横浜さん)
 Neo4jをPostgreSQLから触る話。更新はロジデコ→JSON→Cypher→RESTで。参照はNeo4j_fdwで。

・俺のキュー(っぽいもの) (@aoyagikouheiさん)
PostgreSQL以外のミドルをいれてメンテしたくないので、キューっぽいデータ構造を自作した話。

・ポスグレとスクワットと私(永安さん)
話題の健康法「30日スクワットチャレンジ」をPythonとかBlumixとかSoftlayerとかPostgreSQLでWebシステム化している話。そして懇親会2次会ではそこに秘められたアツい想い、黒い想いをたくさん聞けたのであった笑

・PostgreSQLを用いた動画検索エンジンMoovieおよびMoovie TEOのシステム概要(内山さん)
※情報求む

・PostgreSQLのエコシステム/Windowsでのチューニング/メモリ暴発 防止のヒント(綱川さん)
諸事情あってmaumau名義で活動していたが表にでれる事になったとの自己紹介。開発者としてもだけど、自分的にはMLの救世主maumauさんにお目にかかれて感無量です。
Windows環境では無意味な"update_process_title"パラメータをoffると性能がとても良くなる。
ODBCドライバで全件フェッチする問題があるのでドライバの設定を見直すと良い。
PostgreSQLが対応できるソフトウェアを調査し開発本家のMLに掲載している(→JPUGでも是非その活動を後押ししていきたい!と申し出たところ快く受け入れていただいた!)

・自動フェイルオーバー(澤田さん)
PostgreSQL自身はレプリ構成にしても相手ノードの監視やフェイルオーバーの判断をしないが、それを作っている話?
※情報求む

・CacooのPostgreSQLを9.3から9.5にアップグレードした話
Webサービスで使っている3TBのデータベースクラスタを9.3から9.5にアップグレードした話。停止時間を短く、失敗時のリスクを軽くするオペレーションをちゃんと考えつつ、PostgreSQL的な手順に関して言えばpg_upgradeのマニュアル通りで上手く行きました!という報告。
※情報求む

・PostgreSQLと他のDB(MySQL、Oracle)との違い※非技術的 (北山さん)
 いくつかのコミュニティに参加してきて、とか、PGEConsの活動について、など思うトコロをいろいろ。(懇親会でいろいろ話しすぎてどこまでがセッションの内容だったか忘れてしまったっっ・・不覚)

・pg_stats_reporterで読影会をやってみた(@masudakzさん)
※情報求む

・とあるサービスでのパフォーマンス障害とその対策を赤裸々に報告(桑田さん)
※情報求む

・9.6パラレルやってみたよ(喜田)
集計系クエリ(DWHや夜間バッチ)で効果を期待される9.6の新機能「パラレルクエリ」を実際に近いベンチマークで試してみました、と言う話。
PostgreSQLが昔から注目されるイチ要素であった「テーブル・パーティショニング」との組み合わせで、元々10分かかっていたクエリが7秒に、1時間かかっていたクエリが5分になった。やっぱOracleすごいね、という要素も多々見つかったが、使い方をそれなりにPostgreSQL向けに工夫すると同等の結果まで頑張れる!

・私のストレージ チョットカシコイ(海外さん)
 ※情報求む

・PostgreSQLでデータ分析 10の理由(永安さん)
ユーザーの目線を考えると「データ分析したい!今ならどの技術が適してるんだろう?」という動機で言語なり製品を選ぶのであって、目的に合う事がわかればどのDBでもいいのだが、いろんな要素から今ならPostgreSQLを推すよ、と言う話。
パラレルできるようになって分析クエリが早いだけじゃなくて、FDWでデータを取ってくるところができるとか、「データ分析」に必要とされるいろんな要素を満たせる。

・FDW使える?(山田さん)
 ※情報求む

・PostGIS Bundleで循環セールスマン問題(TSP)(国府田さん)
※情報求む

・SSDハック(海外さん)
※情報求む

あれ、最後の方なんだか聞けてないな。。。
それぞれ見つかり次第、資料をリンクしていくつもり。


■懇親会


上海ブギにて。ブギ様のマーボー丼はうますぎる。
PostgreSQL界の人はtwitterしない問題
若者をとりこむ策(若者とは?)
今日のアンカンファレンスを思い出しながら、次の勉強会のネタ考案とか

二次会は魚八にて。
永安さんのスクワットアプリ開発秘話や、シンガポールでのイベント開催秘話をブラック永安が出てきていろいろ語っていただいた。がんばりたいと思った。

2016年9月9日金曜日

Database Lounge Tokyo #2 に参加しました

Database Lounge Tokyo #2 に参加しました。
今回はバックアップ回で、きっとメインのパネルディスカッションは壮絶な感じなんだろうなーと誰もが予感していたと思いますが、ネタとしては基本中の基本の範囲でカバーしておくべきでもあり、データベースに関わる人すべてが(少なくとも自分が触っているデータベースに関して)きちんと理解しているべき内容です。
というわけで、弊社の新人ちゃんも誘って参加してみました。

イベントの流れは、Lighting Talksがババッとあって、その後、バックアップ・リカバリ観点でのOracle DB、PostgreSQL、MySQLの比較の話と、それを肴にパネルディスカッション。
イベントページで録画や発表資料が公開されると思うので、現時点では記憶を頼りにザザっと書いています。ゆるっと追記・修正していく予定。

■Lighting Talks


・ 「差分、増分、デルタ(って何?) バックアップあれやこれや」 by @meijik


firebirdやMySQLでのバックアップ手法について。
特に差分や増分というデータベース製品によって意味するところが異なるキーワードを解説されていました。今日の一発目として、基本のおさらいだったり、知らない人的には「ほー」っと言う感じ。アーカイブ(トランザクションログ)を適用する話がそもそも増分と呼ばれていたり、文化の違いを感じる。

・「Postgresはグラフデータベースの夢を見るか?」 by @nuko_yokohama


グラフデータベース neo4j を PostgreSQL のインターフェース で扱っちゃう話。
PostgreSQLではロジカルデコーディングという、論理WALを吐くことができるので、PostgreSQLに対して実行したクエリを別のデータベースに伝えて実行させることが可能である。それを受信する側の機構を自作する必要があるが。今回は論理WALをjson型に変換→Cypherクエリ→REST APIでneo4jに突っ込む。
データを参照する方はneo4j_fdwで。PostgreSQLはForeign Data Wrapperという、外部データソースからデータを取ってくる(実装によっては更新も可)基盤があるが、データソース、今回で言うとNeo4jに対応したものを作らないといけない。と思ったら、以前にぬこさん自身が作ったものが公式Wikiにまで掲載されいている!

・「An intelligent storage?」 by @kkaiga


実は運営などバタバタしていて全然聞けなかった。。。
GPUの超メニーコアを使った高速化をやり続けている海外さんの取り組み。フラッシュ上に配置したデータを、CPUがちょろちょろGPUに渡しているとCPUがボトルネックになってしまうので、直接渡せると嬉しいよね!という話(だったと思う。資料が公開されたらちゃんと読む。)

・「ポスグレのバックアップでやらかした話 (˚இ˚) 」 by @kkkida_twtr


僭越ながら発表させていただきました。弊社では、取り扱う製品を自分たちで使ってみて、酸いも甘いも経験して、ちゃんとノウハウを得てからサービス提供してるんですよ!という美しいお話でした。(ウソ)
バックアップ取得先の容量管理とか、シェルのロジックとかの問題で、消し込みがうまく回らずPostgreSQLのアーカイブ領域が埋まる→新規のWALが書けずにDB停止、というのを実際にやっちゃうと運用担当者(俺)がパニくるんですよ。とか。VACUUM FULLの後にVACUUMやってるから意味無くね?って言ってコメントアウトしてみたらANALYZE止めちゃって本来5分のクエリが2時間かかったとか。そんな情けない話でも共有してみんなを救いたい!っていう健気な俺ちゃんのゆるふわな発表でした。

■メインテーマ


・ 「バックアップと障害復旧から考えるOracle Database, MySQL, PostgreSQLの違い」 @wrcsus4


実はお仕事などバタバタしていてあんまり聞けなかった。。。
各データベースのオンラインバンクアップ(アーカイブ運用とか、ポスグレ界でいうところのPITR前提の取り方)について比較し、酔った勢いでMySQLを変わり者と評する感じの流れw。と思いきや、早々にマサカリが飛びかっていることをtwitterで知りあわてて会場に戻りましたw
途中をいろいろ端折って抱いたイメージは、MySQLはOra、Posの当たり前が通用しないけど、論理バックアップにログを適用して最新に追いつけるよ?的な?それはそれで便利そうではある。(取る人は楽ちん、戻す人は大変そうだけど。)
これも資料公開されたらちゃんと勉強する。と思ったけど↑は本題ではないようだwマサカリおそろしいwww

・ディスカッション@wrcsus4 @yoku0825 @kasa_zip @kumagi


テーマが5個ぐらいあったけどメモれず。こういう用途でどんな方法でバックアップ取るか、どう戻すか、みたいなパターンがいくつか。
印象的だったのは、MySQLはスレーブから戻すケースが多くて、「死んだノードで整合性を保った状態で復旧せよ」みたいな問題を考えるほうが珍しいということ。
あとは、PostgeSQLで長年疑問に思っていた、「トランザクション位置を指定したPITRって需要どんだけあるんだろうか?」に対して「普通xid記録してないよね。」ということが分かった。収穫。

全体的に、新たに知ったことや、へーっと思ったことは記憶に残ってるのでMySQLのメモが多いが、PostgreSQLやOracleについてももちろんたくさん話がでていました。総じて両者は似ている。フラッシュバック憧れる。(どれだけ使うかは別として。)
そして@kumagiさんの容赦ない切り口。いろんな経験の人がしゃべってるの面白いなーと思いました。


■その他

今回は、弊社で会場提供して、その活動報告を弊社の広報ブログで取り上げる予定です。
そのために皆さんにも写真撮影にご協力いただきました。きっとキャッキャウフフした感じが撮れていると思いますので、記事の公開を楽しみにしておいてください。

次のテーマは「トランザクション」とのこと。
学術的な方面の人たちがフィーバーしてそうで今から楽しみです。

2016年8月10日水曜日

みんなのPython勉強会#15 に参加しました

みんなのPython勉強会#15に参加しました。

Pythonを書いたのは昨日が初めてですが。。。いろいろ教わりたい立場でありながら、お役に立てることもあろうと思って、のっけからLTなんかもさせていただきました。


自分なりの今のワクワクした気持ち、疑問、Pythonという言語にもっているイメージ、そして期待をふわっと詰め込んだ感じです。PostgreSQL愛とPythonへの期待が少しでも伝わると嬉しいです。笑

■参加してみて

率直に言ってとても楽しかったです。

運営のみなさま、会場提供やビアバッシュを開催してくださったであろうクリーク・アンド・リバー様、リーディング・エッジ様ありがとうございました!!

これからは、PostgreSQLの能力をちゃんと活かせる(試せる)よう、自分でコード書いたりしたいなーと思っていて、そのために知人のススメもあり選んだのが自分の場合Pythonでした。今日のセッションでも述べられていた「意思をもってPythonを選んだ大人」です。まさに。笑
今日LTでお話させていただいたように、PostgreSQLとPythonはとても相性がよい、これからのデータ分析とかの用途で絶対活きてくる組合せだと思っています。
会社も近所なので是非また参加させていただきたいと思いました。


2016年8月8日月曜日

sysbench 0.5 で PostgreSQL 9.5 のベンチマーク!
環境構築編

sysbenchはCPU、メモリ、ディスクなどに負荷をかけてベンチマークをとれるツールで、標準ではMySQLに対応したOLTP系の負荷掛けにも対応しています。

■本投稿の目的

最新(2016年8月8日現在)のsysbench 0.5 では、ネットに出回ってる情報と一部異なる手順であったのと、PostgreSQLで使用するにはマニュアルが無いなど、ちょっとしたつまづきがあったのでメモしておきます。

■環境

何度か本ブログで触れているCentOS 7.2環境、rpmでインストールしたPostgreSQL 9.5を使用します。

PostgreSQLインストール先(バイナリ配置先) /usr/pgsql-9.5/bin
PostgreSQL実行ユーザのホーム /var/lib/pgsql
PostgreSQLのデータディレクトリ($PGDATA) /var/lib/pgsql/9.5/data

PostgreSQLを起動しておきます。
# systemctl start postgresql-9.5.service

■sysbenchの入手


入手
https://github.com/akopytov/sysbench

rootユーザで適当なディレクトリに配置、解凍しておきました。
# cd /tmp
# unzip sysbench-0.5.zip

■sysbenchのインストール

sysbenchのマニュアルにはMySQL対応についてしか書かれていないため、この手順で少々試行錯誤しました。
# cd sysbench-0.5/
# ./autogen.sh
# ./configure --prefix=/usr/sysbench \
> --without-mysql \
> --with-pgsql=/usr/pgsql-9.5 \
> --with-pgsql-includes=/usr/pgsql-9.5/include \
> --with-pgsql-libs=/usr/pgsql-9.5/lib
# make

成功すると、以下の状態になっていることを確認
  • /tmp/sysbench-0.5/sysbench配下に、sysbenchコマンドが作成される。
  • /tmp/sysbench-0.5/sysbench/drivers/pgsql 配下に、ドライバモジュールが作成される。
# ls /tmp/sysbench-0.5/sysbench/drivers/pgsql
Makefile Makefile.am Makefile.in drv_pgsql.c libsbpgsql.a libsbpgsql_a-drv_pgsql.o

なお、実行ユーザでPATHを通しておくと便利なので、今回はpostgresユーザで環境変数を設定しておきます。
# su - postgres
$ vi .bash_profile
-----
export PATH=/tmp/sysbench-0.5/sysbench:.:$PATH
-----
$ source .bash_profile

■実行前のPostgreSQL側の作業

sysbenchでは、デフォルトでlocalhost、sbtestデータベース、sbtestユーザ、5432ポートでPostgreSQLにアクセスします。そのつもりでPostgreSQL側の環境を準備します。
$ psql
postgres=# CREATE ROLE sbuser login password 'sbpass';
CREATE ROLE
postgres=# CREATE DATABASE sbdb OWNER sbuser;
CREATE DATABASE 
postgres=# \q
今回はいろいろ試すべく、データベース名、ユーザ名、パスワードそれぞれ変えておきました。

■sysbenchの実行

oltpベンチマークの実行では、sysbenc 0.4までと違って、testモードを以下のように指定します。
sysbench/tests/db/ディレクトリを見ると、ベンチマークのパターンごとにluaファイルが用意されているので、これを指定しています。

まずは、prepareでテーブル等の準備をします。
$ sysbench --test=/tmp/sysbench-0.5/sysbench/tests/db/oltp.lua \
> --db-driver=pgsql \
> --pgsql-user=sbuser --pgsql-password=sbpass --pgsql-db=sbdb \
> prepare
sysbench 0.5: multi-threaded system evaluation benchmark
Creating table 'sbtest1'...
Inserting 10000 records into 'sbtest1'

つづいてベンチマークの取得です。
$ sysbench --test=/tmp/sysbench-0.5/sysbench/tests/db/oltp.lua \
> --db-driver=pgsql \
> --pgsql-user=sbuser --pgsql-password=sbpass --pgsql-db=sbdb \
> run
sysbench 0.5:  multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1
Random number generator seed is 0 and will be ignored


Initializing worker threads...

Threads started!

OLTP test statistics:
    queries performed:
        read:                            140000
        write:                           40000
        other:                           20000
        total:                           200000
    transactions:                        10000  (96.53 per sec.)
    read/write requests:                 180000 (1737.48 per sec.)
    other operations:                    20000  (193.05 per sec.)
    ignored errors:                      0      (0.00 per sec.)
    reconnects:                          0      (0.00 per sec.)

General statistics:
    total time:                          103.5983s
    total number of events:              10000
    total time taken by event execution: 103.5635s
    response time:
         min:                                  4.04ms
         avg:                                 10.36ms
         max:                                 23.20ms
         approx.  95 percentile:              10.97ms

Threads fairness:
    events (avg/stddev):           10000.0000/0.00
    execution time (avg/stddev):   103.5635/0.00

実行時のオプションでは、OLTPの並列度や実行回数などを調整可能です。 その辺の実行方法や、結果の見方は別の機会に。

2016年7月27日水曜日

Database Lounge Tokyo #1 に参加しました

巷でウワサのキャンセル待ち続出イベントDatabase Lounge Tokyo #1に参加してきました。ありがたいことに抽選あたりました。

感想は一言で言うと「楽しかったけど脳は疲労困憊!」です。
全編通して「ゆるふわ」の定義を再確認したほうが良さそうなゆるふわっぷりに、登壇者の御三方も、参加者もみんな楽しそうでした!
(ひそかに流行らせたい「きゃっきゃうふふ」も同種のものだろうか。きゃっきゃうふふ専門家の意見が求められるところである。あと、一緒にいった会社の後輩ちゃんが「次は本当のゆるふわを見せてやる!」と意気込んでいたw)

■イベント情報まとめ

セッション資料や動画公開されているので、内容を知りたい人は是非こちらを見てください。
資料や動画が公開されています。勉強会の趣旨とかはコチラのグループページへ。

ツイッターまとめました。Database Lounge Tokyo#1 ツイートまとめ
(誰でも編集可能にしたので、漏れてたら追加してください)

資料
MySQLアーキテクチャ図解講座(奥野 幹也氏)
並列クエリを実行するPostgreSQLのアーキテクチャ(海外 浩平氏)
トランザクションの設計と進化(熊崎 宏樹氏)

■セッション

ゆるふわしたセッションたちの「感想文」です。内容が知りたい人は動画をみてください(キリッ!)

・MySQLアーキテクチャ図解講座(奥野 幹也氏)

奥野さんの発表を生で聞くのは初めて。多岐にわたるMySQLの基礎~新機能までを、知らない人向けに、1時間足らずで、次々話していくんだけど、なぜかスッと伝わってくる感じであった。
実況ツイートしてたけど、各所で話されてることが明確なのでなんか書きやすかったです。
こうやってMySQL触った事ないマンだけど知った気になっていくw

・並列クエリを実行するPostgreSQLのアーキテクチャ(海外 浩平氏)

海外さんも一枚かんでた最新機能「パラレルクエリ」を中心にPostgreSQLのアーキテクチャを語る。
ポスグレの内部実装の話は何度も聞いているのでついていけたが、実運用する上では1㍉も知らなくて良い話(失礼w)であった。いや、ほんとは30%ぐらいは知っておいて欲しい。
けどけど、このセッションが難しかったかもしれないけど、ポスグレ使うのは簡単なんですよ!パラレルクエリも簡単に使えるようになるんですよ、なんと無料で!●racle EEオプションに何百万円、何千万円とかけた人だけ使えた機能が、やっとポスグレでも追い付いてきたんですよ!

・トランザクションの設計と進化(熊崎 宏樹氏)

脳みそがパニック。(ダイイングメッセージばりに短いメモだけとれたので、勉強してから出直します。)

えーっと、(恐る恐るまとめると、)トランザクションが守るべき原則(ACID)があって、それを実現しようとすると、今まではRDBみたいな実装、つまりロックで順序を決めて、定期的に変更履歴を書くことでやってきた。しかしそんなのはメモリが貧弱だった時代のものであって、今のハードにあったトランザクションができるじゃないか、という学術レベルの話。
TPC-Cベンチで現公式記録を数倍?数十倍?ぐらい塗り替えるスコアをたたき出す半面、必ず全てのクエリが40msかかる、レイテンシを犠牲にして並列実行性をめちゃくちゃ高めた方式である。
商用製品は2020年ごろには出てくるんじゃないか、とのこと。

■その他

今日の会場はインサイトテクノロジー様に提供いただき、飲み物もビールからソフドリ各種まで振舞っていただきました。ありがとうございました!
インサイトさんの北海道(と言ってた?) と中継で繋がっており、たくさんの方が画面の向こうで見ていたのは印象的でした。上記の飲み物の件といい、この実行力、どなただろうと思ったら社長さんがガッツリ興味を持たれていたとのこと。うーんなるほど。
と思ったらセッション中の講師(主に熊崎さん) に絡む絡むw会場は爆笑(&ちょっと苦笑)でした。

さて、第二回はバックアップをエサにゆるふわしてくださる方々がいらっしゃるそうです。
9月頃開催予定だそうです!

以上

2016年7月23日土曜日

CPI MEGA MIX 2016に参加しました

CPI MEGA MIX 2016に参加してきました。

レンタルサーバーのCPIが中心となり、そのエバンジェリスト8人からWeb 制作に役立つ情報発信ありの、Web 制作者が交流するイベントです。Web製作者じゃないけど紹介いただき興味を持ったので参加してきました。

体調不良でその後のみなさまの動きを追えてなかったのと、ブログ書くのも遅くなってしまいましたが。

■参加の経緯と目的

前回参加したCSS Niteの懇親会で知り合った方が、本イベントに関わっているとのことで紹介いただき、興味を持ったため、キャンセル待ち登録して空きが出たので参加。(というか事前に一言添えていただいて、飛び入り参加歓迎と快く受け入れていただけた!)
データベースがどんな人たちに使われ、課題を孕んでいるのか、その課題はPostgreSQLで解決できるのか、DB目線でどう解決できるか。

PostgreSQL
オープンソースのデータベースで、信頼性や複雑なクエリに強いことが特徴。
年々高機能化、高性能化しており、業務系ではOracleに取って代わる存在として話題になっているが、Web界では多くのCMSが採用してるMySQLにまだまだ遠く及ばない。
読み方は「ぽすとぐれすきゅーえる」、略すと「ポスグレ」「ポストグレス」って感じ。

自分と違う技術者のクラスタにあって実感したいというのがあって、今回もそんな話を1つでも聞けることを期待しつつ、あとは登壇者の一人は知っている方、もう一人は知人の知人だったのでご挨拶もしたいなと。

■イベント全体

イベントは半日のセミナーと、裏ではハンズオンをやっていました。 会場は株式会社 KDDI ウェブコミュニケーションズさんオフィスのオープンスペース的なところで、椅子が100ほど並んでいました。一番後ろに座ったのですが、プロジェクターの高さの関係か、非常に見やすかったのが良かったです。空調はちょっとキツカッタ(後から判明したのですが私が勝手に寒かったのかも。帰宅後から高熱がでたので。ちなみに感染しない系だったので当日絡んだ方も、ご安心を。)
私は所用で遅れて参加、セミナー2コマ目から、最後の懇親会まで参加しました。

■セッション

6セッション+座談会があり、私は遅れて参加したので3つめから。

WebSocket を介してデバイスを利用する Web システム構築術(大月 茂樹氏)

サーバとクライアントのコネクション確立後、そのコネクション上で専用のプロトコルを用いて双方向通信(サーバーからのプッシュ等)が可能なWebSocket技術の応用でした。
PC向けの非接触ICカードリーダ/ライター(SONY PaSoRi)を用いてNFC対応デバイス/ICカードをかざすと、その固有IDを読み取る.Netアプリケーションのデモ、さらにその読み取った情報をWebSocketを介してブラウザから見る(つまり、Webサイトに表示)するサンプルが紹介されました。
NFCに関するわりと詳しい詳細もあり、この1コマでその技術で何ができるかがちゃんとイメージでき、「知っているだけで、あとは工夫次第で難しいことがクリアできる」 ことをお話されていました。

協業プロジェクトにおけるコミュニケーション設計ケーススタディー(古川 勝也氏)

ITエンジニア+料理研究ユニット古川家(KOGAWA-K)として、またローカル雑誌の編集部としてマルチに活躍されている古川さん。イベント会場でのコラボ弁当の出展では、販促に客層を考慮したInstagramの活用を、雑誌の作成ではメンバーの物理的な距離や世代間のコミュニケーション手段の格差を埋めるタスク管理・スケジュール管理をいかに浸透させたか、と言う話でした。
例えば、スマホのクラウド連携機能とか、Googleカレンダーの共有は、メンバーの知識に歩み寄って使えるようにしてあげることで、煩雑さを無くし便利さを実感してもらい浸透した。Instagramの活用では、若い女性から出てきた案を形にすべく、IFTTTで実装。
各コミュニティにマッチしたやり方があり、ちゃんと使ってもらえるようなアプローチを考えることが成功のポイントになるとのことでした。

コミュニティがあなたを強くする~事例に学ぶコミュニティというエコシステムの活用術~ (江頭 竜二氏)

純国産CMSのbaserCMSの作成、コミュニティ運営をされている、そしてそれを中心としたWeb開発会社キャッチアップの社長をされている江頭さん。江頭さんとはPostgreSQLつながりでお話したことがあり、今日はPostgreSQLのコミュニティの発展のために来たようなところもあるので、とても興味深い内容でした。ITコミュニティが早くから活発であった福岡で、どんなコミュニティの変遷があったか、どんな良い事があったかと言う話でした。
人と人のつながり・助け合い、言いだしっぺがやる文化(そしてやった人が結局一番成長できる)、無茶ぶり、飲み会 など!(ざっくりw)そして自虐ネタがおもしろすぎた。
そして秋にbaserCMSの新バージョンが出る、CPIレンタルサーバーにも乗るよ、という発表がありました。PostgreSQLの回し者としては嬉しい話です。触ってみたいです。

クラウドデータベースで業務効率化と、安全な Web サイト運用術(たにぐち まこと氏)

世にデータベースはいろいろあるが、簡単・安全なデータベースがつかえるサイボウズのkintoneでできることが紹介されました。
「簡単」とは、 インターフェース込みで提供されるサービスなのでクエリを書かずに簡単なwebアプリ向けぐらいのスキーマが作れる、データの格納・取り出しができる、ジョインっぽいことも当然できちゃう。
「安全」とは、自身のWebサイトで収集した個人情報をどこに保管するかと言う話で、ローカルに持つよりクラウドに置くのが安全。平文メールで送るよりもSSL通信でクラウドに連携する方が安全。
AWSのRDSのようなクラウドデータベースを想像すると、簡単さと引き換えにクエリによる自由度や性能は犠牲にしているようで、目的に応じてマッチしたら非常に良い、という印象でした。

座談会 2016年上期 Web 業界ニュース~トレンドから未来予測しよう~

LINEやFacebookAPIが公開されさらにコミュニケーションの幅が広がりそう(特にエンジニアにとって、自サービスとLINEの連携的な意味で。)な話とか、古今東西のデザイナー論をまとめたサイトが公開された話とか、ポケモンGOとか、その他、全10トピックぐらい。
総括は、意外とIoTなかったなー、けどIoTもあるよなー的な感じ(?)ちょっとボーッとしてましたごめんなさい。(言い訳するとこの辺が高熱のピークだったんだと思う。)

■懇親会

世界のクラフトビールで乾杯、プレゼントくじ引きがあたりまくる、メイン料理はタコスとチリコンカン、とてもウマかったです。
私自身は参加に至る経緯が紹介であったこともあり、主催の阿部さんを始め、なんかいろんな方とご挨拶させていただき、セッションの質問なり、データベースのこと、Webのこと、知人のことなどなどいろいろ話をさせていただきました。
参加者の中にはOracleの業務システムを置き換える先を探しているという、まさに私が会うべくして会った様な方もいて、とても参加した甲斐のある懇親会でした。
これを機に自分もレンタルサーバーで遊んでみたり、データベースの普及、コミュニティの活性化に一役買えたらなと思いました。(もちろん今もその活動をしてるんだけど。いい刺激になりました。)

以上

2016年7月18日月曜日

CSS Nite「ビジュアル・ドリブンのデザイン」に参加しました

CSS Nite LP46「ビジュアル・ドリブンのデザイン」再演版 に参加してきました。
tweetまとめられてました。 自分もわりとつぶやきました。(実況とは程遠い、私の目線でのつぶやきなので意味があるかは謎い)

とても多くの学び、今後も大切にさせていただきたい出会いがありましたが、それはセミナーの本筋とは関係ないところで、参加レポートと言ってもここで多くを語るべき内容ではないというのが正直なところですが。

■参加の目的

CSS Niteには初参加で、デザイン寄りの畑違いの内容であることはわかっていたのですが、サーバーサイドのエンジニアとして、あと日本PostgreSQLユーザ会の一員として、上の層を担っている人たちの課題、データベースを知ってれば解決できるかもしれないテーマが眠っていないか探りたいと思ってセッション+懇親会に申し込みました。

結果は期待通りで、データベース層ではデザインの領分と直接的なつながりは無さそうであると。
しかし、CMSは日常的に触るのでデータベースの存在ぐらいは意識する方、上から下までWebサービス全般を担う方、地方での勉強会需要など、皆さんの話を聞くことができ、また、私の話にも耳を傾けていただき、今回をきっかけに人とつながれたことは(もちろんそれを狙ってはいたのですが)予想をはるかに超える収穫になりました。


■セッションのこと

どのセッションも、デザインの専門家としていろいろお話していただいて、デザイナーさんの領域がイメージできたというところが今回の総括的な収穫になりました。

普段サーバーしか触らない私の目線からすると、とあるWebサービスで、ユーザが触れるUIの部分、そこから入力を受け取って動作するプログラムの領域、やっとそここら必要があればデータにアクセスしにくるわけで、上の層って誰がどう作ってるんだろうと(一人でWebサービス全部作っちゃうような知人はいるのでw)仕事の領域はどうなってるんだろうという、そんなところがちょっと見えてきました。

で、会場で書いたアンケートがそのまま参加メモというか感想文になってたので、それをそのまま写メって帰ってきました(笑)。以下はその程度の感想文です。セッションの受講レポートが見たい方は別で調べてくださいw

・わずかな工夫で劇的に変わるデザインの対話術(長谷川 恭久氏)

自分が作るデザインが人に伝わるようにどう扱うか(解説する、会話する、意見を求める)という話で、素人目線でも大いに納得でした。
素人からすると、デザイナーさんから出てきた時点で見栄えとしては劇的に違うので頷いてしまう場合が多い。しかしなぜその色なのか、なぜその配置なのか、ぶっちゃけその必然性には全く思考が及んでいませんが、それが「好みに合わない」場合にデザイナーさんのこだわりなのか、モノ申して良いポイントなのか判断しづらかったり、でもこちらの根拠は好みかどうかだけだし、で、製品プロモーション的には意味のない議論になってしまうとか。
ちゃんと会話ができ、必然性を説明いただけるという点こそ、プロと素人の違いになるのかなと思いました。 (素人=イラレ、フォトショとかのツールは使えるちょっとセンスのある人)

・スマホアプリにおけるUIのトレンドと、動的デザインの重要性~テクニカルクリエイターが担うサービス開発の今後(佐藤 洋介氏)~

私の中で注目セッションその1。
「テクニカルクリエイター」とはデザイナー、プログラマーのどの領域を担うのか。(それがサーバーサイドまで意識するようなエンジニアの領域と関わるのか、が注目していたポイント。)
聞くと、動的なUIを実現するようなツールが増えてきて、デザイナーでも自分でプロトタイプを作って動かせるようになってきた。プログラム的な制約がわかってデザインレビューを進めないといけないというように、デザイナーとエンジニアの垣根を低くしていくような取り組みであった。と同時に、やはり業務は分担されていて、サーバーサイドを意識するのはエンジニアだなというのがイメージできてきました。

・こだわりを実現するためのデータを介したコミュニケーション(北村 崇氏)

私の中で注目セッションその2
データとはデータベースか。例えばデザイナー側でWebサービスのレスポンスまで意識してデータ層のことを考えるの?!などと思って受講しましたが、「データ=入稿データ」のお話しでした。
入稿データのレイヤにわかりやすい名前をつけるとか(ソースコードにコメント書くとかに似てる?!)普通に仕事をするうえで、自分の成果物を次の工程で待っている人に配慮した仕事をしましょうという話で、それはそれは、デザインじゃなくとも意識しないといけないよなーと思いました。

・クリエイティブ・ディレクション~リアルダメ出しから発掘するデザインのヒント~(李 優未氏)

より良いアウトプットに近づけるための仕事の進め方、育成の方法という話。実例は素人目にはどれもよく見えてしまってあんまりビフォーアフターの差がわからなかったですが、本題は大いに納得でした。
ステークホルダーの意見を求める場をつくりましょうとか、そのために意見しやすいフォーマットでだしてもらいましょうとか(スマホUIの例ならスマホの幅で縦長に出力した短冊型でWebサイトを印刷して付箋貼っていってもらう)、この辺は一本目の長谷川氏の会話しなさいという話と重なる印象でした。部下の育成なら、その人のレベルに合わせた指摘のやり方をするとか。正解がないもの(だけど失敗はある、と言えば良いのか)を正しく教えていくための方法は難しそうだが大事であると思いました。

・エディトリアルとウェブ、コンテンツファーストでつながるビジュアルドリブン(筒井 美希氏、関口 裕氏)

雑誌(≒エディトリアル)出身の同期お二人組とのこと。トーク形式で進めつつ、雑誌とWebの違いあるある、結局のところ両者の違いはなにか、共通するところは何かを語っていった。
で、コンテンツに合わせて、書式をはじめに決める(WebでいうところのHTMLとCSSの分離)場合もあれば、フリーフォーマット?で各ページごとに適切な配置を考えるケースもある。後者は従来エディトリアルとされてきたような部分だが、Webでもそれはありえるぞと。(エディトリアルという初見の単語だったので、どういうイメージで使われているかとかよくわかんなかったけど。)
これは、スライド作成で使える話ではあるかもしれない。いつもの「タイトル、本文数行、図」みたいな構造化してるやつと、1スライドにどーんと数字がでてきたりするスタイルとの使い分け。みたいな。なんか考えたい。

・築城10年、落城3日。こだわりあるWebデザイン、やるからには本気で良いものを!(中川直樹氏)

デザインに依頼者の意図を余すところなく取り込むためのやりかた。
お願いする側としては、近くて遠い(表現したい自社のイメージはあるのかもしれないがそれを伝えられなくてあきらめる?結局そのイメージって自社内ですら共有できてる?)話でした。
製品販促の施策とかは考えることがよくあるので、デザインでその一端を担うところをちゃんと考えると効果ありそうかもと思いました。

・クロージングトーク:PVを稼ぐグラフィックの作り方(矢野りん氏)

PV=拡散された数×クリック率でクリック率を上げるところでデザインが威力を発揮する話。具体的には写真がSNSによってどう切り抜かれるか知って、それに合わせた画像を用意せよ、等。
これは素人目にも結果が理解しやすく、写真の切り抜きとか撮影の時のテクニックでも使えそうなので 自分でUPする写真なんかでも気にしてみようと思いました。

■会全体についてや懇親会のこと

イベント全体を通して、会話を生むとか、初めての人にも優しい、けどリピーターとか全体的には良く見知った人たちの集い、その両立をうまく実現しているようでした。

昼に会場に到着すると、広い部屋に広々とスペースが取れるよう机が配置され、うーんさすが有償イベントと言いう感じ。「空席」の紙を裏返すと今日のタイムテーブルになっており、新参者にも優しい配慮であるなと感じました。
セッション前には隣の人と強制自己紹介タイム!自分は懇親会までいくのでボッチ回避にはありがたすぎるけど恥ずかしい企画。で、お隣、後ろの方は懇親会でなかったのですが、「僕初めてなんです~」というところから、いつもは○○な雰囲気だよ~などと教えていただきました。そして混んできて椅子が追加された後も、ちょっと腕当たっちゃったりしても問題なしな雰囲気になりましたw

終了後の懇親会は会場の1Fのお店。自由な雰囲気に最初こそ戸惑いましたが、講演者の周りを囲むように席が配置され、とっかかりの会話が生まれやすく、そこからは隣の人とフランクに話せました。
ここでみなさんとお話したことでは、
  • デザイナーとエンジニアの領域は、やはりしっかり分かれている
  • デザイナー寄りの人ではデータベースはブラックボックス
  • CMSとかやっている人だと、データ層の存在ぐらいは意識するがクエリまでは意識しない(CMSがやってくれる範囲か)
  • 参加者側では、Webサービスを作っている人とかいて、マジでフルスタックに求められてツラたんそうだったけどやりがいありそう。
  • 学生を教育する立場の人では、その学生の方向性が決まってないのでデザイン系でもphpからデータベースにアクセスするぐらいは教えているとか
デザイン系の人がphpやりだして、ってことはデータにアクセスするのでSQLも覚えて、その先まで一人でやらないといけなくなったらDB設計・運用とかってなるのはさすがに範囲広すぎだよなと思った。

今日の目的には以下があり、どちらも上々(お金かけてきた価値あった)かな、と思います。
  • データベースの人がちょっと興味もって来てみたよ、という存在アピール(ツイートしてたらすぐに気づいてもらえた!)
  • 参加者の中で数人でも良いので、データベース系イベントの開催地やターゲット層のヒントになる方とお話しする(懇親会まで参加して本当によかった)
そしてデザインの人が語ることを自身の仕事に置き換えてみたり、きれいなスライド、参加しやすい運営など何から何まで勉強になりました。また機会を見つけて参加したいと思います。

2016年7月1日金曜日

Ora2Pgでやってみよう!テーブル定義移行編

こんにちは、kkidaです。
沖縄合宿2日目です。

昨日に引き続き、他DBからPostgreSQLへの移行の話を聞いていただいた方へ、是非この機会に紹介しておきたいのが、OracleまたはMySQLからテーブル定義・データを抽出してPostgreSQL用に書き換え、ロードまでを一括実行できる便利ツール、Ora2Pgを使用したデータ移行(とっかかり)です。


■ここでは何をやるか

移行してみるにあたり、Ora2Pgのオプション設定等は細かくはいろいろあるのですが、最初のハードルは「使える状態にすること」だと思います。(経験談w)
まずは使える状態にして、Oracleからテーブル定義を抜いてくるところができれば、あとはOra2Pgのマニュアルとにらめっこで、オプションをいろいろいじってみて、やりたい事に近づけるのではないでしょうか。

昨日紹介したPostgreSQL on CentOS 7環境で構築します。
例によって、DBサーバーは外部公開しづらい前提で、CentOSのインストールDVDからyumできるもの以外は、個々にモジュールを持って来てサーバーに配置するようにして、実際に動くところまでやってみました。


■準備

以下をダウンロードしておきます。WinSCPなどの転送ツールを使用して、サーバーの/media/ora2pgにrootユーザの所有で配置しておくものとします。
今回は使用したバージョンまで書いておきますが、相手のOracleのバージョンや、いずれかのモジュールの将来のバージョンでは、これらの組合せがそのままでは使えない可能性があることをご注意ください。

Ora2Pgはperlスクリプトで、各DBへの接続はDBD::xxxというDBへの接続ライブラリ(xxxはDB製品名、DBD::Oracleとか)を用います。その前提となるパッケージで、yumで入るものは入れてしまいましょう。また、昨日の続きなので、yum groupinstall "Development Tools"は導入済みとします。
# mount /dev/cdrom /media/CentOS
# yum --disablerepo=* --enablerepo=c7-media install libdbi-dbd-pgsql
# yum --disablerepo=* --enablerepo=c7-media install perl-ExtUtils-MakeMaker
# yum --disablerepo=* --enablerepo=c7-media install perl-DBI

■Oracle Clientのインストール

以下の順でRPMからインストール、環境変数を設定します。後続の手順であるDBD::Oracleのインストール時に、ここで作成したOracleのライブラリが参照されます。
# cd /media/ora2g
# rpm -ivh oracle-instantclient11.2-basic-11.2.0.2.0.x86_64.rpm
# rpm -ivh oracle-instantclient11.2-devel-11.2.0.2.0.x86_64.rpm
# rpm -ivh oracle-instantclient11.2-sqlplus-11.2.0.2.0.x86_64.rpm
# export LD_LIBRARY_PATH=/usr/lib/oracle/11.2/client64/lib
# export ORACLE_HOME=/usr/lib/oracle/11.2/client64/

■DBD::Oracleのインストール

以下の手順でソースファイルを展開、インストールします。
# cd /media/ora2pg
# tar zxvf DBD-Oracle-1.74.tar.gz
# cd DBD-Oracle-1.74
# cp ../DynaLoader.pm ./.
# perl Makefile.PL
# make install

■Ora2Pgのインストール

以下の手順でソースファイルを展開、インストールします。
# cd /media/ora2pg
# tar jxvf ora2pg-17.4.tar.bz2
# cd ora2pg-17.4/
# perl Makefile.PL
# make && make install

■Ora2Pgを使ってみる

構築が完了すると、/usr/local/bin/ora2pgコマンドと、/etc/ora2pg/ora2pg.conf.distファイルが出来上がります。Ora2Pgは、詳細動作を全て設定ファイルに書いて、ora2pgコマンド実行時にその設定ファイルを指定するという使い方をします。

設定ファイルの編集
# cd /etc/ora2pg
# cp ora2pg.conf.dist ora2pg.conf
# vim ora2pg.conf

最低限書いておく必要があるのは、Oracleへの接続方法と、移行対象です。
  • ORACLE_DSN      dbi:Oracle:host=<oracleのホスト名>;sid=<SID名>
  • ORACLE_USER    <Oracleの移行対象オブジェクトを触れるユーザー名>
  • ORACLE_PWD     <上記ユーザのパスワード>  
  • SCHEMA           <移行対象スキーマ名>
設定ファイルができたら、ora2pgコマンドを実行します。-cで設定ファイルを指定します。
# cd <任意のディレクトリ>
# /usr/local/bin/ora2pg -c /etc/ora2pg/ora2pg.conf
[========================>] 1/1 tables (100.0%) end of scanning.
[========================>] 1/1 tables (100.0%) end of table export.
# ls
output.sql  ### PostgreSQL用のスクリプトが生成されている

出力されたファイルの中身を確認すると、PostgreSQL用のDDLスクリプトであることがわかります。参考までに、Oracleではnumber(8)、varchra2(30)、dateを各列のデータ型としていましたがPostgreSQLの対応するデータ型に書き変わっていることが読み取れます。
# more output.sql
-- Generated by Ora2Pg, the Oracle database Schema converter, version 17.4
-- Copyright 2000-2016 Gilles DAROLD. All rights reserved.
-- DATASOURCE: dbi:Oracle:host=192.168.160.179;sid=v11204

SET client_encoding TO 'UTF8';

\set ON_ERROR_STOP ON


CREATE TABLE test (
  no integer NOT NULL,       --- number(8) がintegerに変換された
  val varchar(30),           --- varchar2(30) がvarchar(30)に変換された
  time timestamp             --- date が timestampに変換された
) ;
ALTER TABLE test ADD PRIMARY KEY (no);

本例では、Ora2Pgの実行時設定はほとんどをデフォルトとし、Oracleから指定スキーマの定義情報だけを抽出し、PostgreSQL用のスクリプトを生成するところまでとなりました。
定義情報と併せてデータも抜き出す設定であれば、一気にデータ移行が済んでしまいますし、PostgreSQLへの接続情報を書いておけば、直接PostgreSQLに投入することもできます。詳細動作はOra2Pgのマニュアルから、やりたいことに合わせて試していくことをお勧めします。

以上です。

CentOS7上でのPostgreSQL構築手順(RPM編)

はいさい!

OSC 2016 OKINAWAに参加すべく、沖縄にやってまいりました、kkidaです。

OSCでは、前に福岡福山(広島)でしゃべらせてもらった、「他DBからPostgreSQLへの移行」の話をします。つまり、新たにPostgreSQLを使い始めるかもしれない人にこそご参加いただきたいわけで、せっかくなので、受講だけじゃなくて、一歩踏みこんで試すところまでやってもらいたい。そのための情報を整備しとかなきゃなーと思って、ひとり合宿in沖縄で本稿を書いています。

ここでは、最近仕事でCentOS 7を触っているので、自分の復習も兼ねて、CentOS 7上でのPostgreSQLインストールを超初心者向けに書こうと思います。

■PostgreSQLインストール方法の選択肢

主要なOSディストリビューション向けにはいくつか方法があって、日本PostgreSQLユーザ会のダウンロードページで各配布先が紹介されています。初心者向けなので、これらのページの渡り歩き方から紹介しておこうと思います。

 ●ダウンロードリンク
  • Windows:GUI環境(WindowsまたLinux)向けにインストーラーが提供されていて、簡単にインストール先を変えたい場合は便利です。
  • Linux(yum):実は私、yumでPostgreSQLいれたことありません。DBサーバーはインターネットにつながっていないケースが多く、ハードル高かった記憶があります。
  • Linux(rpm)、Linux(apt):目的のパッケージをダウンロードしサーバーに転送しておけば良いので、知っていれば簡単ですが知らないと「?」が出まくりです。今回はこれを説明(後述)します。
  • ソースコード:PostgreSQLの実行ユーザやインストール先を変えたい場合、または詳細動作のカスタマイズをしたい場合はソースコードからインストールすることになります。

 ●インストールガイド
 本稿を書くにあたってリンク先を確認しましたが、そこそこ古く
 「そのまま実行したら上手くいった」レベルの情報は期待できなさそうです。

 ●その他
 PostgreSQLの本体開発に参加する人向けのWikiや、JPUGのマスコットロゴについて
 書かれています。(ダウンロードページなので、そういうのも一色多に配布してるw)

※ちなみに、日本PostgreSQLユーザ会では、上記ウェブサイトの刷新を鋭意進めている最中で、本稿の内容は新しいものがリリースされたら読み替えが必要になります。もって1年の命でしょう。

■PostgreSQL(RPM版)のインストール準備

今回はRPM版でPostgreSQL 9.5をインストールします。
配布先からズンズンたどって、以下を入手します。
  • postgresql95
  • postgresql95-contrib
  • postgresql95-libs
  • postgresql95-server
2016年7月の現時点だと、各リンク先に「postgresql95-9.5.3-2PGDG.rhel7.x86_64」のようにPostgreSQL 9.5.3のRPM内のtypoを修正した「-2」がありますので、こちらを使用します。
(初心者向けに直接DL先を挙げておくなら、ダウンロードしサーバーに配置しておくべきは、postgresql95contriblibsserverの4つです。←リンク先は2016年7月時点のものであることに注意)
開発者向けのincludeファイルも必要である場合は、develも取得します。まあこの辺は他のソフトウェアと変わらないでしょう。

サーバーへの配置はwinSCPとかを使って、rootユーザの持ち物で/media/pg95とかに置いておけば良いです。
# mkdir -p /media/pg95 (ココにRPMを転送)
# ls /media/postgres
postgresql95-9.5.3-2PGDG.rhel7.x86_64.rpm
postgresql95-contrib-9.5.3-2PGDG.rhel7.x86_64.rpm
postgresql95-devel-9.5.3-2PGDG.rhel7.x86_64.rpm
postgresql95-libs-9.5.3-2PGDG.rhel7.x86_64.rpm
postgresql95-server-9.5.3-2PGDG.rhel7.x86_64.rpm

RPMだと、前提パッケージが足りていないと途中でこけてしまいますが、CentOS 7.2ではOSメディアから"Development Tools"を入れておけば大丈夫でした。
# mount /dev/cdrom /media/CentOS
# yum  --disablerepo=* --enablerepo=c7-media groupinstall "Development Tools"

■インストール

ここまで準備できれば、インストールは簡単です。依存性でつまづく事の無いよう、以下の順にインストールします。
# rpm -ivh postgresql95-libs-9.5.3-2PGDG.rhel7.x86_64.rpm
# rpm -ivh postgresql95-9.5.3-2PGDG.rhel7.x86_64.rpm
# rpm -ivh postgresql95-devel-9.5.3-2PGDG.rhel7.x86_64.rpm
# rpm -ivh postgresql95-server-9.5.3-2PGDG.rhel7.x86_64.rpm

これで、/usr/pgsql-9.5配下にPostgreSQLがインストールされます。postgresユーザが作成され、systemctlコマンドで起動・停止の管理ができるようになります。
# ls /usr/pgsql-9.5
bin  include  lib  share
# systemctl list-unit-files | grep postgres
postgresql-9.5.service disabled

ところが、まだデータベースとして使用できる準備はできていませんので後続の手順を実施してから起動です。(以前のバージョンではこの時点で使えたような記憶がある。)
どうやら最近のRPM版では、初期設定(データベース作成、PostgreSQL用語でいうinitdb)をまるっとやってくれる便利コマンドが用意されているようで、これを使います。
# cd /usr/pgsql-9.5/bin
# ./postgresql95-setup initdb --no-locale
Initializing database ... OK

なななななんじゃこりゃーって感じでしたが、そうなのです。自動化しちゃうと、出来上がったデータベースクラスタがどこにどうできてるの?と気になってしまいますが、初心者向けなののでココは後に譲ります。いつか調べる。(@soudaiさんに指摘いただき、日本語環境においては是非やっておくべきinitdbコマンドのオプション(--no-locale)を追記しました。2016.7.11)


■起動と自動起動設定

初CentOS 7だったので、sysytemctl慣れしたい私の勉強にお付き合いください。笑
# systemctl list-unit-files | grep postgres
postgresql-9.5.service disabled
# systemctl start postgresql-9.5.service
# systemctl status postgresql-9.5.service
● postgresql-9.5.service - PostgreSQL 9.5 database server
  Loaded: loaded (/usr/lib/systemd/system/postgresql-9.5.service; enabled; vendor preset: disabled)
  Active: active (running) since 水 2016-06-22 22:31:50 JST; 14s ago

起動時のログも拾ってくれるんですね。なかなか便利。(実は↑の初期設定が必要なことに気づかす、起動に失敗してログがでたことで発覚しました。systemctlに感謝!)
自動起動はデフォルトでdisabledでしたので、必要に応じて有効にしておきます。
# systemctl enable postgresql-9.5.service
Created symlink from /etc/systemd/system/multi-user.target.wants/postgresql-9.5.service to /usr/lib/systemd/system/postgresql-9.5.service.

# systemctl list-unit-files | grep postgres
postgresql-9.5.service enabled

ついでに、社内サーバー前提でRPMからインストールを解説しているわけですので、デフォルトで有効になっているfirewallはoffっちゃいましょう。(異論は認める。)
# systemctl stop firewalld.service
# systemctl disable firewalld.service

これで、データベースが起動しました。
ここから先は、PostgreSQLの初期設定であり、OSやバージョンによらない内容を別の投稿としたいと思います。

では、今日から楽しいPostgreSQLライフをお送りください。

2016年6月18日土曜日

JPUG 2016 夏セミナーに参加してきました

こんにちは、kkidaです。

日本PostgreSQLユーザ会(JPUG)の2016 夏セミナーに参加してきました。
http://www.postgresql.jp/events/jpug201606semi


■JPUG夏セミナーとは
NPO法人であるJPUGは、年に一回、正会員(≒NPO法人の社員)が全員参加して今後の会のあり方を決定する「総会」を開催しています。JPUGは日本全国各地に会員がいて、みなさんが必ず集まる場はこの総会のみです。 (その他に年一回ペースで開催しているカンファレンスがあり、多くの方がそちらにも参加されます。)
せっかく皆さんに集まっていただくので、今イマ世の中で注目されているテーマ、PostgreSQLの最新情報などをお持ちの方に講演をお願いし、各地に持ち帰って広めていただこうと始まったのが夏セミナーです。

■今日のセッションは2本
いつもPostgreSQLの新機能をイチ早く試して情報発信されている篠田さんと、IoT、BigData、画像処理などに実務で取り組まれている佐藤さんによる発表です。最新機能の紹介と、やりたいことをどうやって実現するかの両側面から学べた勉強会でした。

PostgreSQL 9.6 新機能紹介
 日本ヒューレット・パッカード株式会社 篠田典良氏

・FA分野におけるPostgreSQLの使われ方
 住友金属鉱山株式会社 佐藤健司氏


■併設イベント(本当はこっちがメイン)
昨年度、PostgreSQLに関する技術貢献、情報発信などで普及に貢献していただいた方に、JPUGから感謝賞を授与しています。今年の受賞者は以下の3名でした。

・松田 伸一氏
・斎藤 登氏
・須藤 功平氏

本日のメインは最後の総会・理事会で、 各役割を担う理事から、昨年度の活動報告と今年度の活動計画が提示されました。
総会での大きな話題は、前理事長を始め数名の理事退任と、それに替わる新任理事が紹介され、承認されています。(ただしこの段顔では「役職案」を基に理事会を構成するメンバーが承認されるのみ)
もう1つ、今年度はPGconf.Asiaという新たなイベントが別団体で企画されており、JPUGとしてはそこに協賛するかどうか、どうやって関わるかなども話し合われました。
総会後は、承認されたてほやほやの理事による理事会を行います。ここでの理事会は、役職決めが主で、特に前理事長が退任した今回は様々な意見が飛び交いました。自薦他薦あり、今後の会の方向性などが話し合われ、最後は投票により、高塚理事長の就任となりました。
ちなみに、こういう決め方をしたのは15年のJPUGの歴史の中で初めて、とのことです!
もう1つちなみに、私kkidaは「広報・企画担当」理事として、上述のAsiaイベント運営に関わることになりました〜


■懇親会
会場であったアシスト社の向かいすぐの「上海ブギ」さんで懇親会です。安くて美味しい!

毎年恒例、地方支部メンバーからのお土産争奪戦あり、初参加の方も数名いらっしゃって、ご自身とPostgreSQLの関わりなどをベテランメンバーと話されていたりして各テーブルで盛り上がっていました。あとは常連さんのうち、諸事情ありしばらく来れなくなってしまうであろう方がいて、皆から惜しまれ、愛されてるのが伝わってきました。どうやらポスグレ愛は薄れていないとのことだったので、地方支部の参加(立ち上げ?)もお願いしますよ!S氏!

という感じで、ほぼ丸一日を久々に会う皆さんと過ごし、笑いあり真面目あり、勉強も飲みもありの盛りだくさんな会でした。

以上