kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

InnoDBのバッフアプールとMyISAMのキーバッファについて

概要

  • InnoDBとMyISAMの違いについて解説
  • その上で、それぞれのキャッシュの取り扱いと、キャッシュに関する値の最適化について解説

そもそも、InnoDBとMyISAMの違い

どちらも ストレージエンジン

MySQLは、リクエストから以下のような流れで、データのread/writeが行われる

コネクションプール
    ↓
   パーサー(SQLをパース)
    ↓
ストレージエンジン(SQLを元にストレージを読み書きする)
    ↓
  ストレージ

MySQLではこの ストレージエンジン を MyISAMとInnoDBの2つから選ぶことができる

で、InnoDBとMyISAMどっちが良いの?

以前はMyISAMとInnoDBの棲み分けがあったが、今は概ねInnoDB

MySQL 5.5.5 からは、InnoDBがデフォルト

公式でもInnoDBの利点がいろいろ書かれて推奨されている

dev.mysql.com

InnoDBとMyISAMのキャッシュの取り扱い

InnoDBとMyISAMのおおきな違いの一つ

MyISAMの場合

MyISAMでは、indexのキャッシュはMySQLが管理。データのキャッシュはOSに任せている

  • MyISAMは1テーブル1ファイル
  • それにより、頻繁にアクセスされるファイルは、OSのキャッシュメモリに乗る
  • そのため、全テーブルがキャッシュメモリに乗るようにメモリサイズを調整する必要がある

インデックスの情報は MySQL 側の key_buffer に格納

key_buffer のサイズは key_buffer_size で設定。最適な値について公式ドキュメントでは、以下のように書かれている

  • マシンの合計メモリーの 25% 程度の値を推奨(残りはOSのキャッシュメモリに使わせる)
  • SHOW STATUS して Key_reads/Key_read_requests > 0.01 or Key_writes/Key_write_requests < 1 ならば、インデックスが効率よくキャッシュされていないので、値を増やす

dev.mysql.com

InnoDBの場合

InnoDBでは、indexのキャッシュも、データのキャッシュもMySQLが管理

もうちょっと厳密に話すと、InnoDBではストレージの読み書きの前に バッファプール を経由する

  • readのとき バッファプール にデータがあれば、読み込みはメモリ上で完結する
  • writeのときも バッファプール のデータを更新して、非同期でストレージを更新する

そのため、バッファプールが小さいと頻繁にディスクアクセスが発生して処理が遅くなる

バッファプールは、innodb_buffer_pool_size でキャッシュサイズを設定可能

公式ドキュメントでは、innodb_buffer_pool_size はマシンのメモリの80%を推奨

dev.mysql.com

まとめ

  • MyISAMでは、indexのキャッシュはMySQLが key_buffer で管理。データのキャッシュはOSに任せている
    • key_buffer_size の最適値はマシンの合計メモリーの 25%
  • InnoDBでは、indexのキャッシュも、データのキャッシュもMySQLが innodb_buffer_pool_size で管理
    • innodb_buffer_pool_size の最適値はマシンのメモリの 80%

Rubyでは真偽とtrue/falseは別物だよ! というお話

今日は、Rubyの true, false と、真偽は別物だよというお話をします

これは何?

会社blogで公開しようとした直前に、上位互換の記事が出てお蔵入りしてしまった可愛そうなblog記事です

techracho.bpsinc.jp

供養と自分の学習記録も兼ねて、個人ブログにupいたします

🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏🙏

真偽とは

Rubyにおいては、 真偽は以下のように定義されてます

  • 偽 : false または nil
  • 真 : 偽値でないすべて

Ruby では false または nil だけが偽で、それ以外は 0 や空文字列も含め全て真です。

制御構造 (Ruby 2.6.0)

true, false とは

真偽を代表するオブジェクトであって、これ以外の値も、上記で説明したように真偽を取ります

  • なお、true, false とも、あるclassのシングルトンのインスタンスです

true は TrueClass クラスの唯一のインスタンスです。 true は真を表す代表のオブジェクトです

class TrueClass (Ruby 2.6.0)

false は FalseClass クラスの唯一のインスタンスです。 false は nil オブジェクトとともに偽を表し、 その他の全てのオブジェクトは真です。

class FalseClass (Ruby 2.6.0)

末尾が ? のメソッドは true, false を返すべきなの? 真偽を返せばよいの?

さて、ここまで true, false と真偽値の違いを説明したのは、上記の疑問のためです

具体的には if hoge? == false という実装をコードレビュー中に見かけて、これが正しいのか正しくないのか論理的に説明が出来なかったために調べ始めました

先に自分なりの結論

末尾が ? のメソッドは true, false を返すべき
ただし、それ利用する側は、true, false 以外の真偽が帰ってくる可能性を考慮して実装すべき

その理由

Rubyのドキュメントでは、? が末尾につくメソッドは慣用的に 真偽値 を返せと言われているためです

ドキュメントを探す限り真偽値の定義は見つかりませんでしたが、おそらくは true/falseのことでしょう

  • 真偽だと全てのオブジェクトを指すことになるため、わざわざ明記する理由もないと思うので

慣用的に、真偽値を返すタイプのメソッドを示すために使われます。

Rubyで使われる記号の意味(正規表現の複雑な記号は除く) (Ruby 2.6.0)

ただし、「慣用」なので使う側は、安全側に倒したほうがよいと考えています

  • 実装者によっては偽のつもりで nil を返したり、真のつもりで true 以外の値を返すことも十分にありえるので

まとめ

  • true / false と真偽は別物
  • 偽は、falsenil。真はそれ以外のオブジェクトすべて
  • 末尾に ? がつくメソッドは真偽ではなく true / false を返すべきだけれど、それを期待しすぎないほうが安全

以上になります

おまけ

Rubyの真偽の設計思想については、以下の記事が読み応えがありオススメです

qiita.com

いまさら MacBook Air mid 2011 をサクサクにするためにやった3つのこと

これはなに

やたら重くなった MacBook Air mid 2011 (memory 4GB, strage 250GB) をサクサクにするまでの苦闘の記録です

古いMacBookに発生した、つらみあふれる現象

  • ログイン画面を開いてからパスワードを入力するまで1分ほど待たされる
  • 起動してからブラウザを開くまでやたら時間がかかる
  • 何もしていなくてもメモリを3GBくらい専有されている

上記を解決し、ネットサーフィンくらいなら何も支障がない状態になりました!

そのために、いろいろチューニングした結果をまとめます

MacBook Air mid 2011をサクサクにするためにやったこと

以下の3つです

  • ストレージの空き容量確保
  • CPUを無駄に食うプロセスの整理
  • memoryを無駄に食うプロセスの整理(主にChrome)

ストレージの空き容量確保

なぜ空き容量確保が必要か?

もし、ストレージの空きが1GBを切っている場合、メモリ不足時に使用する仮想メモリ(swap)が確保できず、処理が遅くなったり、アプリが強制終了してしまう場合があります

そのため、空き容量を程度確保して、仮想メモリに余裕をもたせておいたほうが良いです

そうでなくても250GBってあんまり余裕ありませんしね...

空き容量を確保するためにやったこと

以下の3つです

  • 写真をgoogleフォトに移動
  • 音楽をgoogle play musicに移動
  • (エンジニア向け) brew cleanup -s を実行

google様にデータを握られるのは好き嫌いはあると思いますが、私はストレージの空き容量の確保と、スマホと複数マシンで写真/音楽が共有されるメリットを取りました

写真をgoogleフォトに移動

googleフォトヘルプに従って操作すれば何も難しいことはありません

support.google.com

しかし、写真アプリで写真を削除した後も、ユーザーディレクトリのピクチャに 写真 Library.photoslibrary というファイルが残る場合があります

これが数GBの容量を食っていることがあるので、こちらも削除しておきましょう

f:id:kasei_san:20190217133659p:plain
写真 Library.photoslibraryはここにあります

音楽をgoogle play musicに移動

こちらも写真と同様にヘルプに従えば移行できます

support.google.com

brew cleanup -s を実行

homebrew のキャッシュファイルをクリアするコマンドです

私の環境では約50MBしか削減されませんでしたが、環境によっては数十GBほどキャッシュされている場合があるようです

CPUを無駄に食うプロセスの整理

効果の高い順に以下の4つを実施しました

  • SMCをリセットする
  • Spotlight関係のプロセスのCPU負荷を下げる
  • windowServerのCPU負荷を下げる
  • spindumpの無効化

SMCをリセットする

SMCとは「システム管理コントローラ」というPCのローレベルの機能のコントローラーです

SMCには、CPUの温度が上がると自動的にCPUの性能を下げて熱暴走を抑える機能があります

しかし、誤動作でCPUの温度が高くないにもかかわらず、CPUの性能を下げてしまう場合があるようです

公式サポートでは、その場合SMCをリセットすることが推奨されています

support.apple.com

電源をOFFにした状態で shift + control + option と電源キーを10秒間押しましょう

電源キーを押しますが、電源が入ることはありません。何も起こらないな? と不安にならなくて大丈夫です

Spotlight関係のプロセスのCPU負荷を下げる

アクティビティモニタの「CPU」タブを見ていたところ、mds_stores というプロセスがCPUを占拠していました(常に25%くらい)

調べたところ、Spotlightで使用するプロセスのようです

私の環境ではAlfredを使用しており、Spotlightは未使用のため、おとなしくしてもらいました

Spotlightでは、検索対象のディレクトリを常にチェックするプロセスが走っています

それを黙らせるには、検索対象をゼロにする必要があります

システム環境設定 -> Spotlight から、それぞれ設定します

f:id:kasei_san:20190217135306p:plain
検索結果のすべてのチェックを外す

f:id:kasei_san:20190217135341p:plain
Spotlightの検索から除外する場所 に Macintosh HD を追加

windowServerのCPU負荷を下げる

windowServerは、画面に表示されるウインドウを管理するプロセスです

MacなのにwindowServer?ってなりますよね...

システム環境設定から、windowServerにてCPUを食う処理を無効化することで、CPU負荷を下げることができます

無効化の方法は以下をご参考ください(Yosemite時代の記録ですが、現行のOSでも設定は大差ありませんでした)

ringosuki.hateblo.jp

spindumpの無効化

spindumpもCPUを占拠していることがあったので、こちらに従い停止しました

blog.monophile.net

spindumpは実行中のアプリケーションの低レイヤ情報を書き出すためのソフトウェアだが、 得られる情報は一般的な利用目的(ソフトウェア開発以外)では役に立たないことが多いと思う。

とのことです

memoryを無駄に食うプロセスの整理

不要なプロセスは起動しないようにしたのと、一番メモリを食うgoogle Chromeの整理を行いました

未使用のアプリをログイン時に起動しないようにする

システム環境設定->ユーザーとグループ->ログイン項目 から、ログイン時に起動するアプリを設定できます

f:id:kasei_san:20190217150758p:plain
未使用のアプリは起動しないようにしましょう

google Chromeの使用メモリ削減

タスクマネージャーを使用して、メモリを食う機能拡張を特定しました

また、広告ブロッカーを導入することで、広告に使われるメモリを無くすことに成功しました

タスクマネージャーの起動方法

画面右上のメニューから その他のツール -> タスクマネージャー でタスクマネージャーを起動させることができます

これにより、メモリやCPUを食う機能拡張やサブフレームを探すことができます

メモリを食う割に使っていない機能拡張は削除しましょう

f:id:kasei_san:20190217151137p:plain
タスクマネージャーの起動方法

f:id:kasei_san:20190217151223p:plain
メモリを食う割に使っていない機能拡張は削除しましょう

Adblock plusの導入

機能拡張 Adblock plusを導入することで、広告やトラッキングのに使われるiframeの起動が抑制され、結果使用メモリが削減されます

広告ブロックは賛否ありますが、自分環境ではブロックしないと文化的なネットサーフィンが難しいそうでしたので導入しました...

chrome.google.com

広告やトラッキングに使用されるiframeは意外にメモリを食っており、例えばニコニコ大百科の場合、 Adblock plusを導入するとで使用メモリが200MBほど削減されます

(ここで表示されている「サブフレーム」というのがiframeのことですね)

f:id:kasei_san:20190217151700p:plain
Adblock plus導入前

f:id:kasei_san:20190217151714p:plain
Adblock plus導入後

まとめ

適切なチューニングをしたところ MacBook Air mid 2011 でも普通に動作するようになりました...!

MacBook Airを購入してから、日が経つごとにどんどん重くなっていって、OSアップデートごとに無駄に重くして買い替えを促進するAppleの陰謀か...などと思っていたのですが...

みなさんも定期的なチューニングで快適なMacBookAirライフをお過ごしください!

Varnish 2.1のサブルーチンの流れ

こちらの続き

blog.kasei-san.com

VarnishのSaaSであるfastlyでは、VCLがVarnish2.1準拠 そのため、Varnish2.1での処理の流れを改めて把握して、図に書いた

Varnish 2.1のサブルーチンの流れ

エラーやrestatなど、正常処理以外の流れは端折っているので注意

f:id:kasei_san:20190129224334p:plain

ポイント

  • 6.0 よりだいぶシンプル
  • return する値も結構異なっている
  • vcl_hash は、req.hash+= で hash のキーを定義する

参考

👇 v2.1 のサブルーチンの解説

varnish-cache.org

VarnishとVCLおぼえがき

Varnish とは

VCLについて

  • 手続き型言語
  • これだけでIP制限やbasic認証をかけられたりと意外に器用
  • 特定の処理ごとに、VCLに定義されたサブルーチンが呼びだされる(後述)

Varnishのcacheについて

  • cacheは hash と呼ばれる領域に格納される
  • すべてオンメモリなので高速(らしい
  • デフォルトでは、hashのキーはリクエストのURLとドメイン/IPアドレス
    • それとオリジンサーバの Varyヘッダ (後述) の組み合わせをキーにしてcacheが行われる
  • 後述する vcl_recv でcacheの可否。vcl_hash でhashのキーの定義ができる

Varyヘッダについて

  • キャッシュサーバに対して、どのようにcacheを分けるかを指示するための情報
  • オリジンサーバからのレスポンスヘッダに格納されている
  • 例えば Vary: Accept-Encoding ならば、Varnish側のhashキーが同一でも Accept-Encoding が異なるRequestは別の情報としてcacheされる

VCLでよく使う関数

  • include: 別のvclファイルをincludeする
  • set: 変数に値を格納する
  • unset: 変数の値を削除する
  • call: サブルーチンの呼び出し

VCLでよく使われる変数

サブルーチンによって、使えたり使えなかったりするので注意

  • client : クライアントの情報
  • server : サーバの情報
  • req : クライアントからのリクエスト
  • bereq : サーバに実際送るリクエスト
  • beresp: サーバから帰ってきたレスポンスヘッダ
  • resp: クライアントに返すレスポンスヘッダ

Varnishの処理の流れ

  • Requestを受け取った時や、hashのキーを決定するときなど、それぞれのタイミングで サブルーチン が呼ばれる
  • その サブルーチン を定義することで、独自の設定をVarnishに加えることができる
  • サブルーチンの流れは、こちらの公式ドキュメントを参照

ざっくりした流れ

vcl_recv

  • Requestを受けると最初に呼ばれるサブルーチン
    • 主に、hash , pass , pipe のいずれかを return の引数に入れる
    • hash : cacheする場合に選択。次はhashのキーを確定する vcl_cache が呼びだされる
    • pass : cacheしない場合に選択。次はpassした場合に呼び出される、 vcl_pass が呼び出される
    • pipe: 処理をパイプ(キャッシュサーバは何もせず、クライアントとサーバの通信をパイプするだけ)する場合に選択。次はpipeした場合に呼び出される、 vcl_pipe が呼び出される

vcl_pipe

  • vcl_recvreturn(pipe) したら来る
  • あんまりいじることはない。長い処理の場合、タイムアウト値を変更したりする

vcl_pass

  • vcl_recvreturn(pass) したら来る
  • passの場合だけリクエストヘッダをいじったりしたいときにはここに追記する
  • return(fetch) すれば、オリジンサーバに問い合わせをする直前のサブルーチン vcl_backend_fetch が呼び出される

vcl_hash

  • vcl_recvreturn(hash) したら来る
  • hashのキーを定義する
  • Varnishでは、cacheされたデータは hash に格納される
  • vcl_hash では、そのhashを取り出すためのキーを定義する
  • 関数 hash_data に値を追加していって、その値の組み合わせがキーになる
  • 最後に return (lookup) する
  • この後は、キーにhitするcacheがあれ vcl_hit 。無ければ、vcl_miss が呼び出される

参考

この場合、リクエストURLと、ホスト(もしくはipアドレス)の組み合わせがhashのキーとなっている

sub vcl_hash {
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    return (lookup);
}

ここに、webサービスの特性に応じてhash_dataを設定していく

  • スマホとPCでcacheを別にするとか
  • ログインユーザ毎に異なるcacheを表示するとか

vcl_hit

  • cacheがヒットした場合に呼び出される
  • return(deliver) すれば、vcl_deliver (cacheをクライアントにわたす直前のサブルーチン) が呼び出される
  • return(miss) すれば、vcl_miss (cacheがヒットしなかった場合のサブルーチン) が呼び出される

vcl_miss

  • cacheがヒットしなかった場合に呼び出される
  • return(fetch) すれば、オリジンサーバに問い合わせをする直前のサブルーチン vcl_backend_fetch が呼び出される

vcl_backend_fetch

  • オリジンサーバに問い合わせる直前のサブルーチン
  • Requestを加工したりするのはここ
  • return(fetch) すれば、オリジンサーバに問い合わせた後、vcl_backend_response が呼び出される

vcl_backend_response

  • オリジンサーバに問い合わせた後のサブルーチン
  • Responseを加工したりするのはここ
  • return(deliver) すれば、vcl_deliver (cacheをクライアントにわたす直前のサブルーチン) が呼び出される

vcl_deliver

  • クライアントに配信する直前のサブルーチン
  • vcl_hitvcl_backend_response の後に呼び出される
  • return(deliver) すると、クライアントにcacheが渡される

参考

👇 弊社blog記事。最初に読むと概要をつかみやすい

techblog.lclco.com

👇 v3.0.1 時代に作られたわかりやすい同人誌

blog.xcir.net

👇 公式ドキュメントのサブルーチンの流れの解説

varnish-cache.org

👇 公式ドキュメントのサブルーチンの解説

varnish-cache.org

PostgreSQL9.3で同期レプリケーションしてみる

これの続き

blog.kasei-san.com

やったこと

  1. EC2環境を2台構築
  2. マスタの環境構築
  3. スタンバイの環境構築
  4. マスタでレプリケーションステータスを確認
  5. マスタが更新されるとスタンバイが更新されることを確認

EC2環境を2台構築

  • web UI から Amazon linux の t2.small を作成
  • セキュリティグループで、SSHと、PostgreSQLを許可

環境のIP

  • マスタ: 172.31.59.13
  • スタンバイ: 172.31.57.96

マスタの環境構築

概要

  1. PostgreSQLのインストール
  2. DB、テーブルを作成して、動作確認用のデータを挿入
  3. 他のサーバからアクセスするため、postgres ユーザにパスワードを設定
  4. 同期レプリケーションのための設定を postgresql.conf に設定
  5. WALアーカイブディレクトリを作成
  6. 他サーバから postgres ユーザでアクセスするために、pg_hba.conf を設定
  7. PostgreSQLサーバ再起動

PostgreSQLのインストール

$ wget https://download.postgresql.org/pub/repos/yum/9.3/redhat/rhel-6-x86_64/pgdg-ami201503-93-9.3-3.noarch.rpm
$ sudo rpm -ivh pgdg-ami201503-93-9.3-3.noarch.rpm
$ sudo yum -y install postgresql93-server.x86_64
$ sudo chkconfig postgresql-9.3 on
$ sudo service postgresql-9.3 initdb -E UTF-8 --no-locale
$ sudo service postgresql-9.3 start

DB、テーブルを作成して、動作確認用のデータを挿入

$ sudo su - postgres
$ createdb mydb
$ psql mydb
CREATE TABLE weather (
    city            varchar(80),
    temp_lo         int,           -- 最低気温
    temp_hi         int,           -- 最高気温
    prcp            real,          -- 降水量
    date            date
);
INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');

他のサーバからアクセスするため、postgres ユーザにパスワードを設定

$ sudo su - postgres
$ psql
alter role postgres with password 'password';

本当はもっと強固なパスワードにすべき

同期レプリケーションのための設定を postgresql.conf に設定

デフォルトの設定との差分は以下の通り

$ diff /var/lib/pgsql/9.3/data/postgresql.conf.bak /var/lib/pgsql/9.3/data/postgresql.conf
59c59
< #listen_addresses = 'localhost'               # what IP address(es) to listen on;
---
> listen_addresses = '*'                # what IP address(es) to listen on;
159c159
< #wal_level = minimal                  # minimal, archive, or hot_standby
---
> wal_level = hot_standby                 # minimal, archive, or hot_standby
188c188
< #archive_mode = off           # allows archiving to be done
---
> archive_mode = on             # allows archiving to be done
190c190,191
< #archive_command = ''         # command to use to archive a logfile segment
---
> archive_command = 'test ! -f /usr/local/pgsql/backup/%f && cp %p /usr/local/pgsql/backup/%f'
>                               # command to use to archive a logfile segment
206c207
< #max_wal_senders = 0          # max number of walsender processes
---
> max_wal_senders = 3           # max number of walsender processes
215c216
< #synchronous_standby_names = ''       # standby servers that provide sync rep
---
> synchronous_standby_names = 'slave'   # standby servers that provide sync rep

それぞれの設定値の説明

  • listen_addresses : 外部からのアクセスを許可する設定(* ですべてのIPからのアクセスを許可)
  • wal_level : WALの保存レベルの設定
  • archive_mode : on にすると、WALアーカイブプロセスが起動するようになる
  • archive_command : WALアーカイブ時に実行されるコマンド
  • max_wal_senders : WALを外部に送信するプロセスの最大数
  • synchronous_standby_names : 同期スタンバイの名前(あとでスタンバイ側でも定義する)

WALアーカイブディレクトリを作成

$ sudo mkdir -p /usr/local/pgsql/backup/
$ sudo chown postgres:postgres /usr/local/pgsql/backup

他サーバから postgres ユーザでアクセスするために、pg_hba.conf を設定

  • 接続方法/ユーザ/DB毎の認証方法を定義するファイル
  • HBAとは、host-based authentication: ホストベース認証の略
  • フォーマットは、 接続方法\t接続するDB\tuser\tIPアドレス\t認証方法

詳しくは公式ドキュメント → pg_hba.confファイル

デフォルトの設定との差分は以下の通り

$ diff /var/lib/pgsql/9.3/data/pg_hba.conf /var/lib/pgsql/9.3/data/pg_hba.conf.bak
90,91d89
< host    replication     postgres        172.16.0.0/12            md5
< host    all     postgres        172.16.0.0/12            md5

レプリケーション用の設定(1行目)と、普通にDBにアクセスする設定(2行目)も追加

  • 接続するDBreplication はレプリケーション用の特別な設定
  • 認証方法 md5 はパスワード認証

PostgreSQLサーバ再起動

$ sudo service postgresql-9.3 restart

スタンバイ環境構築

概要

  1. PostgreSQLのインストール
  2. マスタのベースバックアップを取得
  3. recovery.conf にスタンバイサーバの設定を追記
  4. postgresql.conf にホットスタンバイの設定を追記
  5. PostgreSQLサーバを起動

PostgreSQLのインストール

$ wget https://download.postgresql.org/pub/repos/yum/9.3/redhat/rhel-6-x86_64/pgdg-ami201503-93-9.3-3.noarch.rpm
$ sudo rpm -ivh pgdg-ami201503-93-9.3-3.noarch.rpm
$ sudo yum -y install postgresql93-server.x86_64
$ sudo chkconfig postgresql-9.3 on

マスタのベースバックアップを取得

直接 $PG_DATAを上書き

$ sudo su - postgres
$ rm -rf $PGDATA
$ pg_basebackup -h 172.31.59.13 -D $PGDATA -x -P -U postgres -R
  • パスワードを聞かれるので、先程設定したパスワードを入力する
  • -R オプションで、後述の recovery.conf を自動生成してくれる

recovery.conf にスタンバイサーバの設定を追記

スタンバイサーバの設定は、 recovery.conf に書く

なんで recovery.conf に書くかというと、どうやら、PostgreSQLのレプリケーションとは、マスタデータから来たデータを元にDBをリカバリし続けるという方法のため、リカバリの設定を書く所に書くようになったっぽい

$ sudo su - postgres
$ vim /var/lib/pgsql/9.3/data/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=postgres host=172.31.59.13 port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres application_name=slave'
  • primary_conninfo はDBに接続する時の接続文字列を定義
    • pg_basebackup で自動生成された内容に加えて、同期レプリケーションのため、master の postgresql.conf で定義した application_name=slave を追記
  • primary_conninfo について、詳しくは → データベース接続制御関数#接続文字列

postgresql.conf にホットスタンバイの設定を追記

hot_standby = on だけ追記

  • これにより、スタンバイでも読み込みだけ可能になる

PostgreSQLサーバを起動

$ sudo service postgresql-9.3 start

レプリケーションステータスを確認

マスタ側で実行

$ sudo su - postgres
$ psql
postgres-# \x
Expanded display is on.

postgres=# SELECT * FROM pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid              | 356
usesysid         | 10
usename          | postgres
application_name | slave
client_addr      | 172.31.57.96
client_hostname  | 
client_port      | 34172
backend_start    | 2019-01-01 09:40:25.070405+00
state            | streaming
sent_location    | 0/30002A8
write_location   | 0/30002A8
flush_location   | 0/30002A8
replay_location  | 0/30002A8
sync_priority    | 1
sync_state       | sync
  • 172.31.57.96 と、同期 sync_state: sync のレプリケーションができていることが確認できた

マスタが更新されるとスタンバイが更新されることを確認

スタンバイでテーブルをSELECT

$ sudo su - postgres
$ psql mydb
mydb=# SELECT * FROM weather;
     city      | temp_lo | temp_hi | prcp |    date    
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
(1 row)

マスタでテーブルを更新

$ sudo su - postgres
$ psql mydb
mydb=# INSERT INTO weather VALUES ('Tokyo', 46, 50, 0.25, '1994-11-27');
INSERT 0 1

スタンバイでテーブルをSELECT

mydb=# SELECT * FROM weather;
     city      | temp_lo | temp_hi | prcp |    date    
---------------+---------+---------+------+------------
 San Francisco |      46 |      50 | 0.25 | 1994-11-27
 Tokyo         |      46 |      50 | 0.25 | 1994-11-27
(2 rows)

カラムが追加された! 👏

スタンバイでテーブルを更新

mydb=# INSERT INTO weather VALUES ('hoge', 46, 50, 0.25, '1994-11-27');
ERROR:  cannot execute INSERT in a read-only transaction

エラーになることを確認

PostgreSQLのバックアップ覚えがき

PostgreSQLのバックアップは3種類

  • コールドバックアップ
  • ホットバックアップ
  • オンラインバックアップとポイントインタイムリカバリ(PITR)

コールドバックアップ

特徴

  • 物理バックアップとも言われる
  • $PG_DATA のファイルをまるまるコピーする方法

メリット

  • 方法がシンプルなので、手順もシンプル
  • ホットバックアップより、バックアップもリストアも高速

デメリット

  • DBの停止が必要
  • 全DBのバックアップしかできない
  • バックアップしたところまでしか、リストアできない

手順

rsync を利用する(特にPostgreSQL由来のコマンドは使用しない)

  • それぞれ、PostgreSQLを停止してから行うこと

バックアップ

  • $BACKUP は、バックアップファイル格納PATH
$ async -av $PGDATA/ $BACKUP/pgdata

リストア

$ rm -rf $PGDATA
$ async -av $BACKUP/pgdata $PGDATA/ 

ホットバックアップ

特徴

  • 論理バックアップとも言われる
  • 現状のDBをSQLに変換して保存する

メリット

  • DBを停止せずにバックアップが可能
  • DBやテーブル単位のバックアップが可能
  • コマンド1つで実行可能なため手順がシンプル

デメリット

  • コールドバックアップより、バックアップもリストアも時間がかかる
  • バックアップしたところまでしか、リストアできない

手順

pg_dump コマンドを実行する

  • pg_dumpコマンドは以下の形式でdumpが可能
    • プレーンテキスト(SQL)
    • カスタムアーカイブ(圧縮したバイナリ)
    • ディレクトリ(テーブル単位で圧縮したバイナリ)
    • TAR形式(テーブル単位のバイナリ)
  • カスタムアーカイブが、サイズが小さいけど、いざとなったらSQLに変換もできるのでオススメ

バックアップ

リモートからもバックアップ可能

  • オプション -Fc はカスタムアーカイブ形式の指定
$ pg_dump -Fc ${database_name} -U ${user_name} -h ${host_name} -f $BACKUP/dump.custom

リストア

$ pg_restore -d ${database_name} $BACKUP/dump.custom

オンラインバックアップとポイントインタイムリカバリ(PITR)

特徴

  • バックアップデータと、元DBのWALを使用してリカバリを行う
  • PITRとは、オンラインバックアップから現在までの任意の時間の状態でリカバリできる仕組み(すごい!

メリット

  • オンラインバックアップ開始〜最新状態の任意の時刻の状態に復旧可能

デメリット

  • 手順/運用が複雑(サードパーティツールである程度緩和可能)
  • WALアーカイブが必要
    • その分CPUリソースも、ストレージも食う
  • 元DBのWALアーカイブと、WALが必要
    • WALは必要が無くなると、別の情報に上書きされてしまう
    • そのため、不要になったWALをアーカイブ領域に保存する必要がある → それがWALアーカイブ

手順

ポイント

  • ベースバックアップと、マスタにあるWALアーカイブの組み合わせで復旧を行う

手順

(PostgreSQLを停止した状態で実施する)

  1. WALアーカイブを保存するように設定
  2. ベースバックアップの実行
  3. ベースバックアップからの復旧
  4. recovery.conf の設定(PITRの設定)

WALアーカイブを保存するように設定

postgresql.conf に以下の設定をする

wal_level = archive
archive_mode = on
archive_command = 'test ! -f /usr/local/pgsql/backup/%f && cp %p /usr/local/pgsql/backup/%f'
  • wal_level : WALの保存レベル。以下の3種類がある
    • minial : クラッシュまたは即時停止から回復するのに必要な情報のみ書き出す(デフォルト)
    • replica : WALアーカイブに対応。ストリーミングレプリケーションに必要な情報も追加
      • 9.6の時に、archivehot_standby という別々の設定が統一された
    • logical : ロジカルレプリケーションをサポートするのに必要な情報も追加
  • archive_mode : WALアーカイブを許可する設定。デフォルトOFF
    • on にすると、アーカイブプロセスが立ち上がるようになる
  • archive_command : WALを破棄する時に実行されるコマンド
    • %p はバックアップすべきWALのパス。 %f はWALファイル名
    • 自前でコマンドを用意する必要があるんだ...

ベースバックアップの実行

$ pg_basebackup -D $BACKUP -F t -x -z
  • -F: ファイルフォーマットの指定。t はtar形式の保存。デフォルトは p でプレーンテキストで保存される
  • -z : 出力ファイルをzip圧縮する
  • -x : ベースバックアップ作成中、出力ファイルの更新を止める(メモリを食うので注意

ベースバックアップからの復旧

既存のデータからWALだけ避難

$ mv $PGDATA/pg_wal/ $BACKUP_WAL

既存のデータは不要なので移動

$ mv $PGDATA/pg_wal/  ${適当なバックアップ先}

ベースバックアップからの復旧

(tar.gz を解凍してから)

$ cp $BACKUP/pgdata/* $PGDATA

WALを最新のものに変更

(ベースバックアップのWALは古いので消す)

$ rm -rf  $PGDATA/pg_wal/
$ cp $BACKUP_WAL $PGDATA/pg_wal

recovery.conf の設定

restore_command = 'cp /usr/local/pgsql/backup/%f %p'
recovery_target_time = '2018/12/01 00:47:00'
  • restore_command : archive_command と逆に、WALアーカイブからcopyするコマンドを設定
  • recovery_target_time : PITRしたい場合、その時刻を設定する
    • PITRが不要な場合はこの行は不要

参考

www.slideshare.net

WEB+DB PRESS Vol.108

WEB+DB PRESS Vol.108

  • 作者: 中野暁人,山本浩平,大和田純,曽根壮大,ZOZOTOWNリプレースチーム,権守健嗣,茨木暢仁,松井菜穂子,新多真琴,laiso,豊田啓介,藤原俊一郎,牧大輔,向井咲人,大島一将,上川慶,末永恭正,久保田祐史,星北斗,池田拓司,竹馬光太郎,粕谷大輔,WEB+DB PRESS編集部
  • 出版社/メーカー: 技術評論社
  • 発売日: 2018/12/22
  • メディア: 単行本
  • この商品を含むブログを見る

物理バップアップの概要 | Let's Postgres

blog.poppypop.mydns.jp

pg_basebackup

19.5. ログ先行書き込み(WAL)