2. プロセスとメモリー
3.3 ファイルシステムと動作
3.3.4 VACUUM 動作
ここでは VACUUM 処理により、ファイルの内容がどのように変化するかを確認します。
動作検証は自動VACUUMを停止(パラメータautovacuum=off)して行いました。
□ VACUUM CONCURRENT
VACUUM CONCURRENT処理は、更新前情報を再利用可能な状態にマーキングします。
postgres=> SELECT relname, relfilenode FROM pg_class WHERE relname='data1' ;
relname | relfilenode ---+--- data1 | 16409 (1 row)
$ cd data/base/16385
$ ls 16409*
-rw--- 1 postgres postgres 8192 Feb 11 16:46 16409 ← Table
-rw--- 1 postgres postgres 24576 Feb 11 16:46 16409_fsm ← Free Space Map -rw--- 1 postgres postgres 8192 Feb 11 16:46 16409_vm ← Visibility Map
$
Table Visibility Map Free Space Map
無効タプル 有効タプル
page-
pa ge- page 1 0%
page 2
page 3
50%
25%
処理の前後で、ブロックの情報がどのように変化するか確認します。
例 47 データ準備(12件挿入)
1レコード1 KBのテーブルを作成し、12レコードを格納します。これにより2ブロッ クのテーブルが作成されます。
例 48 データ準備(ファイルの特定)
postgres=> CREATE TABLE data1 (c1 CHAR(500) NOT NULL, c2 CHAR(500) NOT NULL) ; CREATE TABLE
postgres=> INSERT INTO data1 VALUES ('AAA', '111') ; postgres=> INSERT INTO data1 VALUES ('BBB', '222') ; postgres=> INSERT INTO data1 VALUES ('CCC', '333') ; postgres=> INSERT INTO data1 VALUES ('DDD', '444') ; postgres=> INSERT INTO data1 VALUES ('EEE', '555') ; postgres=> INSERT INTO data1 VALUES ('FFF', '666') ; postgres=> INSERT INTO data1 VALUES ('GGG', '777') ; postgres=> INSERT INTO data1 VALUES ('HHH', '888') ; postgres=> INSERT INTO data1 VALUES ('III', '999') ; postgres=> INSERT INTO data1 VALUES ('JJJ', '000') ; postgres=> INSERT INTO data1 VALUES ('AAA', 'aaa') ; postgres=> INSERT INTO data1 VALUES ('AAA', 'bbb') ; INSERT 0 1
postgres=# CHECKPOINT ; CHECKPOINT
$ oid2name –d datadb1 From database "datadb1":
Filenode Table Name --- 16470 data1
$ cd /usr/local/pgsql/data/base/16424
$ ls –l 16470
-rw--- 1 postgres postgres 16384 Feb 11 10:56 16470
例 49 ブロック初期状態(第1ブロック)
0000000 nul nul nul nul dle U stx nak soh nul nul nul 4 nul H etx
* 略
0001740 ` bel nul nul G G G sp sp sp sp sp sp sp sp sp
* 略
0002720 sp sp sp sp sp sp sp sp ` bel nul nul 7 7 7 sp
* 略
0003740 ack nul stx nul stx bs can nul ` bel nul nul F F F sp
* 略
0004740 ` bel nul nul 6 6 6 sp sp sp sp sp sp sp sp sp
* 略
0005760 ` bel nul nul E E E sp sp sp sp sp sp sp sp sp
* 略
0006740 sp sp sp sp sp sp sp sp ` bel nul nul 5 5 5 sp
* 略
0007760 eot nul stx nul stx bs can nul ` bel nul nul D D D sp
* 略
0010760 ` bel nul nul 4 4 4 sp sp sp sp sp sp sp sp sp
* 略
0012000 ` bel nul nul C C C sp sp sp sp sp sp sp sp sp
* 略
0012760 sp sp sp sp sp sp sp sp ` bel nul nul 3 3 3 sp
* 略
0014000 stx nul stx nul stx bs can nul ` bel nul nul B B B sp
* 略
0015000 ` bel nul nul 2 2 2 sp sp sp sp sp sp sp sp sp
* 略
0016020 ` bel nul nul A A A sp sp sp sp sp sp sp sp sp
* 略
0017000 sp sp sp sp sp sp sp sp ` bel nul nul 1 1 1 sp
*
例 50 ブロック初期状態(第2ブロック)
各ブロックの中間レコードを削除し、VACUUM処理を実行します。
0020000 nul nul nul nul nul k stx nak soh nul nul nul , nul X vt 0020020 nul sp eot sp nul nul nul nul x esc dle bs p etb dle bs 0020040 h dc3 dle bs ` si dle bs X vt dle bs nul nul nul nul 0020060 nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul nul
*
0025720 nul nul nul nul nul nul nul nul 6 dc4 etx nul nul nul nul nul
* 略
0025760 ` bel nul nul L L L sp sp sp sp sp sp sp sp sp
* 略
0026740 sp sp sp sp sp sp sp sp ` bel nul nul b b b sp
* 略
0027760 eot nul stx nul stx bs can nul ` bel nul nul K K K sp
* 略
0030760 ` bel nul nul a a a sp sp sp sp sp sp sp sp sp
* 略
0032000 ` bel nul nul J J J sp sp sp sp sp sp sp sp sp
* 略
0032760 sp sp sp sp sp sp sp sp ` bel nul nul 0 0 0 sp
* 略
0034000 stx nul stx nul stx bs can nul ` bel nul nul I I I sp
* 略
0035000 ` bel nul nul 9 9 9 sp sp sp sp sp sp sp sp sp
* 略
0036020 ` bel nul nul H H H sp sp sp sp sp sp sp sp sp
* 略
0037000 sp sp sp sp sp sp sp sp ` bel nul nul 8 8 8 sp 0037020 sp sp sp sp sp sp sp sp sp sp sp sp sp sp sp sp
* 0040000
例 51 レコード削除とVACUUM実行
この操作によりブロック内容がどのように変化したかを確認します。以下の2例は、
VACUUM後のブロック状態を示しています。ブロック内で、有効なレコードがブロック下
部へ移動され、ヘッダとブロック下部の間に空き領域が作成されています。この動作により、
連続した空き領域を作成していることがわかります。ただし、一部のレコード(下記例では 001740 C1=’GGG’, C2=’777’のレコードと、003740 C1=’LLL’ ,C2=’bbb’のレコード)は重複 して格納されています。
注意
postgres=> DELETE FROM data1 WHERE c1 IN ('CCC', 'JJJ') ; DELETE 2
postgres=# CHECKPOINT ; CHECKPOINT
postgres=> VACUUM data1 ; VACUUM
ブロック内のレコード整理自体は HOT (Heap On Tuples) の機能でも実施されていま す。ページ内に空き容量が不足していると確認された場合には、ブロック内でVACUUM 相当の動作を行います。以下のページに動作が記載されています。
http://lets.postgresql.jp/documents/tutorial/hot_2/hot2_2
例 52 VACUUM処理後(第1ブロック)
0000000 nul nul nul nul sp 7 stx nak soh nul soh nul 4 nul P bel
*略
0001740 ` bel nul nul G G G sp sp sp sp sp sp sp sp sp
*略
0002720 sp sp sp sp sp sp sp sp ` bel nul nul 7 7 7 sp
*略
0003740 bel nul stx nul stx ht can nul ` bel nul nul G G G sp
*略
0004740 ` bel nul nul 7 7 7 sp sp sp sp sp sp sp sp sp
*略
0005760 ` bel nul nul F F F sp sp sp sp sp sp sp sp sp
*略
0006740 sp sp sp sp sp sp sp sp ` bel nul nul 6 6 6 sp
*略
0007760 enq nul stx nul stx ht can nul ` bel nul nul E E E sp
*略
0010760 ` bel nul nul 5 5 5 sp sp sp sp sp sp sp sp sp
*略
0012000 ` bel nul nul D D D sp sp sp sp sp sp sp sp sp
*略
0012760 sp sp sp sp sp sp sp sp ` bel nul nul 4 4 4 sp
*略
0014000 stx nul stx nul stx ht can nul ` bel nul nul B B B sp
*略
0015000 ` bel nul nul 2 2 2 sp sp sp sp sp sp sp sp sp
*略
0016020 ` bel nul nul A A A sp sp sp sp sp sp sp sp sp
*略
0017000 sp sp sp sp sp sp sp sp ` bel nul nul 1 1 1 sp
*
例 53 VACUUM処理後(第2ブロック)
□ VACUUM後の空間利用
VACUUM処理で空き領域を作成できたため、データを格納します。
例 54 新規データ格納
0020000 nul nul nul nul dle H stx nak soh nul soh nul , nul ` si
*略
0025760 ` bel nul nul L L L sp sp sp sp sp sp sp sp sp
*略
0026740 sp sp sp sp sp sp sp sp ` bel nul nul b b b sp
*略
0027760 enq nul stx nul stx ht can nul ` bel nul nul L L L sp
*略
0030760 ` bel nul nul b b b sp sp sp sp sp sp sp sp sp
*略
0032000 ` bel nul nul K K K sp sp sp sp sp sp sp sp sp
*略
0032760 sp sp sp sp sp sp sp sp ` bel nul nul a a a sp
*略
0034000 stx nul stx nul stx ht can nul ` bel nul nul I I I sp
*略
0035000 ` bel nul nul 9 9 9 sp sp sp sp sp sp sp sp sp
*略
0036020 ` bel nul nul H H H sp sp sp sp sp sp sp sp sp
*略
0037000 sp sp sp sp sp sp sp sp ` bel nul nul 8 8 8 sp
* 0040000
postgres=> INSERT INTO data1 VALUES ('MMM', 'ccc') ; INSERT 0 1
postgres=# CHECKPOINT ; CHECKPOINT
例 55 INSERT後(第1ブロック)
上記のように、これまで重複して格納されていた0001740 部分が上書きされていること がわかります。この動作はテーブルのFILLFACTOR属性によって異なる場合があります。
□ VACUUM FULL
VACUUM FULLは、更新済レコードの再利用化だけでなく、ファイルの縮小も実施しま
す。実際のファイルがどのように変化するかを確認します。
VACUUM FULLを実行すると、ファイル名とファイルのi-nodeが変更されていること
から、新規のファイルが作成されることがわかります。このことからVACUUM FULLは 既存のファイルを読み、レコードの整理をしながら新規ファイルを作成することでファイ ルの縮小を行っていることがわかります。
例 56 VACUUM処理(VACUUM FULL実行)
$ ls -li 16470 ← VACUUM FULL 前のファイル
558969 -rw--- 1 postgres postgres 16384 Feb 11 11:39 16470
$ oid2name -d datadb1 From database "datadb1":
Filenode Table Name ---
16476 data1 ← VACUUM FULL で Filenode が変更された。
$ ls -li 16476 ← ファイル名と i-node が変更された
558974 -rw--- 1 postgres postgres 16384 Feb 11 11:47 16476
0000000 nul nul nul nul ` t stx nak soh nul soh nul 4 nul H etx
* 略
0001740 ` bel nul nul M M M sp sp sp sp sp sp sp sp sp
* 略
0002720 sp sp sp sp sp sp sp sp ` bel nul nul c c c sp
* 略
0003740 bel nul stx nul stx ht can nul ` bel nul nul G G G sp
* 略
0004740 ` bel nul nul 7 7 7 sp sp sp sp sp sp sp sp sp
* 略
複数のテーブルから構成されるデータベースに対してVACUUM FULL文を実行する場 合、同時に実行されるVACUUM処理は1テーブルだけです(CONCURRENT
VACUUMも同様)。また、複数セグメントから構成されるテーブルに対してVACUUM
FULLを実行する場合、全ファイルのVACUUM FULL処理が完了するまでテーブルを構 成するすべてのファイルは維持されます。このため大規模なテーブル(多数のセグメント から構成される)では、一時的にテーブルのストレージ容量が最大で2倍になる可能性が あります。
□ 一括更新と部分更新
1,000レコードのテーブルに対して全レコード一括でUPDATE文を実行する場合と、
各レコードに対して1,000回UPDATE文を実行する場合では、VACUUM対象となる不 要レコードの数が異なります。一括更新の場合は全レコードのコピーが作成されるのに対 し、単一レコードの更新ではVACUUMを実行する前に、ブロック内の不要レコードの再 利用が行われるためです。
例 57 更新方法によるブロック数の差異
postgres=> CREATE TABLE data1(c1 NUMERIC, c2 VARCHAR(100), c3 VARCHAR(100)) ; CREATE TABLE
-- insert 1000 records
postgres=> SELECT relpages, reltuples FROM pg_class WHERE relname='data1' ; relpages | reltuples
---+--- 8 | 1000 (1 row)
postgres=> UPDATE data1 SET c1=c1+1 ; ← 一括更新 UPDATE 1000
postgres=> SELECT relpages, reltuples FROM pg_class where relname='data1' ; relpages | reltuples
---+---
15 | 1000 ← ブロック数が増えている (1 row)
postgres=> TRUNCATE TABLE data1 ; -- insert 1000 records
postgres=> UPDATE data1 SET C2='TEST' WHERE c1=100 ; -- 1,000 回実行 UPDATE 1
postgres=> SELECT relpages, reltuples FROM pg_class WHERE relname='data1' ; relpages | reltuples
---+---
8 | 1000 ← ブロック数は増えていない (1 row)