PostgreSQL ์ vaccum์ ๋ํด ์์๋ณด์.
by JiwonDev
๐ PostgreSql
Oracle ์ฌ์ฉ์๋ค์ด ๊ฐ์ฅ ์ฝ๊ฒ ์ ์ํ ์ ์๋ ์คํ์์ค DBMS์ด๋ค. postgres(ํฌ์คํธ๊ทธ๋ฆฌ)๋ผ๊ณ ๋ ๋ง์ด ๋ถ๋ฅธ๋ค. ์กฐํ ์ฑ๋ฅ์ ์ธ ์ธก๋ฉด์์ ๋ฐ์ด๋๊ธฐ ๋๋ฌธ์ MySQL์ฒ๋ผ ํจ๊ป ๋ง์ด ์ฌ์ฉ๋๋ค. http://www.postgresql.org/
https://techblog.woowahan.com/6550/
๐ PostgreSQL ๋ ์ด๋ค ๊ธฐ์ค์ผ๋ก ์ฌ์ฉํด์ผํ ๊น์?
์ค์ํ ๋ด์ฉ์ ์๋๋ผ์, ๊ถ๊ธํ๋ฉด ๋๋ฌ๋ณด์
์ ์ธ๊ณ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ๋ฒ ํ์ด์ ๋น๊ตํด๋ณด๋ฉด ์๋์ ๊ฐ๋ค.
Orcale๊ณผ Microsoft ๋ ์ ๋ฃ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋น์ค์ด๊ณ MySQL๊ณผ PostgreSQL์ ์คํ์์ค ๋ฌด๋ฃ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ด๋ค.
๋ณดํต MySQL๊ณผ PostgreSQL์ ์ธ๊ธฐ๊ฐ ๋ง์ผ๋ฉฐ ํ์ฌ๋ ๊ด๊ณต์์ฒ๋ผ ์ฌํ์ง์์ด ํ์ํ ์์ฉ์๋น์ค๋ ๋น์ผ ์๊ธ์ ์ง๋ถํ๊ณ Oracle, Microsoft๋ฅผ ์ฃผ๋ก ์ฌ์ฉํ๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค.
์ฌ๋ด์ผ๋ก 2010๋ ๋๋ ๋ค์ํ DBMS๋ค์ด ์ฐํ์ฃฝ์์ผ๋ก ํ์ํ๋ ์๊ธฐ์๋ค.
๋ชจ๋ฐ์ผ์ ๋ฑ์ฅ์ผ๋ก ์ ํต์ ์ธ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค, RDBMS์ ์๋๋ ์ ๋ฌธ๋ค๋ ์ด์ผ๊ธฐ๊ฐ ๋์๊ณ NoSQL๊ณผ ๊ฐ์ ๋จ์ํ ๋ก๊ทธ ์ฑ๊ฒฉ์ ๋น ๋ฐ์ดํฐ์ ์ ์ฅ์ด ํ์ํด์ง๋ฉฐ Cassandra๋ HBase, ๋์ค์ ๋์จ MongoDB๋ฑ์ด ๋ง์ ๊ด์ฌ์ ๋ฐ์์๋ค.
ํ์ง๋ง 10๋ ์ด ์ง๋ ์ง๊ธ, ๊ทธ ๋๊ตฌ๋ ๊ทธ๋ ์ถ์๋๋ NoSQL DBMS๊ฐ ์์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ์ํ๋ค๊ณ ๋ ์ธ๊ธํ์ง ์๋ ๋ฏ ํ๋ค.
* ์บ์ ์๋ฒ๋ก ๋ง์ด ์ฌ์ฉ๋๋ Redis์ Memcached๋ ์๋น์ค DBMS๋ผ๊ณ ๋ณด๊ธฐ์๋ ๋ฒ์ฃผ๊ฐ ์ฝ๊ฐ ๋ค๋ฅด๋ค. ElasticSearch๋ ๋ง์ฐฌ๊ฐ์ง
๊ทธ๋๋ง HBase์ MongoDB์ ๋๊ฐ ์๊ธฐ๋ง์ ์์ญ์์ ์ฌ์ฉ๋๊ณ ์์ผ๋ฉฐ ์์๊ณผ๋ ๋ค๋ฅด๊ฒ ์ฌ์ ํ MySQL, Postgres์ ๊ฐ์ ๊ด๊ณํ DB๊ฐ ์๋ง์ NoSQL DBMS์ ์ญํ ์ ๋์ฒดํ๋ฉฐ ์ฌ์ฉ๋๊ณ ์๋ค.
์คํ์์ค DB ๋๊ฒฐ, MySQL vs Postgres
์๋ ๋ฌด๋ฃ DBMS์ค์์ MySQL ์ฌ์ฉ๋ฅ ์ด ์๋์ ์ด์์ผ๋ 2009๋ ์ ์ค๋ผํด์ด Sun์ฌ๋ฅผ ์ธ์ํ๋ฉด์ MySQL์ ์์ ๊ถ ์ด์, ์ ๋ฃํ๋ฑ์ ๋ฌธ์ ๋๋ฌธ์ PostgreSQL ์ฌ์ฉ๋ฅ ์ด ์ฆ๊ฐํ๊ณ ์ดํ ์ด๋์ ์ฅ์ ๋ค ๋๋ถ์ ์ฌ์ฉ๋ฅ ์ด ๊พธ์คํ๊ฒ ์ฆ๊ฐ๋๊ณ ์๋ค.
https://news.hada.io/topic?id=8018
https://news.hada.io/topic?id=11287
PostgreSQL ์ฅ์
- PostgreSQL์ SQL ํ์ค์ ๊ฒฝ์์ฌ๋ณด๋ค ์ ์งํค๊ณ ์๋ ํธ์ด๋ค.
- ๋ค์ํ join ๋ฐฉ๋ฒ (sort merge join , hash join)์ ์ ๊ณตํ์ฌ ๋ณต์กํ ์กฐํ ์ฟผ๋ฆฌ ์ฑ๋ฅ์ด ์ข๋ค.
- PostGIS(์ขํ๊ณ)์ ๊ฐ์ ๋ค์ํ ํ์ฅ ํ๋ฌ๊ทธ์ธ์ ์ ๊ณตํ๋ค.
PostgreSQL ๋จ์
- update ๋ฅผ ๊ณผ๊ฑฐ ํ์ ์ญ์ ํ๊ณ ์๋ก์ด ํ์ ์ถ๊ฐํ๋ ํ์์ด๋ผ ๊ฒฝ์์ฌ์ ๋นํด ์ฑ๋ฅ์ด ๋จ์ด์ง๋ ํธ์ด๋ค
์ฆ ์์ ๋ณด๋ค insert ๋ฅผ ์ฃผ๋ก ํ๊ณ ๋ณต์กํ ์กฐํ๊ฐ ๋ง์ ์๋น์ค์์ ์ ํฉํ๋ค. - MVCC ๊ฐ ์ถ๊ฐ ์ ์ฅํ๋ ์์ผ๋ก ๊ตฌํ๋์ด์๋ค. ์ดํ ๋ง์น Java GC ์ฒ๋ผ auto vaccum์ ํตํด ์ฃฝ์ ๋ฐ์ดํฐ๋ค์ ์ ๋ฆฌํ๋ค. ์ฆ auto vaccum ๋์์ ๋ํด ์ ์๊ณ ์์ง์๊ณ ์ฌ์ฉํ๋ค๋ฉด ์ฃฝ์ ๋ฐ์ดํฐ๊ฐ ์์ฌ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ์ผํฌ ์๋ ์๋ค.
๐ Postgre MVCC ์ vaccum
MVCC(Multi-Version Concurrency Control)๋ ์ฌ๋ฌ ํธ๋์ญ์ ์์ ๋ฝ์ ๊ฑธ์ง ์๊ณ ๋์์ฑ์ ๊ด๋ฆฌํด ์ฑ๋ฅ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ์๋ฏธํ๋ค.
postgreSQL์ MVCC๋ ์์ ํ ๋ฐ์ดํฐ๋ฅผ ํต์ผ๋ก insertํ ๋ค ๋์ค์ ๋ฐ๋ก vaccum ํด์ค๋ค. ์ด ๋ฐฉ๋ฒ์ผ๋ก ๋์์ฑ๊ณผ ์ฑ๋ฅ์ ๊ทน๋ํ์์ผฐ๋ค.
๋ค์ ์ค๋ช ํ์๋ฉด (* ํธ๋์ญ์ ID = tx_id , ๋ฐ์ดํฐ ๋ ์ฝ๋ = Tuple )
- Postgre MVCC๋ ํธ๋์ญ์ ์ด ์์ํ๊ฒ ๋๋ฉด ๋ณธ์ธ์ id ๋ฅผ ํ ๋น๋ฐ๋๋ค.
- ์ดํ UPDATE๋ฑ์ ํ ๋ page (=Free Space Map page)๋ฅผ ์์ฑํ๊ณ ๋ณ๊ฒฝ๋ ๋ ๋ง๋ค ํ์ฌ์ ์ค๋ ์ท์ insert ํ์์ผ๋ก ์์๊ฐ๋ค.
- MVCC ๊ตฌํ์ ์ํด ํ์ฌ ๋ฐ์ดํฐ ํ์ด์ง ๋ด์ ์์ ์ tx_id ๋ณด๋ค ์์๊ฐ์ id๋ฅผ ๊ฐ์ง Tuple์ ์ฝ์ด์จ๋ค.
์ด ๋ ๊ฐ Tuple์ ํ ๋น๋๋ tmin, tmax๊ฐ์ ์๋์ ๊ฐ๋ค.
- xmin (*tx_id min)
- INSERT ์ -> ์ ๊ท Tuple ์ xmin ์ tx_id ํ ๋น
- UPDATE ์ -> ์ ๊ท Tuple ์ xmin ์ tx_id ํ ๋น - xmax (*tx_id max)
์ ๊ท Tuple ์ xmax ์๋ NULL ํ ๋น
DELETE ์ -> ์ด์ Tuple ์ xmax ์ tx_id ํ ๋น
UPDATE ์ -> ์ด์ Tuple ์ xmax ์ tx_id ํ ๋น,
์ด ๊ณผ์ ์์์ ์ฌ๋ฌ๋ฒ ๋ณ๊ฒฝ๋์ด ์ธ๋ชจ์์ด์ง ์ด์ ๋ฐ์ดํฐ๋ค์ ์ด๋์๋ ์ฐธ์กฐ๋์ง ์๋๋ฐ, ์ด๋ฅผ dead tuple์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ด dead tuple์ ์๋ ๋ช ๋ น์ด๋ก ์ง์ธ ์๋ ์์ง๋ง ๋คํํ๋ postgre auto vaccum์ ํตํด ๋๋ถ๋ถ ์ฒ๋ฆฌ๋๋ค.
๐ค vaccum์ด ๋์ฒด ๋ญ์ฃ ? ์ํ๋ฉด ์ด๋ป๊ฒ ๋๋๊ฑฐ์ฃ ?
์ฐ์ Postgre์์๋ Vaccum๊ณผ Vaccum full 2๊ฐ์ง ์ข ๋ฅ๊ฐ ์๋๋ฐ, full ์ ๊ฒฝ์ฐ ๋์คํฌ ๋ฉ๋ชจ๋ฆฌ๊น์ง ์ ๋ฆฌํ์ง๋ง ํ ์ด๋ธ์์ ๋ฒ ํ๋ฝ์ ๊ฑธ์ด DML, ์ฟผ๋ฆฌ ์ด๋ ํ ๊ฒ๋ ํ์ง ๋ชปํ๊ณ ์๊ฐ๋ ์ค๋๊ฑธ๋ฆฐ๋ค. ์ด์๋์ค์๋ ๊ฐ๋ฅํ๋ฉด vaccum full ์์ ์ ํผํด์ผ ํ๊ธฐ์ auto vaccum ๊ธฐ๋ณธ ๊ฐ์ vaccum full ์์ ์ ํ์ง ์๊ฒ ์ค์ ๋์ด์๋ค. ๋์คํฌ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ๋ฑ ์ต์ ์ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ์. ๋จ์ํ ํน์ ํ ์ด๋ธ์์ ์๊ธด ๋ฌธ์ ๋ผ๋ฉด ALTER TABLE๋ก ์๋ก์ด ํ ์ด๋ธ์ ๋ง๋๋๊ฒ ๋ ๋ซ๋ค. + ์ฐธ๊ณ ๋ก Truncate๋ก ํต์งธ๋ก ๋ ๋ฆฌ๋ฉด dead tuple๋ ํจ๊ป ์ ๋ฆฌ๋๋ค.
-- DB ์ ์ฒด ํ ์คํ
vacuum full analyze;
-- DB ์ ์ฒด ๊ฐ๋จํ๊ฒ ์คํ
vacuum verbose analyze;
-- ํด๋น ํ
์ด๋ธ๋ง ๊ฐ๋จํ๊ฒ ์คํ
vacuum analyse [ํ
์ด๋ธ ๋ช
];
-- ํน์ ํ
์ด๋ธ๋ง ํ ์คํ
-- ์ฐธ๊ณ ๋ก full ์คํ์ pg_class.relfilenode ๊ฐ ๋ณ๊ฒฝ๋๋๋ฐ, ์ด๋ ๋์คํฌ์ ์ ์ฅ๋๋ ๋ฌผ๋ฆฌ์ ์ธ ํ์ผ๋ช
์ด๋ค.
vacuum full [ํ
์ด๋ธ๋ช
];
์ผ๋ฐ vaccum ์์ ์ ๋ฝ์ ์๊ฑธ๋ฆฌ์ง๋ง Disk I/O ๋ถํ๋ฅผ ๋ง๋ค๊ณ ๋์์ ์์ ์ค์ธ ๋ค๋ฅธ DB ์ธ์ ์ ์ฑ๋ฅ์ ๋จ์ด๋จ๋ฆฐ๋ค. ๊ทธ๋์ ์๋์ผ๋ก ์คํ์ ์ฌ์ฉ๋์ด ์ ์ ์๋ฒฝ์๊ฐ๋์ vaccum ๋ช ๋ น์ ํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค. ๋ฌผ๋ก ์ด๋ ๊ด๋ จ ์ค์ ๋ณ๊ฒฝ์ผ๋ก ์ด๋์ ๋ ์กฐ์ ์ ๊ฐ๋ฅํ๋ค.
๋คํํ ์ด๋ ์ฌ์ฉ์๊ฐ ๋งค๋ฒ ์ ๊ฒฝ ์ธ ํ์๊ฐ ์๋๋ก auto-vaccum์ ์ํด ์ฒ๋ฆฌ๋๋ค.
auto-vaccum์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ค์ธ๋ค๊ธฐ๋ณด๋จ, ์ฃผ๊ธฐ์ ์ผ๋ก vaccum์ ๋๋ ค ์ผ์ ์์ค ์ด์ ์ปค์ง์ง ์๊ฒ ๋ง๋ค์ด์ฃผ๋๋ก ๋์ํ๋ค. ๋ํ ์ฃฝ์ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ํ ์ฟผ๋ฆฌ(์ธ๋ฑ์ค) ํต๊ณ ์ ๋ณด๊ฐ ๋ฐ๋ ํ์๊ฐ ์๋ค๊ณ ํ๋จ๋๋ฉด ANALYZE ๋ช
๋ น์ ์ถ๊ฐ๋ก ์ํํ๋ค. ๋จ์ํ ์ญ์ ๋ ๋ฐ์ดํฐ์๋ฅผ ๊ธฐ์ค์ผ๋ก ํ๋จํ๋๊ฑด ์๋๊ณ , ์นผ๋ผ์ ๋ณ๊ฒฝ๋น๋๋ ์๋ฃํ๋ฑ ์ฌ๋ฌ ์กฐ๊ฑด์ ๊ณ ๋ คํ์ฌ ํ์ฌ ์ ๋ฆฌํ ํ
์ด๋ธ์๋ง analyze๋ฅผ ํ๋ค.
auto-vaccum ์ ์ฌ๋ฌ๊ฐ์ ๋ฐ๋ชฌ ํ๋ก์ธ์ค(autovaccum launcher)๋ก ํญ์ ์คํ๋์ด ์์ผ๋ฉฐ ํ ์ด๋ธ์ ์๋ฃ๊ฐ ๋ง์ด ๋ณ๊ฒฝ๋์์ ๋ (autovaccum worker) ํ๋ก์ธ์ค๋ฅผ ๋ง๋ค์ด์ ์๋์ผ๋ก ์คํ๋๋ค. ์ด๋ ๋ค๋ฅธ ์์ ์ ๋ฐฉํดํ์ง ์์ผ๋ share update lock์ ๊ฒฝ์ฐ์๋ง vaccum์ด ์ค๋จ๋๊ณ , ํด๋น ๋ฝ์ ๊ณ์ ์ก๊ณ ์๋ ๊ฒฝ์ฐ vaccum์ด ์ ๋๋ก ์๋ฃ๋์ง ์์ ์ ์๋ค. ๋น์ฐํ๋ง์ด์ง๋ง db max_connection ๊ฐ์ ์ต์ ๊ณผ autovaccum ํ๋ก์ธ์ค๋ ์๋ฌด๋ฐ ๊ด๋ จ์ด ์๋ค.
์ฐธ๊ณ ๋ก ํ์ฌ ์คํ์ค์ธ vaccum ์ธ์ ์ ์๋์ ๊ฐ์ด ํ์ธํด๋ณผ ์ ์๋ค.
SELECT datname, usename, pid, CURRENT_TIMESTAMP - xact_start AS xact_runtime, query
FROM pg_stat_activity
WHERE upper(query) LIKE '%VACUUM%'
ORDER BY xact_start;
PostgreSQL์์ VACUUM ๋ช ๋ น์ ๋ค์๊ณผ ๊ฐ์ ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๋ก ์ ๊ธฐ์ ์ผ๋ก ๊ฐ ํ ์ด๋ธ ๋จ์๋ก ์คํ๋์ด์ผ ํ๋ค
- ์ฃฝ์ tuple ๋๋ฌธ์ ์๊ธฐ๋ ๋ถํ์ํ ์ถ๊ฐ Disk I/O์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ค์ด๊ธฐ ์ํด ํ์ํ๋ค.
- ํธ๋์ญ์ ID ๊ฒน์นจ์ด๋, ๋ค์ค ํธ๋์ญ์ ID ๊ฒน์นจ ์ํฉ์ผ๋ก ์ค๋๋ ์๋ฃ๊ฐ ์์ค ๋ ๊ฐ๋ฅ์ฑ์ ๋ฐฉ์งํด์ผํ ํ์๊ฐ ์๋ค.
- PostgreSQL ์ฟผ๋ฆฌ ์คํ ๊ณํ๊ธฐ๊ฐ ์ฌ์ฉํ ์๋ฃ ํต๊ณ ์ ๋ณด(ANALYZE)๋ฅผ ๊ฐฑ์ ํ ํ์๊ฐ ์๋ค.
- ์ปค๋ฒ๋ฆฌ์ง ์ธ๋ฑ์ค(only index) ์๋๋ฅผ ์ํด, ์ค์๋ฃ ์ง๋(visibility map, vm) ๊ฐฑ์ ํ ํ์ ์๋ค.
1๏ธโฃ ์ฃฝ์ tuple ๋๋ฌธ์ ์๊ธฐ๋ ๋ถํ์ํ ์ถ๊ฐ Disk I/O์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ์ค์ด๊ธฐ ์ํด ํ์ํ๋ค.
Postgre๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ๋ FSM page (Free Space Map page) ๋จ์๋ก ์ฝ์ด๋ค์ธ๋ค. ์ด๋ ๊ธฐ๋ณธ 8KB์ธ๋ฐ ํ์ด์ง์ ์ฐ๋ ๊ธฐ ๋ฐ์ดํฐ๊ฐ ๋ง๋ค๋ฉด ๋ ๋ง์ FSM page๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ ค์ผํ๋ค. ์ฆ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ญ๋น๋๋๊ฑด ๋ฌผ๋ก ์ด๊ณ Disk I/O ์์ ํ์๋ฅผ ๋๋ฆฌ๊ฒ ๋๊ณ ์์ด๋ฉด ์์ผ์๋ก ์ฟผ๋ฆฌ์ ์ฑ๋ฅ ์ ํ๋ก ์ด์ด์ง๊ฒ ๋๋ค.
์ฐธ๊ณ ๋ก ์ด๋ ๊ฒ ๋ถํ์ํ ํ์ด์ง๋ฅผ ๋ง์ด ์ฝ๊ฒ๋๋ฉด, ์ธ๋ฑ์ค๊ฐ ๋ฉ์ฉกํ ์์์๋ ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํ์ง์๋ ํฉ๋นํ ์ํฉ๋ ๋ฐ์ํ ์ ์๋ค.
2๏ธโฃ ํธ๋์ญ์ ID ๊ฒน์นจ์ผ๋ก ์ค๋๋ ์๋ฃ๊ฐ ์์ค ๋ ๊ฐ๋ฅ์ฑ์ ๋ฐฉ์งํด์ผํ ํ์๊ฐ ์๋ค.
Postgre MVCC์ ์ฌ์ฉ๋๋ ํธ๋์ญ์ ID๋ 32bit ์์์ด๊ธฐ์ 42์ต ๊ฐ๊น์ง๋ง ์ ์ฅํ ์ ์๋ค. ์ด ์๋ฅผ ์ด๊ณผํ๋ฉด ์ค๋ฒํ๋ก์ฐ๋ก 0์ด ๋๋ฉด์ ๋ฐ์ดํฐ๊ฐ ์์ค๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค. ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ๊ธฐ์ํด ์ค์ ๋ ์์น๋ฅผ ๋๊ฒ๋๋ฉด auto-vaccum ์ ์ด์ฉํด ํ ์ด๋ธ์ ๋ชจ๋ ํธ๋์ญ์ ID๋ฅผ ๋ฐ๋ก ์์ฝ๋ Fronzen xid(2)๋ก ๋ฐ๊ฟ๋ฒ๋ฆฐ๋ค. ์ด๋ฅผ freeze (๋๋ anti wraparound vaccum)๋ผ๊ณ ๋ถ๋ฅธ๋ค. ์ฐธ๊ณ ๋ก Postgre๋ ํธ๋์ญ์ ID๊ฐ Fronzen xid๋ก ๋์ด์๋ค๋ฉด, ๊ฐ์ ๋น๊ตํ์ง์๊ณ ๋ฌด์กฐ๊ฑด ์๋ Tuple๋ก ํ๋จํ๋ค.
์ฐธ๊ณ ๋ก PostgreSQL 9.4 ์ด์ ์๋ ๋จ์ํ xmin = 2 (Fronze xid) ๋ก ๋ฎ์ด์์์ ์ ์ฅํ๋๋ฐ ์ ๋ฒ์ ์์๋ ๋ณ๋์ flag(1bit) ๊ฐ์ผ๋ก ํ์ํด๋๊ณ ๋๊ณ xmin์ ๋ณ๊ฒฝํ์ง๋ ์๊ธฐ์ ์ฅ์ ๋ถ์์ฉ์ผ๋ก ์ฌ์ฉ๋ ์๋ ์๋ค.
์ด ์๊ณ์น๋ vacuum_freeze_min_age ์ผ๋ก ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ, ๊ธฐ๋ณธ ์ต๋๊ฐ์ 5000๋ง์ด๋ค.
์ค์ ๋์์์๋ ์ฆ์ auto-vaccum์ด ์คํ๋๋ ๊ฑด ์๋๊ณ freeze๊ฐ ํ์ํ Tuple์ด ์๊ธฐ๋ฉด ํ ์ด๋ธ ํ๋ผ๋ฉํ(vacuum_freeze_table_age) ๊ฐ์ ์ ์ฅํด๋์๋ค๊ฐ ์ค์ ๋ ์๊ณ์น(autovacuum_freeze_max_age) ๋ฅผ ๋๊ฒ๋๋ฉด ๊ฐ์ ๋ก freeze(Anti Wraparound Vaccum)๋ฅผ ์ํํ๋ค.
๊ทธ๋ด ์ผ์ ์๊ฒ ์ง๋ง auto-vaccum์ด ๋ฌดํ์ ์คํจํด์ ํธ๋์ญ์ ID ๊ฒน์นจ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ฐ๋ ค๊ฐ ์๋ค๋ฉด, ์๋์ ๊ฒฝ๊ณ ๋ฉ์์ง๋ฅผ ๋์์ฃผ๊ณ
WARNING: database "mydb" must be vacuumed within 10985967 transactions
HINT: To avoid a database shutdown, execute a database-wide VACUUM in that database.
์ด๋ฅผ ๋ฌด์ํ๊ณ ๊ณ์ ์ฌ์ฉํ๋ค๋ฉด ์๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ๋จ๊ธฐ๋ฉฐ Postgre ์๋ฒ๋ ๋ชจ๋ ํธ๋์ญ์ ์์ ์ ์ค๋จํ๋ค.
ERROR: database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT: Stop the postmaster and use a standalone backend to VACUUM in "mydb".
๋ง์ฝ ์ฐ๋ฆฌ DB์์ ์ค๋๋ ํธ๋์ญ์ ID๊ฐ ์ด๋์ ๋์ธ์ง ๊ถ๊ธํ๋ค๋ฉด, pg_class ์ pg_database ํ ์ด๋ธ๋ก ํ์ธํ ์ ์๋ค.
-- relfrozenxid ์ vaccum ์ ๋ฆฌํ๋ ๊ธฐ์ค ํธ๋์ญ์
ID๊ฐ ์ด๋ค. (์ด ๊ฐ๋ณด๋ค ์ค๋๋ ๋ชจ๋ ID๋ freeze ์ฒ๋ฆฌ๋จ)
-- age๋ ํ์ฌ ์์ ๊ธฐ์ค, vaccum์ผ๋ก ์ ๋ฆฌ๋ ๊ฐ์ฅ ์ค๋๋ ํธ๋์ญ์
ID๊ฐ ์ด๋ค.
SELECT c.oid::regclass as table_name,
greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');
SELECT datname, age(datfrozenxid) FROM pg_database;
์ฌ๋ด์ผ๋ก Postgre 9.3 ์ดํ๋ก ๋ค์ค ํธ๋์ญ์ ID ๊ฒน์นจ๋ฌธ์ ๊ฐ ๊ณต์๋ฌธ์์ ์ถ๊ฐ๋์๋๋ฐ, ์ค๋ช ์ ์์ ํฌ๊ฒ ๋ค๋ฅด์ง ์์ผ๋ฏ๋ก ๊ถ๊ธํ๋ฉด ๋ฌธ์๋ฅผ ์ฝ์ด๋ณด์.
3๏ธโฃ PostgreSQL ์ฟผ๋ฆฌ ์คํ ๊ณํ๊ธฐ๊ฐ ์ฌ์ฉํ ์๋ฃ ํต๊ณ ์ ๋ณด(ANALYZE)๋ฅผ ๊ฐฑ์ ํ ํ์๊ฐ ์๋ค.
์ด๋ ์์๋ ์ธ๊ธํ์ง๋ง, auto-vaccum ๊ณผ์ ์์์ ํ์์ ๋ฐ๋ผ ANALYZE๋ฅผ ์ถ๊ฐ๋ก ์คํํ๋ค.
์ค์ DB ๋ฒ์ ์ ๋ฐ๊พธ๊ฑฐ๋ ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ๊ฒฝ์ฐ ์คํ๊ณํ์ด ํฌ๊ฒ ๋ฌ๋ผ์ ธ ์ฅ์ ๊ฐ ๋ฐ์ํ ์ ์๋๋ฐ, ์ด๋ ์ฟผ๋ฆฌ๋ฅผ ๊ณ์ ์คํ์์ผ์ฃผ๊ฑฐ๋ ใ ANALYZE ๋ช ๋ น์ ์ ๋ ฅํ๋ฉด Postgre ์๋ฒ๊ฐ ์๋ง์ ์คํ๊ณํ์ผ๋ก ์ต์ ํ ํด์ค๋ค.
์ฌ๋ด์ผ๋ก anaylze๋ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
# ๋ฐ์ดํฐ๋ฒ ์ด์ค
analyze;
# ํ
์ด๋ธ
analyze t1;
# ์ปฌ๋ผ
analyze t1 (c1, c2);
# ์์ธ ์ํ๋ด์ญ ํ์ธ
analyze verbose t1;
-- INFO: analyzing "public.t1"
-- INFO: "t1": scanned 443 of 443 pages, containing 100000 live rows and 0 dead rows; 30000 rows in sample, 100000 estimated total rows
-- ANALYZE
๋ํ ์๋์ ์ค์ ์ ๊ฑด๋๋ฆฐ ์ ์ด ์๋ค๋ฉด, ๊ธฐ๋ณธ์ ์ผ๋ก auto-vaccum ์ ์คํํ๋ฉด์ ํต๊ณ ์ ๋ณด๋ฅผ ๊ฐ์ด ์์งํ์ฌ ํ๋จ ๊ธฐ์ค์ ์ฌ์ฉํ๋๋ฐ ํ์ํ๋ฉด pg_class, pg_stats ๋ฑ์ ํ ์ด๋ธ๋ก ํ์ธํด๋ณผ ์ ์๋ค.
- autovacuum (๊ธฐ๋ณธ๊ฐ on) : autovacuum ํ๋ก์ธ์ค ์ฌ์ฉ ์ฌ๋ถ
- track_counts (๊ธฐ๋ณธ๊ฐ true): ๋ฐ์ดํฐ๋ฒ ์ด์ค ํต๊ณ์ ๋ณด ์์ง์ฌ๋ถ
- autovacuum_analyze_scale_factor (๊ธฐ๋ณธ๊ฐ0.1) : ํ ์ด๋ธ ๋ด์ ๋ ์ฝ๋ ๋ณ๊ฒฝ ๋น์จ
- autovacuum_analyze_threshold (๊ธฐ๋ณธ๊ฐ 50) : ์ต์ ๋ณ๊ฒฝ ๋ ์ฝ๋ ์
#------------------------------------------------------------------------------
# postgresql.conf ํ์ผ ๋ด AUTOVACUUM PARAMETERS
#------------------------------------------------------------------------------
#autovacuum = on # Enable autovacuum subprocess? 'on' ## PostgreSQL 9.x ๋ถํฐ๋ default๋ก ํ์ฑํ๋ ์ํ์
# requires track_counts to also be on.
#log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and ## 0๋ฉด autovacuum ์ผ์ด๋ ๋๋ง๋ค ๋ก๊ทธ ๋จ๊ธฐ๊ณ ,
# their durations, > 0 logs only 200ms ์ค์ ํ๋ฉด 200ms ์ด์ ๊ฑธ๋ ค ์คํ๋๋
# actions running at least this number
# of milliseconds.
#autovacuum_max_workers = 3 # max number of autovacuum subprocesses
# (change requires restart)
#autovacuum_naptime = 1min # time between autovacuum runs ## ์คํ ์ฃผ๊ธฐ.. 1min ๋ ์งง๋ค..
#autovacuum_vacuum_threshold = 50 # min number of row updates before ## vacuum ์์
์ ์ต์ ๊ฐฏ์.. 1000์ด์์ผ๋ก ๋๋ ค๋ ๋จ
# vacuum
#autovacuum_analyze_threshold = 50 # min number of row updates before ## ๋ง์ฐฌ๊ฐ์ง 500์ด์
# analyze
#autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 20ms # default vacuum cost delay for
# autovacuum, in milliseconds;
# -1 means use vacuum_cost_delay
#autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for
# autovacuum, -1 means use
# vacuum_cost_limit
Postgre ์คํ๊ณํ ๋ฐ ํต๊ณ - ์ฟผ๋ฆฌ์ฑ๋ฅ ๋ถ์ํ๊ธฐ seunghyunson
4๏ธโฃ ์ปค๋ฒ๋ฆฌ์ง ์ธ๋ฑ์ค(only index) ์๋๋ฅผ ์ํด, vm(visibility map, vm) ๊ฐฑ์ ํ ํ์ ์๋ค.
3๋ฒ ANAYLZE์ ๋ ์กฐ๊ธ ๋ค๋ฅธ๋ด์ฉ์ธ๋ฐ, postgre๋ vaccum ์์ ์ ํตํด ๋ด๋ถ์ visibility map์ ๊ฐฑ์ ํด์ ํ์ฌ์ ๋ฏธ๋์ ๋ชจ๋ ํธ๋์ญ์ ์ด ์ค์ ๋ก ์ฌ์ฉํ ํ ์ด๋ธ๋ณ ์ง๋๋ฅผ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋๋ค.
์ด๋ ์ธ๋ฑ์ค ์ ์ฉ ์ฟผ๋ฆฌ๋ค (์ปค๋ฒ๋ฆฌ์ง ์ธ๋ฑ์ค) ์ ๋ํด ๋น ๋ฅธ ์๋ต์ ์ ๊ณตํ๋๋ฐ ์ฌ์ฉ๋๋ค. PostgreSQL์ ์ธ๋ฑ์ค๋ ์ด๋ค ์๋ฃ๋ฅผ ์ด๋ค DB ์ธ์ ์ ๋ณด์ฌ์ค์ผํ ์ง ๊ฒฐ์ ํ๊ธฐ ์ํด ํ ์ด๋ธ์ page๋ฅผ ์ฝ์ด๋ณด๋๋ฐ, ๋ง์ฝ vaccum ๋ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋ visibility map ๋ง์ผ๋ก ์ฐพ์์ ์ค์ page ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ์ง ์๊ณ ๋ฐ๋ก ์ฐพ๊ธฐ์ ๋ฐ์ดํฐ๊ฐ ๋ง์ ํ ์ด๋ธ์ ์กฐํํ ๋ Disk I/O๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์๋ค.
๐ ์ค์ ๋ก ์์๋ Vaccum ์ด์
์ฌ๋ด์์ ์ฌ์ฉ์์ ํ์ต์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ์ํด SpringStateMachine๋ฅผ ์ฌ์ฉํด์ ๊ฐ๊ฐ์ ์ฌ์ฉ์์ ํ์ต์ํ(Context) ๊ฐ์ฒด๋ฅผ ์ง๋ ฌํํ ๋ค ๊ณ์ ์ ๋ฐ์ดํธํ์ฌ DB์ ์ ์ฅํ๊ณ ์์๋ค.
create table state_machine (
machine_id varchar(255) not null,
state varchar(255),
state_machine_context oid,
primary key (machine_id)
);
postgres์์๋ lobs(Large Objects) ๋ฅผ ์ ์ฅํ ๋ ์๋ณ์ฉ oid ๊ฐ ์นผ๋ผ์ ์ ์ฅํด๋๊ณ ์ค์ ๋ฐ์ดํฐ๋ pg_largeobject ํ ์ด๋ธ์ ๋ฐ๋ก ์ ์ฅ๋๋ค.
๐ค ๊ทธ๋์ ๋ญ๊ฐ ๋ฌธ์ ์์ฃ ?
์ฌ๊ธฐ์ ๋ฌธ์ ๊ฐ ๋์๋๊ฑด oid ๊ฐ์ auto-vaccum์ด ์ฒ๋ฆฌํด์ฃผ์ง๋ง large_object์ ์ ์ฅ๋์ด์๋ ๋ฐ์ดํฐ๋ oid์์ ๋งํฌ ๋๋ฌธ์ dead tuple๋ก ์ทจ๊ธ๋์ง ์์ ์ง์์ง์ง ์์๋ค. ์๋ง oid๋ฅผ ์ ๋ฐ์ดํธํ๋๋ผ๋ ๊ธฐ์กด ๋ฐ์ดํฐ ์์ ์ด ์๋ ์๋ก์ด row๊ฐ ์ถ๊ฐ๋๋ ํ์์ด์๊ธฐ ๋๋ฌธ์ผ๋ก ์ถ์ธกํ๋ค.
ํ์ต์ํ ํน์ฑ์ ๊ต์ฅํ ์์ฃผ ์ ๋ฐ์ดํธ ๋๋๋ฐ, ์ ํ ์ด๋ธ์ ๋ณด์ด๋ data(Hex)๊ฐ ์์ ๋ ๋ ๋ง๋ค dead tuple์ด ๊ณ์ ์์ด๊ฒ ๋์ด ์๋์ผ๋ก vaccum์ ํด์ฃผ์ง ์์ผ๋ฉด ์ ์ ์ฑ๋ฅ์ด ๋๋ ค์ง๋ ์ด์๊ฐ ์์๋ค. ๐ฃ ์์๋ ์ธ๊ธํ์ง๋ง Dead Tuple์ด ์์ด๋ฉด ๋ถํ์ํ page๋ฅผ ์์ฒญ๋๊ฒ ์ฝ์ด Disk I/O๊ฐ ์ฆ๊ฐํ๋๊ฑด ๋ฌผ๋ก ์คํ๊ณํ์ด ๋ฐ๋์ด ์ธ๋ฑ์ค ์์ฒด๋ฅผ ์ํ๊ฒ ๋ณ๊ฒฝ๋ ์๋ ์๋ค.
๋ฌธ์ ํด๊ฒฐ์ ๊ฐ๋จํ๋ค. ์๋ ๋ช ๋ น์ ํตํด oid ์ ๋งํฌ๋ฅผ ํด์ ํด์ฃผ๋ฉด auto-vaccum์ด ๋์ํ๋ค.
SELECT lo_unlink($oid๊ฐ)
๋คํํ ์ด๋ ์๋ ค์ง ์ด์๋ผ์, Postgre github์ ์ด๋ฏธ ๋ง๋ค์ด๋ VaccumIO๊ฐ ์ด๋ฏธ ์๋ค. ์ด๋ ๋ชจ๋ ํ ์ด๋ธ์ oid์ pg_largeobject์ oid๋ฅผ ๋น๊ตํ์ฌ ์ง์์ง oid๋ฅผ unlink ํ๋๋ก ๋์ํ๋ค.
๋ค๋ง ์ฃผ์ํด์ผํ ๊ฒ์ lo_unlink ์์ฒด์ ์ฟผ๋ฆฌ๊ฐ ๋ฌด๊ฒ๊ณ , lock์ ๊ฑธ๊ธฐ์ ์ด์์ค์๋ ์กฐ์ฌํด์ ์ฌ์ฉํด์ผํ๋ค. ํ ์คํธํด๋ณด๋ ์์ ์๋น์ค๋ฅผ ์ค์งํด์ผํ ์ ๋๋ ์๋๋ผ์ ์๋ฒฝ ์๊ฐ๋ ๋ฐฐ์น ์คํฌ๋ฆฝํธ๋ฅผ ํตํด VaccumIO๋ฅผ ์คํํ๋๋ก ํด์ ํด๊ฒฐํ์๋ค.
์๋ ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋์ปค ์ด๋ฏธ์ง์ ๋ง์์ `docker run` ์ผ๋ก ์คํํ ์ ์๊ฒ ๋ง๋ค์ด ์๋ฒฝ๋ง๋ค vaccumIo๋ฅผ ์คํํ๋๋ก ๋ง๋ค์๋ค.
# vacuumioscript.sh
#!/bin/bash
echo "*:*:*:*:$DB_PW" > ~/.pgpass
chmod 600 ~/.pgpass
vacuumlo -h ${DB_URL} -p 5432 -U postgres -w ${DRY_RUN} -v lms -l ${LIMIT}
FROM postgres:14.3-alpine
ENV DB_URL=...
ENV DB_PW=...
ENV DRY_RUN="--dry-run" # vaccumlo --dry-run ์ํตํด ํ์ฌ DB๊ฐ large object๋ฅผ ์ฌ์ฉํ๋์ง ํ์ธ๊ฐ๋ฅ
ENV LIMIT=500
COPY vacuumioscript.sh vacuumioscript.sh
RUN chmod 775 vacuumioscript.sh
ENTRYPOINT ["./vacuumioscript.sh"]
๊ทธ ๋น์์๋ ๋ณ ์๊ฐ์์ด ํฐ๋ฐ์ดํฐ? ๊ทธ๋ผ LOB์ ์ ์ฅํ๋ฉด ๋๊ฒ ์งํ๊ณ ์ ํํ๊ณ vaccum ๋์์ ๋ํด์๋ ๋ฌด์งํ๋๋ฐ
์ดํ ๋ ์ฐพ์๋ณด๋ LargeObject(oid)๊ฐ ์๋ bytea ๋ก ๋ฐ๋ก ์ ์ฅํ๋ฉด auto-vaccum์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋จ์ ํ์ธํ๊ณ ํ์ ์ ๋ณ๊ฒฝํด์ ํด๊ฒฐํ๋ค. ๋ฌผ๋ก ์ผ๋ฐ์ ์ธ ํ ์ด๋ธ์ lob -> bytea ๋ก ๋ณ๊ฒฝํ๋ฉด์ ์กฐํ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ง๋ง, ํด๋น ํ ์ด๋ธ์ stateContext ์ ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์ด์๊ธฐ๋ ํ๊ณ ์ฌ์ฉ์ 1๋ช ์ด ๊ณ ์ ํ ID๋ก ์์ ์ ๋ฐ์ดํฐ๋ง ์ฌ์ฉํ๋ ๊ตฌ์กฐ๋ผ ๋ณ ๋ฌธ์ ์์๋ค.
@Entity
@Table(name = "jiwon_state_machine")
@JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator::class)
class LmsStateMachineEntity : RepositoryStateMachine() {
@Id
@Column(name = "machine_id")
private var machineId: String? = null
@Column(name = "state")
private var state: String? = null
// @Lob ์ ๊ฑฐ, bytea ํ์
์ผ๋ก ๋ฐ๋ก ์ ์ฅ
@Column(name = "state_machine_context", length = 10240)
private var stateMachineContext: ByteArray? = null
}
create table jiwon_state_machine (
machine_id varchar(255) not null,
state varchar(255),
state_machine_context bytea,
primary key (machine_id)
);
๐ ๋ค๋ฅธ ์ฌ๋ก
์ฌ๋ด์ผ๋ก Postgre๋ฅผ ๋ฅํ๊ฒ ์ฌ์ฉํ๋ค๋ฉด ํ๋ฒ์ฉ ๊ฒช๋ ๋ฌธ์ ๊ฐ Vaccum ์ด์์ธ๋ฐ, ๋ด๊ฐ ๊ฒช์ @Lob๊ณผ ๊ฐ์ ํน์ํ ์ํฉ์ด ์๋๋๋ผ๋ ๋ฐ์ดํฐ๋์ด ๋ง์์ง๋ฉด auto-vaccum์ด ์ ๋์ง์์ Dead Tuple๋ก ์ธํด ๋น์ทํ ์ด์๊ฐ ๋ฐ์ํ ์ ์๋ค. ์๋ 29CM Auto-vaccum ์ฅ์ ๋์๊ธฐ๋ ๊ผญ ํ๋ฒ ์ฝ์ด๋ณด๊ธฐ๋ฅผ ์ถ์ฒํ๋ค.
๐ ๋ ํผ๋ฐ์ค
ํ๋์ ์ดํด๋ณด๋ PostgreSQL - https://d2.naver.com/helloworld/227936
PostgreSQL Vaccum์ ๋ํ ๊ฑฐ์ ๋ชจ๋ ๊ฒ https://techblog.woowahan.com/9478/
Postgre13 Docs - https://postgresql.kr/docs/13/routine-vacuuming.html
Postgre Wiki - ๊ฐ๋ฐ์ฒ ํ https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
'๐ฑBackend > DB(MySQL,PostgreSQL)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ ๋ฆฌ์ค-6 (0) | 2021.10.27 |
---|---|
์ ๋ฆฌ์ค-5 (0) | 2021.10.27 |
์ ๋ฆฌ์ค - 4 (0) | 2021.10.20 |
์ ๋ฆฌ์ค - 3 (0) | 2021.10.20 |
์ ๋ฆฌ์ค -2 (0) | 2021.10.20 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev