Contenu connexe Plus de Masahiko Sawada (20) Inside vacuum - 第一回PostgreSQLプレ勉強会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”
• 読み方
タグ付けを行ったディレクトリから読む
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
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
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
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
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