2. プロセスとメモリー
3.3 ファイルシステムと動作
3.3.4 VACUUM 動作
ここではVACUUM処理により、ファイルの内容がどのように変化するかを確認します。
□ VACUUM CONCURRENT
VACUUM CONCURRENT処理は、更新前情報を再利用可能な状態にマーキングします。
処理の前後で、ブロックの情報がどのように変化するか確認します。
postgres=> SELECT relname, relfilenode FROM pg_class WHERE relname='demo1' ;
relname | relfilenode ---+--- demo1 | 16409 (1 row)
$ cd data/base/16385
$ ls 16409*
-rw--- 1 postgres postgres 8192 Jun 6 16:46 16409 ← Table
-rw--- 1 postgres postgres 24576 Jun 6 16:46 16409_fsm ← Free Space Map -rw--- 1 postgres postgres 8192 Jun 6 16:46 16409_vm ← Visibility Map
$
例 36 データ準備(12件挿入)
1レコード1 KBのテーブルを作成し、12レコードを格納します。これにより2ブロック のテーブルが作成されます。
例 37 データ準備(ファイルの特定)
postgres=> CREATE TABLE demo1 (c1 CHAR(500) NOT NULL, c2 CHAR(500) NOT NULL) ; CREATE TABLE
postgres=> INSERT INTO demo1 VALUES ('AAA', '111') ; postgres=> INSERT INTO demo1 VALUES ('BBB', '222') ; postgres=> INSERT INTO demo1 VALUES ('CCC', '333') ; postgres=> INSERT INTO demo1 VALUES ('DDD', '444') ; postgres=> INSERT INTO demo1 VALUES ('EEE', '555') ; postgres=> INSERT INTO demo1 VALUES ('FFF', '666') ; postgres=> INSERT INTO demo1 VALUES ('GGG', '777') ; postgres=> INSERT INTO demo1 VALUES ('HHH', '888') ; postgres=> INSERT INTO demo1 VALUES ('III', '999') ; postgres=> INSERT INTO demo1 VALUES ('JJJ', '000') ; postgres=> INSERT INTO demo1 VALUES ('AAA', 'aaa') ; postgres=> INSERT INTO demo1 VALUES ('AAA', 'bbb') ; INSERT 0 1
postgres=# CHECKPOINT ; CHECKPOINT
$ oid2name –d demodb From database "demodb":
Filenode Table Name --- 16470 demo1
$ cd /opt/PostgreSQL/9.4/data/base/16424
$ ls –l 16470
-rw--- 1 postgres postgres 16384 Apr 26 10:56 16470
例 38 ブロック初期状態(第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
*
例 39 ブロック初期状態(第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
例 40 レコード削除とVACUUM実行
この操作によりブロック内容がどのように変化したかを確認します。以下の2例は、
VACUUM後のブロック状態を示しています。ブロック内で、有効なレコードがブロック下
部へ移動され、ヘッダとブロック下部の間に開き領域が作成されています。この動作によ り、連続した空き領域を作成していることがわかります。ただし、一部のレコード(下記 例では001740 C1='GGG', C2='777'のレコードと、003740 C1='LLL' ,C2='bbb'のレコード)
は重複して格納されています。
postgres=> DELETE FROM demo1 WHERE c1 IN ('CCC', 'JJJ') ; DELETE 2
postgres=# CHECKPOINT ; CHECKPOINT
postgres=> VACUUM demo1 ; VACUUM
ブロック内のレコード整理自体はHOT (Heap On Tuples) の機能でも実施されていま す。ページ内に空き容量が不足していると確認された場合には、ブロック内でVACUUM 相当の動作を行います。以下のページに動作が記載されています。
http://lets.postgresql.jp/documents/tutorial/hot_2/hot2_2
例 41 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
*
例 42 VACUUM処理後(第2ブロック)
□ VACUUM後の空間利用
VACUUM処理で空き領域を作成できたため、データを格納します。
例 43 VACUUM処理(データ格納)
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 demo1 VALUES ('MMM', 'ccc') ; INSERT 0 1
postgres=# CHECKPOINT ; CHECKPOINT
例 44 INSERT後(第1ブロック)
上記のように、これまで重複して格納されていた0001740 部分が上書きされていること がわかります。この動作はテーブルのFILLFACTOR属性によって異なる場合があります。
□ VACUUM FULL
VACUUM FULLは、更新済レコードの再利用化だけでなく、ファイルの縮小も実施しま
す。実際のファイルがどのように変化するかを確認します。
VACUUM FULLを実行すると、ファイル名とファイルのi-nodeが変更されていること
から、新規のファイルが作成されることがわかります。このことからVACUUM FULLは 既存のファイルを読み、レコードの整理をしながら新規ファイルを作成することでファイ ルの縮小を行っていることがわかります。
例 45 VACUUM処理(VACUUM FULL実行)
$ ls -li 16470 ← VACUUM FULL 前のファイル
558969 -rw--- 1 postgres postgres 16384 Apr 26 11:39 16470
$ oid2name -d demodb From database "demodb":
Filenode Table Name ---
16476 demo1 ← VACUUM FULL で Filenode が変更された。
$ ls -li 16476 ← ファイル名と i-node が変更された
558974 -rw--- 1 postgres postgres 16384 Apr 26 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
* 略
□ 一括更新と部分更新
1,000レコードのテーブルを一括でUPDATE行う場合と、単一レコードを1,000回
UPDATEを行う場合では、VACUUM対象となる不要レコードの数が異なります。一括更
新の場合は全レコードのコピーが作成されるのに対し、単一レコードの更新ではVACUUM を実行する前に、ブロック内の不要レコードの再利用が行われるためです。
例 46 更新方法によるブロック数の差異
postgres=> CREATE TABLE demo1(c1 NUMERIC, c2 VARCHAR(100), c3 VARCHAR(100)) ; CREATE TABLE
-- insert 1000 records
postgres=> SELECT relpages, reltuples FROM pg_class WHERE relname='demo1' ; relpages | reltuples
---+--- 8 | 1000 (1 row)
postgres=> UPDATE demo1 SET c1=c1+1 ; ← 一括更新 UPDATE 1000
postgres=> SELECT relpages, reltuples FROM pg_class where relname='demo1' ; relpages | reltuples
---+---
15 | 1000 ← ブロック数が増えている (1 row)
postgres=> TRUNCATE TABLE demo1 ; -- insert 1000 records
postgres=> UPDATE demo1 SET C2='TEST' WHERE c1=100 ; -- 1,000 回実行 UPDATE 1
postgres=> SELECT relpages, reltuples FROM pg_class WHERE relname='demo1' ; relpages | reltuples
---+---
8 | 1000 ← ブロック数は増えていない (1 row)