Your SlideShare is downloading. ×
Inside vacuum - 第一回PostgreSQLプレ勉強会
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Inside vacuum - 第一回PostgreSQLプレ勉強会

1,227
views

Published on

Published in: Engineering

0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,227
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
20
Comments
0
Likes
5
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. VACUUM解析資料 第28回PostgreSQLしくみ+アプリケーション勉強会 2014/02/01 澤田雅彦、江川大地
  • 2. 目次 0. はじめに ∼プレ勉強会のご紹介 1.VACUUMとは 2.VACUUMの進化 3.VACUUM内部処理の流れ 4. CONCURRENTVACUUMの詳細 5. FULLVACUUMの詳細 2
  • 3. はじめに ∼まずは自己紹介から ✦名前 澤田雅彦(twitter: @sawada_masaho) ✦ソースコードの読み方 emacs + etags ✦近況 •chefと格闘しました。 •gitのソースコードを読んでます。 ! ✦名前 江川大地(twitter: @daiti0804) ✦ソースコードの読み方 vi + ctags ✦近況 • 転職しました。 • シアトルに行ってきました。 3
  • 4. はじめに ∼プレ勉強会のご紹介 4 プレ勉強会とは、しくみ分科会の前に集まり、PostgreSQLのコアに限定 した内容を学んでいこうという趣旨の勉強会です。 ★勉強会の場 ■ 日時:しくみ分科会+アプリケーション分科会の開催同日 ■ 場所:しくみ分科会+アプリケーション分科会と同じ ■ 勉強内容:ソースコードをはじめとするPostgreSQLのコア ! ★進め方 1. 主導する人と勉強する内容を決定 2. 主導する人を中心に次回の勉強会の準備 (ソースコードリーディングを例にすると) 2.1. 主導する人がソースコードを読む 2.2. 詰まった部分や進 状況をMLでシェア。 2.3. MLに参加している人がサポート。 2.4. プレ勉強会で話す資料の作成 3. プレ勉強会での発表
  • 5. 勉強内容の例 • PostgreSQLソースコードリーディング ‣ PostgreSQLコアのソースコードを読み、実装方法を共有。 • PostgreSQL機能追加 ‣ プレ勉強会で、PostgreSQL本体に機能追加する案を出して、議論する。実 装する。 • PostgreSQLパッチレビュー ‣ CommitFestに出されているパッチをレビュー。その機能やレビューないよ うをプレ勉強会で共有。 • PostgreSQL放置モジュールのアップデート ‣ textsearch-jaなど、PostgreSQLのアップデートに対応していないツールの アップデート。ツールやアップデート内容を共有。 5
  • 6. ソースコードリーディング案 1つの案として、井久保さんのソースコード解析資料(http://ikubo.x0.com/PostgreSQL/ pg_source.htm)をアップデートする形で進めていこうと考えています。 No テーマ 説明 担当 1 ソースツリーとPostgreSQLのアーキテクチャ ソースコードの概要とPostgreSQLのアーキテクチャ 2 初期DBの作成処理(Bootstrap) 主に initdb で実行される内容 3 ストレージマネージャ smgr, file, page, buffer, freespace 4 メモリ管理 utils/mmgr 5 postmaster postmaster の処理フロー、シグナル処理など 6 postgresプロセスの概要 postgresプロセスの処理フロー、エラー処理など 7 プロセス間通信(IPC) 共有メモリ、シグナル、共有 inval メッセージ 8 MVCC MVCCの動作および実装方法について 9 node 構造 node 構造の説明 10 字句解析と構文解析 字句解析器と構文解析器の説明。簡単にflexとbisonも解 11 トランザクションマネージャ   12 VACUUMの実装 VACUUMの実装に付いての説明。 澤田、江川 13 意味解析器(アナライザ) parse_analyze() の処理についての説明。 14 バックグラウンドライタプロセス バックグラウンドライタプロセスについて説明。 15 レプリケーション 非同期、同期、カスケードレプリケーションの実装方法 16 WAL 基本的なWALの構造。 17 ロック ロックの実装方法   18 View View MaterializedViewの実装方法 19 トリガ トリガー、イベントトリガーの実装方法 20 FDW FDWの実装方法 21 データ型 9.0以降に追加されたデータ型について :
  • 7. ソースコードの読み方の一例 (vi + ctags編) • vi + ctagsによるソースコードの読み方とは ctagsにより、ソース内に書かれているメソッドなどの呼出し元と呼び出し側を対応づけるタグファイ ルを生成し、viでソースコードを読む流儀 • 読むための準備(タグの付け方) 7 $ wget http://ftp.postgresql.org/pub/source/v9.3.2/postgresql-9.3.2.tar.gz $ tar xvfz postgresql-9.3.2.tar.gz $ ./configure --enable-debug $ make $ sudo make install $ cd postgresql-9.3.2.tar.gz/src/backend $ ctags */*.[chyl] */*/*.[ch] ../include/*.h ../include/*/*.h $ view commands/vacuum.c ※ メソッドに入るには、”Ctrl” + “]” ※ メソッドから抜けるには、”Ctrl” + “t” • 読み方 タグ付けを行ったディレクトリから読む
  • 8. ソースコードの読み方の一例 (emacs編) •etagsによるソースコードの読み方とは etagsにより、ソース内に書かれているメソッドなどの呼出し元と呼び出し側を対応づけるタグ ファイルを生成し、emacsでソースコードを読む流儀 • 読むための準備(タグの付け方) ! ! ! ! ! ! • 読み方 ! ※ M-. <関数名> でタグジャンプ ※ M-* でジャンプ元に戻る 8 $ wget http://ftp.postgresql.org/pub/source/v9.3.2/postgresql-9.3.2.tar.gz $ tar xvfz postgresql-9.3.2.tar.gz $ ./configure --enable-debug $ make $ sudo make install $ cd postgresql-9.3.2.tar.gz/src/ $ sh tool/make-etags $ emacs vacuum.c
  • 9. ソースコードの読み方の一例 (doxygen編) •doxygenによるソースコードの読み方とは doxygenとは、PostgreSQLのソースコードを読めるコンテンツです。メソッド の呼出し元、呼び出し先がリンク付けられたweb上のドキュメントを読み進 める流儀。 ! • URL : http://doxygen.postgresql.org/ ! 9
  • 10. 1.Vacuumとは
  • 11. VACUUMとは…の前に 簡単にPostgreSQLのアーキテクチャの話を… 11
  • 12. VACUUMとは…の前に PostgreSQLは追記型アーキテクチャを採用しています。 ➢データの変更があっても元のレコードを物理的に消さず、新しい行を追加 して、元のレコードを無効とマークします。 12 ID NAME 1 test1 2 test2 3 test3 ID NAME 1 test1 2 test2 3 test3 2 test2’ 3 test3’ UPDATE UPDATE tablename SET NAME=test2’ WHERE ID = 2; ! UPDATE tablename SET NAME=test3’ WHERE ID = 3; :不要領域 :ページ
  • 13. VACUUMとは VACUUMは、不要領域を再利用可能にし、その箇所をFSM(空き領域マップ)に登録するコマンド です。 13 ID NAME 1 test1 2 test2 3 test3 2 test2’ 3 test3’ VACUUM実行 VACUUMにより! 不要領域回収 :不要領域 :ページ ID NAME 1 test1 2 test2 3 test3 2 test2’ 3 test3’ ID NAME 1 test1 2 test2’ 3 test3’ VACUUM完了 再利用可能に!
  • 14. VACUUMとVACUUM FULL ✦ VACUUMは、不要領域を再利用可能な状態にします。 ✦ VACUUM FULLは、物理的にファイルを圧縮します。 ‣ 排他ロックが必要なため、VACUUM FULL中はテーブルへアクセス不可 14 :不要領域 :ページ ID NAME 1 test1 2 test2 3 test3 2 test2’ 3 test3’ VACUUM VACUUM FULL ID NAME 1 test1 2 test2’ 3 test3’ ID NAME 1 test1 2 test2’ 3 test3’ 物理的に! サイズ縮小 SHARE UPDATE! EXCLUSIVE ACCESS! EXCLUSIVE
  • 15. 2.Vacuumの進化
  • 16. VACUUMの進化 PostgreSQLのアップグレードとともにVACUUMも進化しています。特 に8系では様々な改良が入り、PostgreSQLの運用性向上の要素となっ ています。 16 8.4 9.0 VACUUM FULL高速化! (新VACUUM FULLの実装) ~ 8.2 VACUUMの! 基礎固め 9.2~ FSM自動管理! Visibility Mapの導入 8.3 autovacuumの改良! HOTの導入 9.1 VACUUM FULL VERBOSE! ログの改良
  • 17. autovacuumの改良 autovacuumは定期的にテーブル状態を監視し、必要があれば自動でVACUUMやANALYZE を実施する機能です。8.2までは1プロセスしか割り当てられませんでしたが、8.3以降、複数 プロセスを割り当てる(autovacuum_max_workers)ことができるようになりました。 17 VACUUM VACUUM VACUUM ~ 8.2 8.3~シングルプロセス マルチプロセス 大きなテーブルに長 時間かかり、他のテーブル の不要領域がなかなか回収 されないことも… 大きなテーブルのVACUUM中も 並行して実行できる。
  • 18. HOT HOT(Heap OnlyTuple)は、テーブルデータの更新時にインデックスデータの更新をスキップで きる仕組みです。これにより、以下のメリットが生まれます。 1. インデックスデータの更新が無いため、更新処理そのものが高速化 2. 更新済みデータ(ガベージ)は、VACUUMを待たずして再利用可能領域化できる UPDATE tbl SET 在庫= 99 WHERE ID=2 ID 名前 在庫 1 A 10 2 B 8 索引 ID 名前 在庫 1 A 10 2 B 8 2 B 99 索引 ID 名前 在庫 1 A 10 2 B 8 2 B 99 索引 ~ 8.2 8.3~VACUUMが実行され るまで不要領域は再利 用不可 インデックスも更新される VACUUMを待たずに不要 領域を再利用可能! →不要領域の増加を抑制 転送エントリを利用すること で、インデックスの更新を回避。 これにより、VACUUMが改修すべきガベージの量を大幅に削減でき、これによ り頻繁に更新が走るテーブルへのVACUUM回数が削減。
  • 19. Visibility Map Visibility Map(VM)は、テーブルのどの部分に不要領域があるかを記録しているマップファイ ルです。各テーブル毎に持っています。これにより、VACUUMは、VACUUMすべき箇所を特 定して処理を実施します。 VACUUM VACUUM ~ 8.3 8.4~テーブル テーブルVM VACUUMの必要の ない部分もスキャン。 VMを見て、必要な 箇所のみスキャン。 これにより、VACUUMのスキャン範囲が局所化され、実施時間の短縮やI/O削減が期待できま す。特に、ガベージがテーブルの一部分に集中している場合にはその効果が明確に現れます。
  • 20. Free Space Map(FSM)自動管理 FSMは、VACUUMによる不要領域が除去され、再利用可能になった箇所を記録するマップ ファイルです。8.3まではDBクラスタにひとつ存在し、ユーザがサイズを指定(max_fsm_pages) する必要がありました。8.4からはテーブル毎にFSMを持ち、自動で管理されています。 VACUUM VACUUM ~ 8.3 8.4~ 再利用OK ブランク ブランク ブランク 再利用OK 再利用OK 再利用OK 再利用OK テーブル テーブル FSM (DBクラスタで1つ) FSM (テーブルで1つ) NG 空き領域として 登録 max_fsm_pagesを超 えると記録不可 FSMは自動で
 必要な分を記録 再利用され ず、肥大化 の原因に 従来はFSMサイズにガベージ回収に必要十分な量を指定しておかなければ、VACUUMを実施 してもブランク領域が増え続けテーブルの肥大化が発生していました。今では必要な分を自 動で記録するように改善されています。
  • 21. 3. VACUUM内部処理の流れ
  • 22. VACUUMソースコードのありか ✦VACUUMのソースコード •src/backend/commands配下 vacuum.c:VACUUM,VACUUM FULLの流れ vacuumlazy.c:VACUUMの処理 cluster.c:VACUUM FULLの処理 ! ✦VACUUMに関係する機能のソースコード •Visibility Map access/heap/visibilitymap.c ! •フリースペースマップ(FSM) storage/freespace/freespace.c 22
  • 23. VACUUMの概要 23 vacuum() vacuum_rel() 対象のリレーションをリストアップ 対象のリレーション一つずつに対して以下を繰り返す VACUUMまたはVACUUM FULLの場合 ANALYZEの場合 analyze_rel() リレーションにロックの取得 権限のチェック VACUUM FULLの場合 VACUUMの場合 lazy_vacuum_rel() cluster_rel() VACUUM FULL :AccessExclusiveLock VACUUM : SharedUpdateExclusiveLock
  • 24. 4. CONCURRENT VACUUMの詳細
  • 25. CONCURRENT VACUUM概要 ✦オプションなしでVACUUM を実行した場合の処理です。 ! ! ✦テーブルのページ単位での不要領域の回収とそのインデックスの削 除を行います。 ! ✦処理中は、テーブルの排他的ロックを取得しないため、通常のデー タベース処理と並行して実行することができる。 25 postgres=# VACUUM;
  • 26. CONCURRENT VACUUMのメカニズム ✦CONCURRENTVACUUMは4つの段階を経て実施します。 ! 1. 不要領域の列挙 maintenance_work_memに格納可能な分、ヒープページを読み込み、不要領域の一覧を書き出します。 2. VACUUM対象タプルのインデックス・エントリ削除 全インデックスページを読み込み、不要領域へのエントリを除去し、書き出します。 3. ページごとの不要領域除去(lazy_vacuum_page) VACUUM 対象ページを読み込み、不要領域を除去し、書き出します。 4. テーブル末尾の切り詰め テーブル末尾に有効な行が無い場合、その領域を切り詰め、ファイルを縮小します。 26
  • 27. lazy_vacuum_rel() lazy_scan_heap() lazy_vacuum_heap() lazy_vacuum_page() 2.インデックス・エントリ削除 3. ページごとの不要領域除去 ↓対象のリレーション一つずつに対して呼ばれる lazy_vacuum_index() 1.不要領域の列挙 4.テーブル末尾の切り詰め lazy_truncate_heap() 27 ブロックごとにループ
  • 28. CONCURRENT VACUUMのソースコード lazy_vacuum_rel() - vacuum_set_xid_limits():実行中の全トランザクション中での一番古い xmin の取得と、FREEZE する XID のカットオフポイントの計算を行う。 - LVRelStats構造体: (VACUUM情報保持用) の初期化 - vac_open_indexes:リレーションが保有しているインデックスをオープンする(RowExclusiveLock)。 - lazy_scan_heap:vacuumの実行。 - lazy_space_alloc() : 不要タプル格納領域の確保。(max_fsm_pagesの設定値は8.4以降削除) ✦ [不要タプルが一杯の場合] - lazy_vacuum_index():インデックス・エントリ削除 - lazy_vacuum_heap():lazy_vacuum_page()呼び出し後、テーブル末尾の切り詰め。 - lazy_vacuum_page() :不要領域除去 ✦ [不要タプルが一杯でない場合] - 不要領域の列挙 - lazy_vacuum_index() : インデックス・エントリ削除 - lazy_vacuum_heap() : lazy_vacuum_page()呼び出す。 - lazy_vacuum_page() : 不要領域除去 - vac_close_indexes : インデックスをClose(vacuum.cを参照) - lazy_truncate_heap(): ファイル末尾を切り詰め。scan_allしている場合 - FreeSpaceMapVacuum():FSMのvacuum - vac_update_relstats : 統計情報の更新 - pgstat_report_vacuum:stats collectorへもvacuumしたテーブルのことを送信 28
  • 29. テーブルをスキャンして、ページ 単位のVACUUM、インデックスのVACUUMを行い、スキャン中に ページ のフリースペース情報を構築していくメソッド lazy_scan_heap() ✦ lazy_scan_heap() - lazy_space_alloc() ‣ CONCURRENTVACUUMで使用する領域を確保する(max_dead_tuplesに、maintenance_work_mem 分の領域を割り当てる)。 - visibility map によるテーブルの確認 ‣ heap内のブロックを順番に確認し、ブロック上の全タプルが、誰からも可視(visible)かチェックする。可視でないブロック (next_not_all_visible_block)が見つかった時点で、ループを中断(break)する。 ‣ 可視であるブロックが、SKIP_PAGES_THRESHOLDよりも多かった場合は、skipping_all_visible_blocksにtrueを格納する(そう でない場合はfalse)。 - ブロックごとにループ(ブロックナンバーを0からインクリメントしていく) ‣ ブロックナンバーが、next_not_all_visible_blockと一致する場合 ‣ 次のnext_not_all_visible_blockを探す(ブロック上の全タプルが、誰からも可視(visible)かチェックし、可視でないブロッ クが見つかった時点で、ループを中断)。 ‣ 可視であるブロックが、SKIP_PAGES_THRESHOLDよりも多かった場合は、skipping_all_visible_blocksにtrueを格納する (そうでない場合はfalse) ‣ 上記に当てはまらない場合 ‣ VACUUM処理をスキップする(continue文)。 ‣ lazy_vacuum_index() : index_bulk_delete()を呼び出して、VACUUM対象タプルのインデックスエントリをまとめて削除する。 ‣ lazy_vacuum_heap() : lazy_vacuum_page()を呼び出して、ページごとの不要領域を除去する。 29
  • 30. テーブルをスキャンして、ページ 単位のVACUUM、インデックスのVACUUMを行い、スキャン中に ページ のフリースペース情報を構築していくメソッド lazy_scan_heap() ① lazy_space_alloc()を呼び出し、max_dead_tuplesに、maintenance_work_mem 分の領域を割り 当て、CONCURRENTVACUUMで使用する領域を確保する。 ② テーブルの先頭のブロックから順番に、visibilitymap_test()で確認していき、初めて不要領 域があるブロックを探す。 ③ (ここからブロックごとのループに入る) ブロックを順番に確認していき、不要領域がない ブロックの場合はスキップする。 ④ 不要領域があるブロックの場合、次の不要領域があるブロックを探す。 30
  • 31. lazy_scan_heap() lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true
  • 32. lazy_scan_heap() 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true next_not_all_visible_block = 3 次に不要領域が現れるのは 3ブロック目 lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。
  • 33. lazy_scan_heap() 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true next_not_all_visible_block = 3 次に不要領域が現れるのは 3ブロック目 ここはスキップ lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。
  • 34. lazy_scan_heap() 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true next_not_all_visible_block = 3 次に不要領域が現れるのは 3ブロック目 ここはスキップ lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。 ここは不要領域あり
  • 35. lazy_scan_heap() 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true ここはスキップ false true next_not_all_visible_block を設定しよう! lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。 ここは不要領域あり
  • 36. lazy_scan_heap() VACUUM 0 1 2 3 4 5 6 7 8 テーブルVM visibilitymap_test() true false true true ここはスキップ false true next_not_all_visible_block = 5 lazy_scan_heap() では、Visibility Mapを利用して、VACUUMする必要のないブロックをスキッ プします。visibilitymap_test()で、テーブルの先頭のブロックから順番に、visibilitymap_test()で 不要領域があるかチェックする。
  • 37. テーブルをスキャンして、ページ 単位のVACUUM、インデックスのVACUUMを行い、スキャン中に ページ のフリースペース情報を構築していくメソッド lazy_scan_heap() ① lazy_space_alloc()を呼び出し、max_dead_tuplesに、maintenance_work_mem 分の領域を割り当て、 CONCURRENTVACUUMで使用する領域を確保する。 ② テーブルの先頭のブロックから順番に、visibilitymap_test()で確認していき、初めて不要領域があるブ ロックを探す。 ③ (ここからブロックごとのループに入る) ブロックを順番に確認していき、不要領域がないブロックの 場合はスキップする。 ④ 不要領域があるブロックの場合、次の不要領域があるブロックを探す。 ⑤ visibilitymap_pin()で、vmのどこまでを読んだか記録する。 ⑥ ページのタプルの状態を検査する。 タプルの状態によって処理がかわる。 HEAPTUPLE_DEAD : HOT更新されたタプルでなければ、lazy_record_dead_tuple()で VACUUM対象として、そのタプルの位置を登録。 HEAPTUPLE_LIVE : oidの妥当性チェックなどを行うのみで、VACUUM対象とはしない。 その他の状態 :VACUUM対象とはしない。 ⑦ lazy_vacuum_index()で、ページ内のVACUUM対象のタプルのインデックスエントリを削除する。 ⑧ lazy_vacuum_page()で、ページ内の不要領域を除去する、 ⑨ テーブル末尾に空白がある場合は、lazy_truncate_heap()で、空白を切り詰める。 37
  • 38. 5. FULL VACUUMの詳細
  • 39. VACUUM FULLの概要 •ページをまたがった領域の切り詰めを行う •処理はCONCURRENTVACUUMに比べてシンプル •CLUSTERコマンドでの処理と似ている • postgres=#VACUUM FULL; •テーブルを指定することで、指定されたテーブルに対してのみ VACUUM FULLを行う •テーブルが指定されていない場合は全テーブルが対象 39
  • 40. vacuum fullのメカニズム •cluster.cの中でCLUSTERコマンドで使用される関数と同じ関数を使用 する •同じ関数の中で、CLUSTERコマンドでの処理との区別は、引数で渡 されるindexOidでしている •CLUSTERコマンドの場合は、CLUSTERで使用するIndexのOIDが 入っている •VACUUM FULLではインデックスは使用しないのでInvalidOID(=0)が はいっている •そのため、ソースを読むときはCLUSTERコマンドでの処理と分けな がら読んでいくことが大切 40
  • 41. 41 cluster_rel() rebuild_relation() copy_heap_data() reform_and_rewrite_tuple() rewrite_heap_tuple() raw_tuple_insert() 移動先のリレーション作成 タプルを一つずつ読み込んで、以下を繰り返し 古いタプルを元に新しいタプルを作成 新しいタプルを新しいリレーションに挿入 ↓対象のリレーション一つずつに対して呼ばれる
  • 42. DEAD DEAD DEAD DEAD 図にすると 42 DEAD DEAD 古いリレーション
  • 43. DEAD DEAD DEAD DEAD 図にすると 43 DEAD DEAD 古いリレーション 新しいリレーション rebuild_relation() 移動先のリレーションを作成
  • 44. DEAD DEAD DEAD DEAD 図にすると 44 DEAD DEAD 古いリレーション 新しいリレーション copy_heap_data() タプルを一つに読み込む
  • 45. DEAD DEAD DEAD DEAD 図にすると 45 DEAD DEAD 古いリレーション 新しいリレーション reform_and_rewrite_tuple() 新しいタプルを作成
  • 46. DEAD DEAD DEAD DEAD 図にすると 46 DEAD DEAD 古いリレーション 新しいリレーション rewrite_heap_tuple() raw_insert_tuple() 新しいリレーションに挿入
  • 47. DEAD DEAD DEAD DEAD 図にすると 47 DEAD DEAD 古いリレーション 新しいリレーション これらの処理を生きている タプルに対して行う
  • 48. VACUUM FULLのソースコード(1/4):cluster_rel() ! cluster_rel(tableOid, indexOid, recheck, verbose) { - try_relation_open()で対象のリレーションをopenする - CheckTableNotInUse() - TransferPredicateLocks(OldHeap) - rebuild_relation(OldHeap, indexOid, verbose) } —————————————————————————————————————————— rebuild_relation(OldHeap, indexOid, verbose) { - make_new_heap(tableOid, tableSpace, false,AccessExclusiveLock) - 移動先のリレーションを作成 - copy_heap_data(OIDNewHeap, tableOid, indexOid, verbose
 &swap_toast_by_content, &frozenXid, &cutoffMulti); - finish_heap_swap(tableOid, OIDNewHeap, is_system_catalog
 swap_toast_by_content, false true, frozenXid, cutoffMulti) } 48 • VACUMM FULLを実行するリレーションひとつずつに対して呼ばれる • VACUUM FULLから呼ばれる場合は、recheckは必ずfalse • CLUSTERでもこの関数を使用
  • 49. VACUUM FULLのソースコード(2/4) copy_heap_data() copy_heap_data(OIDNewHeap, OIDOldHeap, OIDOldIndex, 
 verbose, pSwapToastByContent, pFreezeXid, pCutoffMulti) { - heap_open() - NewHeap, OldHeapをOIDNewHeapとOIDOldheapを元にopen - RelationGetDescr() - newTupDesc(), oldTupDesc()を取得 - use_wal = XLogIsNeeded() && RelationNeedsWAL(NewHeap); - XLogIsNeeded() : - RelationNeedsWAL() : - vacuum_set_xid_limits() - pFreezeXid, pCutoffMultiに代入する値を取得 - VACUUM FREEZEのしきい値となるXIDを計算 - begin_heap_rewrite(NewHeap, OldestXmin, Freezexid, MultiXactCutoff, use_wal); - for(;;) - 詳細は次のスライド - heap_endscan() - end_heap_rewrite() - heap_close(OldHeap) - heap_close(NewHeap) } 49 RewriteState rs_new_rel 移動先のリレーショ ン rs_buffer 移動先リレーションの 現在のバッファ rs_use_wal WALに記録するかどうか : :
  • 50. VACUUM FULLのソースコード(3/4) copy_heap_data()の処理の一部 copy_heap_data(OIDNewHeap, OIDOldHeap, OIDOldIndex, 
 verbose, pSwapToastByContent, pFreezeXid, pCutoffMulti) { - もろもろ準備 - for(;;) - tuple = heap_getnext() - タプルの状態を示すフラグによって処理を変える - tups_vacuumed +=1 - 死んでいるタプルは、
 rewrite_heap_dead_tuple() → continue - num_tuples += 1 - reform_and_rewrite_tuple(tuple, oldTupeDesc, newTupDesc, 
 values, isnull, NewHeap->rd_rel->relhasoids, rwstate) - heap_endscan() - end_heap_rewrite() - heap_close(OldHeap) - heap_close(NewHeap) } 50 状態 isdead HEAPTUPLE_DEAD T HEAPTUPLE_RECE NTLY_DEAD F HEAPTUPLE_LIVE F
  • 51. VACUUM FULLのソースコード(4/4) copy_heap_data()の処理の一部 reform_and_rewrite_tuple(tuple, oldTupleDesc, newTupleDesc, 
 values, isnull, newRelHadOids, rwstate) { - heap_deform_tuple() - tupleのなかの情報をvaluesに展開 - heap_form_tuple() - valuesとisnullを元に新しいタプルを作成する - rewrite_heap_tuple(rwstate, tuple, copiedTuple) - rwstate : 移動先の情報が入っている - tuple : 古いタプル(この後、freeされる) - copied : tupleを元に作られた新しいタプル(新しいHeapに入れられる) - heap_freetuple(copiedTuple) } 51
  • 52. • VACUMM FULLでは元のリレーションから新しいリレーションにタ プルが移動される ! ! ! • 最近のhackersでの問題を紹介します 52
  • 53. とある日のpgsql-performanceへのメール(2012/11/29) ! <VACUUM FULLのあとIndex Only Scanをすると遅くなる> After vacuum: Index Only Scan using i on ta (cost=0.00..50882.62 rows=2018667 width=4) (actual time=0.014..193.120 rows=2000000 loops=1) Index Cond: (ca = 1) Heap Fetches: 0 ! After vacuum full: ! Index Only Scan using i on ta (cost=0.00..155991.44 rows=1990333 width=4) (actual time=0.042..364.412 rows=2000000 loops=1) Index Cond: (ca = 1) Heap Fetches: 2000000 53
  • 54. 改善するために(今まで) cluster_rel() { - reform_and_rewrite_heap() - reformする - rewrite_heap_tuple() - raw_heap_insert() - 移動先のページ1に空きがなくなったら、 - 新しいページ2を開く - 新しいページ2にタプルを挿入 : } ! ! 54
  • 55. 改善するために(現在の案) cluster_rel() { - reform_and_rewrite_heap() - reformする - rewrite_heap_tuple() - raw_heap_insert() - 移動先のページ1に空きがなくなったら、 - ページ1に入っているタプルがすべて可視(Visibule)で あるかを確認        - 可視である場合はVMを更新(visibility_map_set()) - 新しいページを開く - 新しいページにタプルを挿入 : } 55
  • 56. レビューしてみましょう 56
  • 57. 参考資料
  • 58. その他、VACUUMのあれこれ VACUUMについては、他にも様々な点が強化されています。 58 手動VACUUM、autovacuum双方でゆっくりVACUUMを行う設定が可能です。負荷の高 い時間帯においてもシステム性能への影響を最小限にできます。 性能 autovacuumは、指定時間以上かかった場合には処理内容とともにログ出力する機能が あります。問題発生時の切り分けに有用です。 ログ 各テーブルへのVACUUMの実施時間や回数をシステムビュー経由で確認可能です。問 題なく手動 or 自動でのVACUUMがなされているかの監視に有用です。(VACUUM回数 の確認は9.2から) 監視
  • 59. vacuum_set_xid_limitsの出力値 59 出力値 説明 oldestXmin 実行中の全トランザクション中の一番古いxid。タプルがDEAD or RECENTLY_DEAD か判定する際に使用されるカットオフポイント。 freezeLimit VACUUM中の凍結トランザクションIDに置き換えられるすべてのxid よりも古い値。 xidFullScanLimit 最小のxid。この値よりも古いrelfrozenxidを持つテーブルは、テーブ ル全体にわたってタプルを凍結するため、テーブル全体のVACUUM が適用されます。この値よりも新しいテーブルのVACUUMは部分的 にスキャンすることになります。 multiXactCutoff Xmaxから除去されるすべてのMultiXactIdsよりも古い値。 mxactFullScanLimit テーブル全体のVACUUMを行うため、xidFullScanLimitとともにテーブ ルのrelminmxidの値と比較される値。