database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
ㆍPostgreSQL
Firebird
Oracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
PostgreSQL Q&A 10221 게시물 읽기
No. 10221
PostgreSQL tuple 에 대해 공부하다 질문드립니다.
작성자
허원석(jigsawfit)
작성일
2021-01-01 13:05
조회수
1,526

일반적인 데이터베이스 용어로 보면 tuple / record / row 는 모두 동일하게 행을 가리킨다고 알고 있습니다.
oracle 과 mysql 은 변경 데이터 또는 삭제 데이터를 undo segment 에 저장하는 것과 달리 PostgreSQL 은 변경 또는 삭제 데이터를 테이블이 사용 중인 page 에 
그대로 쌓아가고 dead tuple 이라고 마킹만 하는걸로 알고 있습니다.(MVCC 구현의 차이) 그렇기 떄문에 tuple 의 개념이 타 rdbms 와 조금 다르게 이해하고 있구요.(live tuple 과 dead tuple)
그래서 아래와 같이 테스트 해봤는데 PostgreSQL 버전별로 VACUUM FULL verbose 결과가 아래와 같이 상이하게 나타났습니다.

PostgreSQL v9.5 에서는 VACUUM FULL verbose 수행하면 INFO:  "tuple_test": found 1200 removable, 1100 nonremovable row versions in 13 pages 이라고 떠서 dead tuple 이 1200개 였고,
VACUUM FULL 수행후에 dead tuple 이 0 으로 변했음을 확인할 수 있는데

PostgreSQL v12 에서는 동일하게 VACUUM FULL verbose 수행했을 때, INFO:  "tuple_test": found 0 removable, 1100 nonremovable row versions in 13 pages 라고 확인되서 문의드립니다.

-- 테이블 생성 및 1000건 insert

postgres=# CREATE TABLE tuple_test (
id NUMERIC NOT NULL,
rand_num NUMERIC NOT NULL,
birth DATE,
CONSTRAINT id_pk PRIMARY KEY (id)
);
CREATE TABLE
postgres=# INSERT INTO tuple_test (id, rand_num, birth) SELECT GENERATE_SERIES, (random()*9000 + 10000)::int, CURRENT_DATE - (random() * 365 * 10)::int - 40 * 365 FROM GENERATE_SERIES(1, 1000);
INSERT 0 1000
postgres=# SELECT c.relname, pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as t_tup,
pg_stat_get_live_tuples(c.oid) AS n_live_tup, 
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
pg_size_pretty(pg_total_relation_size(c.oid)) as total_rel_size,
pg_size_pretty(pg_relation_size(c.oid)) as rel_size
FROM pg_class as c 
WHERE pg_stat_get_live_tuples(c.oid) > 0 
AND c.relname = 'tuple_test' ORDER BY n_live_tup DESC;
  relname   | t_tup | n_live_tup | n_dead_tup | live_tuple_rate | dead_tuple_rate | total_rel_size | rel_size 
------------+-------+------------+------------+-----------------+-----------------+----------------+----------
 tuple_test |  1000 |       1000 |          0 |          100.00 |            0.00 | 120 kB         | 48 kB
(1 row)

-- 다시 100 건 insert
postgres=# BEGIN;
BEGIN
postgres=# INSERT INTO tuple_test (id, rand_num, birth) SELECT GENERATE_SERIES, (random()*9000 + 10000)::int, CURRENT_DATE - (random() * 365 * 10)::int - 40 * 365 FROM GENERATE_SERIES(1001, 1100);
INSERT 0 100
postgres=# COMMIT;
COMMIT
postgres=# SELECT c.relname, pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as t_tup,
pg_stat_get_live_tuples(c.oid) AS n_live_tup, 
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
pg_size_pretty(pg_total_relation_size(c.oid)) as total_rel_size,
pg_size_pretty(pg_relation_size(c.oid)) as rel_size
FROM pg_class as c 
WHERE pg_stat_get_live_tuples(c.oid) > 0 
AND c.relname NOT LIKE 'pg_%' ORDER BY n_live_tup DESC;
  relname   | t_tup | n_live_tup | n_dead_tup | live_tuple_rate | dead_tuple_rate | total_rel_size | rel_size 
------------+-------+------------+------------+-----------------+-----------------+----------------+----------
 tuple_test |  1100 |       1100 |          0 |          100.00 |            0.00 | 120 kB         | 48 kB
(1 row)


-- 100 건 insert 하고 rollback
postgres=# BEGIN ;
BEGIN
postgres=# INSERT INTO tuple_test (id, rand_num, birth) SELECT GENERATE_SERIES, (random()*9000 + 10000)::int, CURRENT_DATE - (random() * 365 * 10)::int - 40 * 365 FROM GENERATE_SERIES(1101, 1200);
INSERT 0 100
postgres=# ROLLBACK ;
ROLLBACK
postgres=# SELECT c.relname, pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as t_tup,
pg_stat_get_live_tuples(c.oid) AS n_live_tup, 
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
pg_size_pretty(pg_total_relation_size(c.oid)) as total_rel_size,
pg_size_pretty(pg_relation_size(c.oid)) as rel_size
FROM pg_class as c 
WHERE pg_stat_get_live_tuples(c.oid) > 0 
AND c.relname = 'tuple_test' ORDER BY n_live_tup DESC;            
  relname   | t_tup | n_live_tup | n_dead_tup | live_tuple_rate | dead_tuple_rate | total_rel_size | rel_size 
------------+-------+------------+------------+-----------------+-----------------+----------------+----------
 tuple_test |  1200 |       1100 |        100 |           91.00 |            8.00 | 136 kB         | 56 kB
(1 row)

-- update 하고 tuple 정보 확인
postgres=# UPDATE tuple_test SET rand_num = rand_num + 1;
UPDATE 1100
postgres=# SELECT c.relname, pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as t_tup,
pg_stat_get_live_tuples(c.oid) AS n_live_tup, 
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
pg_size_pretty(pg_total_relation_size(c.oid)) as total_rel_size,
pg_size_pretty(pg_relation_size(c.oid)) as rel_size
FROM pg_class as c 
WHERE pg_stat_get_live_tuples(c.oid) > 0 
AND c.relname = 'tuple_test' ORDER BY n_live_tup DESC;  
  relname   | t_tup | n_live_tup | n_dead_tup | live_tuple_rate | dead_tuple_rate | total_rel_size | rel_size 
------------+-------+------------+------------+-----------------+-----------------+----------------+----------
 tuple_test |  2300 |       1100 |       1200 |           47.00 |           52.00 | 232 kB         | 104 kB
(1 row)


-- vacuum full 수행하고, 다시 tuple 정보 확인
postgres=# vacuum FULL verbose tuple_test ;
INFO:  vacuuming "public.tuple_test"
INFO:  "tuple_test": found 1200 removable, 1100 nonremovable row versions in 13 pages
DETAIL:  0 dead row versions cannot be removed yet.
CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
VACUUM
postgres=# SELECT c.relname, pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid) as t_tup,
pg_stat_get_live_tuples(c.oid) AS n_live_tup, 
pg_stat_get_dead_tuples(c.oid) AS n_dead_tup,
round(100*pg_stat_get_live_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as live_tuple_rate,
round(100*pg_stat_get_dead_tuples(c.oid) / (pg_stat_get_live_tuples(c.oid) + pg_stat_get_dead_tuples(c.oid)),2) as dead_tuple_rate,
pg_size_pretty(pg_total_relation_size(c.oid)) as total_rel_size,
pg_size_pretty(pg_relation_size(c.oid)) as rel_size
FROM pg_class as c 
WHERE pg_stat_get_live_tuples(c.oid) > 0 
AND c.relname = 'tuple_test' ORDER BY n_live_tup DESC;  
  relname   | t_tup | n_live_tup | n_dead_tup | live_tuple_rate | dead_tuple_rate | total_rel_size | rel_size 
------------+-------+------------+------------+-----------------+-----------------+----------------+----------
 tuple_test |  1100 |       1100 |          0 |          100.00 |            0.00 | 104 kB         | 48 kB
(1 row)
 

이 글에 대한 댓글이 총 1건 있습니다.

12버전에서는 autovacuum 데몬이 vacuum full 하기 전에 dead tuple을 빈 공간으로 만들어버렸겠죠.

 

김상기(ioseph)님이 2021-01-03 00:26에 작성한 댓글입니다.
[Top]
No.
제목
작성자
작성일
조회
10225쿼리 및 커서 캐싱 [2]
charlie p
2021-01-07
1415
10224성능지연관련 인덱스 생성여부 문의(epas 10버전) [2]
우재권
2021-01-07
1387
10223pgadmin4 원격접속 [2]
전호진
2021-01-05
1377
10221PostgreSQL tuple 에 대해 공부하다 질문드립니다. [1]
허원석
2021-01-01
1526
10220array_to_string 사용 쿼리 질문 드립니다. [1]
이요남
2020-12-22
1377
10219Linux C 프로그램에서 postgresql DB에 한글 Insert 시 Insert 실패 문제 [1]
초보DB
2020-12-22
1411
10218원격지 파일로 data import [1]
궁금
2020-12-21
1290
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2023 DSN, All rights reserved.
작업시간: 0.054초, 이곳 서비스는
	PostgreSQL v16.1로 자료를 관리합니다