LoginSignup
5
5

More than 5 years have passed since last update.

postgres_fdwによるDB垂直分割

Last updated at Posted at 2016-09-16
1 / 20

困っていたこと

  • 某Webサービス用DBServerの Disk usage が 79% まで膨張した
    • 近い将来、最大TABLEの水平分割工事をする余裕を残す必要あり
  • 汎用ログの格納TABLE action_logs のカット率が75%
    • ユーザ端末のバッテリー状況とか、Wifi通信エラーレートとか
    • 25%あれば全体統計をみるには十分だが、個々の状況の時間推移は追えない

不可視バグでDBServerが危ない

  • 端末log送信は直接目に見えないところなので、バグが仕込まれやすい
  • バグがDailyAccessUser数の倍率で飛んでくる
  • 例1:通信回復時の再送処理の考慮漏れで、毎正時(*:00:00)にPOST殺到
  • 例2:(key,Value)のValueがやたらと長いlogが急増

対策概要

  • action_logs 専用の DBServer を追加する
  • postgres_fdw: Foreign Data Wrapper で連携させる
    • 既存DB に CREATE FOREIGN TABLE .... すればコード変更一切なし
    • SQLだけの移行処理で対応可能

postgres_fdw?

  • 外部のPostgreSQLサーバに格納されたデータをアクセスする
  • 以前のdblinkモジュールが提供する機能と重複

    • より透過的で標準に準拠した構文
    • 多くの場合においてより良い性能
  • こんな図になる

  • インガオホー! jsonb + postgres_fdw by ぬこ@横浜 サン


遅くならない?

内部TABLEと外部TABLE のFULL JOINを含むとか、そんなことをすれば、外部サーバとの通信量が跳ね上がって性能劣化するのは、postgres_fdw も dblink も同じ。
幸いなことに action_logs 系は、ひたすら INSERT するのみ。
通信オーバーヘッドは上乗せされるが、定数項だろう。


効果見積

直近で、合計 XXXGB を本体DBから削減可能。

サイズ・IO処理が本体から切り離されるので、POST api/logs を100%記録することも可能になる。


将来展開

action_logs の垂直分割に成功したら

  • ほかにも運用でINSERTだけして、分析DBにコピーしてからSELECTしているTABLEがある
  • そっちに適用拡大するのは容易
  • エンドユーザにも必要なTABLEは、本体DBとの一体的Backup/Snapshotが必要になって面倒。今はそっちに拡大する予定はない。

移行リハーサル

  • 移行前に 該当POSTを rewrite.conf で全て成功応答にする
    • Apache で遮断して、DB の TABLEの更新停止
  • 移行所要時間
    • 既存の行を新DBServerに移す処理が最大
    • 直近の合計で数千万行
    • 通常の INSERT でトランザクション処理すると推定10時間くらい
    • COPY文のTSV出力(1分)、scp(10分)、COPY文のTSV入力(10分)

本体DB側コード(fdw定義)

CREATE EXTENSION IF NOT EXISTS postgres_fdw;

CREATE SERVER logdb FOREIGN DATA WRAPPER postgres_fdw
  OPTIONS (host 'logdb', dbname 'product_x' , port '5432' );

GRANT ALL ON FOREIGN SERVER logdb TO product_x;

CREATE USER MAPPING FOR product_x SERVER logdb 
  OPTIONS ( user 'product_x' , password 'xxxxxx' );


本体DB側コード(外部テーブル定義)

CREATE FOREIGN TABLE schema_x.action_logs_new
(
  id bigint NOT NULL DEFAULT nextval('action_logs_new_id_seq'::regclass),
  child_id bigint NOT NULL,
-- ...
  created_at timestamp without time zone NOT NULL
) SERVER logdb
  OPTIONS (schema_name 'schema_x', table_name 'action_logs');

データExportコード

COPY (SELECT * FROM schema_x.action_logs WHERE id >= 120000000 AND id < 130000000)
 TO '/tmp/action_logs_12.tsv' WITH DELIMITER E'\t' CSV;

COPY (SELECT * FROM schema_x.action_logs WHERE id >= 130000000 AND id < 140000000)
 TO '/tmp/action_logs_13.tsv' WITH DELIMITER E'\t' CSV;

-- $ scp -p host_db_1:/tmp/action_*.tsv .
-- $ scp -p ./action_*.tsv host_db_2:/tmp/
-- $ ssh host_db_1 sudo rm /tmp/action_*.tsv

データImportコード

COPY action_logs_12 FROM '/tmp/action_logs_12.tsv' WITH DELIMITER E'\t' CSV;
COPY action_logs_13 FROM '/tmp/action_logs_13.tsv' WITH DELIMITER E'\t' CSV;

あとは、本体DBの対象TABLEを rename で 現行/new を入れ替える。


性能検証

  • 過負荷ストレステストのJMeterがあったので再利用
    • 運用実績の180倍速
  • 90%保証応答時間, 99%保証応答時間(msec)
分離前 分離後
90%値 285 387 +36%
99%値 439 551 +26%

検証結果

  • POST 単体は 3-4割応答が遅くなる
  • 本体DBServer の負荷は、一部外部Serverに移るので軽くなる

※社内用には Muninグラフがあった


運用コスト

幸い、某DBServer用AWS instanceのReserved期間の使い残しがある。
その間に、運用環境で必要な性能をみきわめて最適化する。


第2次性能検証

運用環境での実需に寄せた性能検証シナリオにして、必要十分なところを探す。

  • リクエスト数
  • 処理時間平均
  • 平日ピーク 17:00-23:00 の6hに全集中するとして 全日POST数/6hのrequest/sec
  • この3倍を性能目標とした

POST内容

運用action_log実績

  • 90%応答時間 168msec
  • 平均 4行INSERT/POST。最大100行 が約1000POST。
  • 平均 label長 60文字、最大4440文字

JMeterシナリオを上記をもとに修正


検証結果

Type IOPS 90% 99% 503rate rps
m3.xlarge 4000 74 264 0.45
m3.large 4000 77 272 0.41
m3.large 1000 75 269 0.45
m3.medium 4000 82 283 0.50
m3.medium 1000 87 317 0.54

2016-8時点の実需にそった計測条件で、レスポンスタイムにも、503発生率もほとんど影響のないところを選択して、 m3.large, 1000IOPS とする。
$188/月で垂直分割できた(前払い無し1年間リザーブド)。


上司講評

綿密な考証、入念な事前準備。
本体サービスは無停止で、ログ記録を止めた時間も20分程度。
原理は複雑だけど、実際にやっていること、変更点を見ると、こんなに簡単にできるのかと拍子抜けするほどシンプル。
運用上のコストを下げることも考えられていて良かった。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5