[Daily morning study] PostgreSQL vs MySQL ๋น๊ต
#daily morning study
PostgreSQL vs MySQL ๋น๊ต
๊ฐ์
PostgreSQL๊ณผ MySQL์ ๋ ๋ค ์คํ์์ค ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ด์ง๋ง, ์ค๊ณ ์ฒ ํ๊ณผ ์ง์ ๊ธฐ๋ฅ์์ ์ฐจ์ด๊ฐ ๊ฝค ํฌ๋ค.
- MySQL: ์๋์ ๋จ์ํจ์ ์ฐ์ ์ํด์ ์ฝ๊ธฐ ์ค์ฌ ์ํฌ๋ก๋์ ๊ฐํ๋ค.
- PostgreSQL: ํ์ค ์ค์์ ํ์ฅ์ฑ์ ์ฐ์ ์ํด์ ๋ณต์กํ ์ฟผ๋ฆฌ์ ๋ค์ํ ๋ฐ์ดํฐ ํ์ ์ ๊ฐํ๋ค.
ํต์ฌ ์ฐจ์ด์ ์์ฝ
| ํญ๋ชฉ | PostgreSQL | MySQL |
|---|---|---|
| ๋ผ์ด์ ์ค | PostgreSQL License (์์ ๋ ๋์) | GPL v2 (Oracle ์์ ) |
| SQL ํ์ค ์ค์ | ๋งค์ฐ ๋์ | ์ค๊ฐ (์ผ๋ถ ๋นํ์ค ๋ฌธ๋ฒ ์ฌ์ฉ) |
| ๊ธฐ๋ณธ ์คํ ๋ฆฌ์ง ์์ง | ๋จ์ผ (Heap) | ๋ค์ค (InnoDB, MyISAM ๋ฑ) |
| MVCC ๊ตฌํ | ์์ฒด MVCC | InnoDB MVCC |
| JSON ์ง์ | JSONB (๋ฐ์ด๋๋ฆฌ JSON, ์ธ๋ฑ์ค ์ง์) | JSON (ํ ์คํธ ๊ธฐ๋ฐ) |
| ๋ฐฐ์ด/๋ฒ์ ํ์ | ์ง์ | ๋ฏธ์ง์ |
| ํํ ์คํธ ๊ฒ์ | ์ง์ | ์ง์ (๋จ, PostgreSQL์ด ๋ ๊ฐ๋ ฅ) |
| ํํฐ์ ๋ | ์ ์ธ์ ํํฐ์ ๋ (v10+) | ์ง์ |
| ๋ณต์ | ์คํธ๋ฆฌ๋ฐ ๋ณต์ , ๋ ผ๋ฆฌ์ ๋ณต์ | ๋ฐ์ด๋๋ฆฌ ๋ก๊ทธ ๊ธฐ๋ฐ ๋ณต์ |
| ์๋์ฐ ํจ์ | ์์ ์ง์ | ์ง์ (v8+) |
| CTE (WITH ์ ) | ์์ ์ง์ | ์ง์ (v8+) |
| ์ปค๋ฎค๋ํฐ/ํด๋ผ์ฐ๋ ์ง์ | RDS, Cloud SQL, Azure ๋ฑ | RDS, Cloud SQL, Azure ๋ฑ |
์ํคํ ์ฒ ์ฐจ์ด
ํ๋ก์ธ์ค ๋ชจ๋ธ
- PostgreSQL: ์ฐ๊ฒฐ๋ง๋ค ๋ณ๋์ OS ํ๋ก์ธ์ค๋ฅผ ์์ฑํ๋ค. ๋ฉ๋ชจ๋ฆฌ ๊ฒฉ๋ฆฌ๊ฐ ํ์คํ์ง๋ง, ์ฐ๊ฒฐ์ด ๋ง์์ง๋ฉด ์ค๋ฒํค๋๊ฐ ์ปค์ง๋ค. ์ด ๋๋ฌธ์ PgBouncer ๊ฐ์ ์ปค๋ฅ์ ํ๋ฌ๋ฅผ ํจ๊ป ์ฐ๋ ๊ฒ ์ผ๋ฐ์ ์ด๋ค.
- MySQL: ์ค๋ ๋ ๊ธฐ๋ฐ ๋ชจ๋ธ์ด๋ค. ์ฐ๊ฒฐ๋น ์ค๋ ๋๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํ๋ก์ธ์ค ์์ฑ ๋น์ฉ์ด ์์ด ๋๋ ์ฐ๊ฒฐ์ ์๋์ ์ผ๋ก ์ ๋ฆฌํ๋ค.
์คํ ๋ฆฌ์ง ์์ง
MySQL์ ์คํ ๋ฆฌ์ง ์์ง์ ๊ต์ฒดํ ์ ์๋ ํ๋ฌ๊ทธ์ธ ์ํคํ ์ฒ๋ฅผ ๊ฐ๊ณ ์๋ค. ๊ธฐ๋ณธ๊ฐ์ InnoDB์ธ๋ฐ, ์์ ์๋ ํธ๋์ญ์ ์ด ์ ๋๋ MyISAM์ด ๊ธฐ๋ณธ์ด์๋ค. PostgreSQL์ ๋จ์ผ ์คํ ๋ฆฌ์ง ์์ง์ ์ฌ์ฉํ๋ ๋์ ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ๋ ๊น์ด ์ต์ ํํ๋ค.
MVCC ๊ตฌํ ๋ฐฉ์
MVCC(Multi-Version Concurrency Control)๋ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๊ฐ ์๋ก ๋ธ๋กํนํ์ง ์๋๋ก ํ๋ ํต์ฌ ๊ธฐ๋ฒ์ด๋ค.
PostgreSQL์ MVCC
- ๋ ์ฝ๋์
xmin,xmax๊ฐ์ ํธ๋์ญ์ ID๋ฅผ ์ง์ ์ ์ฅํ๋ค. - ๊ตฌ๋ฒ์ ๋ ์ฝ๋๋ฅผ ๊ฐ์ ํ
์ด๋ธ์ ๋จ๊ฒจ๋๋ค๊ฐ
VACUUMํ๋ก์ธ์ค๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก ์ ๋ฆฌํ๋ค. - ์ฐ๊ธฐ๊ฐ ๋ง์ผ๋ฉด dead tuple์ด ์์ฌ VACUUM ํ๋์ด ์ค์ํด์ง๋ค.
MySQL InnoDB์ MVCC
- ์ธ๋ ๋ก๊ทธ(undo log)๋ฅผ ๋ณ๋ ๊ณต๊ฐ์ ์ ์งํ๋ค.
- ๊ตฌ๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ํ ์ด๋ธ์ด ์๋ undo tablespace์ ๋ณด๊ดํ๋ฏ๋ก ํ ์ด๋ธ bloat ๋ฌธ์ ๊ฐ ์๋ค.
- ๋์ ์ธ๋ ๋ก๊ทธ๊ฐ ๊ธธ์ด์ง๋ฉด ๋กค๋ฐฑ ๋น์ฉ์ด ์ปค์ง๋ค.
๋ฐ์ดํฐ ํ์ ๋น๊ต
PostgreSQL์ด ์ง์ํ๋ ํ์ ์ด ํจ์ฌ ๋ค์ํ๋ค.
PostgreSQL ๊ณ ์ ํ์
-- ๋ฐฐ์ด ํ์
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
tags TEXT[]
);
INSERT INTO orders (tags) VALUES (ARRAY['urgent', 'bulk']);
-- ๋ฒ์ ํ์
CREATE TABLE reservations (
id SERIAL PRIMARY KEY,
during TSRANGE
);
INSERT INTO reservations (during) VALUES ('[2026-05-17, 2026-05-20)');
-- JSONB (๋ฐ์ด๋๋ฆฌ JSON, GIN ์ธ๋ฑ์ค ์ง์)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
attributes JSONB
);
CREATE INDEX idx_attributes ON products USING GIN (attributes);
SELECT * FROM products WHERE attributes @> '{"color": "red"}';
MySQL์ JSON
-- JSON ํ์
(ํ
์คํธ ๊ธฐ๋ฐ)
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
attributes JSON
);
-- ํจ์ํ ์ธ๋ฑ์ค๋ก ํน์ ๊ฒฝ๋ก์ ์ธ๋ฑ์ค ์์ฑ ๊ฐ๋ฅ (v5.7.8+)
ALTER TABLE products ADD INDEX idx_color ((CAST(attributes->>'$.color' AS CHAR(50))));
PostgreSQL์ JSONB๋ ์ ์ฅ ์ ํ์ฑํด์ ๋ฐ์ด๋๋ฆฌ๋ก ๋ณด๊ดํ๊ธฐ ๋๋ฌธ์ ์ฝ๊ธฐ ์๋๊ฐ ๋น ๋ฅด๊ณ GIN ์ธ๋ฑ์ค๋ฅผ ํตํด JSON ๋ด๋ถ ๊ฐ์ผ๋ก ํจ์จ์ ์ธ ๊ฒ์์ด ๋๋ค.
ํธ๋์ญ์ ๊ณผ ๊ฒฉ๋ฆฌ ์์ค
๋ ๋ค ACID๋ฅผ ๋ณด์ฅํ์ง๋ง ์ง์ํ๋ ๊ฒฉ๋ฆฌ ์์ค์ ์ฐจ์ด๊ฐ ์๋ค.
| ๊ฒฉ๋ฆฌ ์์ค | PostgreSQL | MySQL InnoDB |
|---|---|---|
| READ UNCOMMITTED | ์ง์ (์ค์ ๋ก๋ RC์ฒ๋ผ ๋์) | ์ง์ |
| READ COMMITTED | ์ง์ | ์ง์ |
| REPEATABLE READ | ์ง์ | ์ง์ (๊ธฐ๋ณธ๊ฐ) |
| SERIALIZABLE | ์ง์ | ์ง์ |
MySQL InnoDB์ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ REPEATABLE READ์ด๊ณ , PostgreSQL์ ๊ธฐ๋ณธ๊ฐ์ READ COMMITTED๋ค.
PostgreSQL์ SERIALIZABLE์ SSI(Serializable Snapshot Isolation)๋ฅผ ๊ตฌํํด์ ์ง์ง ์ง๋ ฌํ ๊ฐ๋ฅ์ฑ์ ๋ณด์ฅํ๋ค. MySQL์ SERIALIZABLE์ SELECT์ ๊ณต์ ๋ฝ์ ๊ฑธ์ด ๊ตฌํํ๋ฏ๋ก ์ฑ๋ฅ ๋น์ฉ์ด ๋ ํฌ๋ค.
๋ณต์ (Replication)
PostgreSQL
- ์คํธ๋ฆฌ๋ฐ ๋ณต์ : WAL(Write-Ahead Log)์ ์ค์๊ฐ์ผ๋ก ์คํ ๋ฐ์ด์ ์ ์กํ๋ค. ๋ฌผ๋ฆฌ์ ๋ณต์ ๋ผ์ ๋ฐ์ดํธ ๋จ์๋ก ๋์ผํ ๋ณต์ฌ๋ณธ์ ๋ง๋ ๋ค.
- ๋ ผ๋ฆฌ์ ๋ณต์ : ํ ์ด๋ธ ๋จ์๋ก ๋ณ๊ฒฝ์ฌํญ์ ๋ฐํ/๊ตฌ๋ ํ๋ ๋ฐฉ์์ด๋ค. ๋ค๋ฅธ ๋ฒ์ ๊ฐ ๋ณต์ ๋ ์ ํ์ ๋ณต์ ๊ฐ ๊ฐ๋ฅํ๋ค.
- Patroni, repmgr ๊ฐ์ ์ธ๋ถ ๋๊ตฌ๋ก ์๋ ํ์ผ์ค๋ฒ๋ฅผ ๊ตฌ์ฑํ๋ ๊ฒ ์ผ๋ฐ์ ์ด๋ค.
MySQL
- ๋ฐ์ด๋๋ฆฌ ๋ก๊ทธ(binlog) ๊ธฐ๋ฐ ๋ณต์ : Statement ๊ธฐ๋ฐ, Row ๊ธฐ๋ฐ, Mixed ๋ฐฉ์์ด ์๋ค. Row ๊ธฐ๋ฐ์ด ๊ฐ์ฅ ์์ ์ ์ด๋ค.
- GTID(Global Transaction ID): ํธ๋์ญ์ ๋ง๋ค ๊ณ ์ ID๋ฅผ ๋ถ์ฌํด์ ๋ณต์ ์์น๋ฅผ ์ถ์ ํ๋ค. ํ์ผ์ค๋ฒ ์ ๋ณต์ ์ฌ๊ตฌ์ฑ์ด ์ฝ๋ค.
- MySQL Router, Orchestrator ๋ฑ์ผ๋ก HA๋ฅผ ๊ตฌ์ฑํ๋ค.
์ฑ๋ฅ ํน์ฑ
์ฝ๊ธฐ ์ฑ๋ฅ
๋จ์ํ OLTP ์ฝ๊ธฐ(PK ์กฐํ, ๋จ์ JOIN)์์๋ MySQL์ด ์กฐ๊ธ ๋ ๋น ๋ฅธ ๊ฒฝํฅ์ด ์๋ค. ์ค๋ ๋ ๋ชจ๋ธ๊ณผ ์ฟผ๋ฆฌ ์บ์(deprecated๋์ง๋ง) ๋๋ถ์ ๋จ์ ์ฟผ๋ฆฌ ์ฒ๋ฆฌ๋์ด ๋๋ค.
๋ณต์กํ ๋ถ์ ์ฟผ๋ฆฌ(์๋์ฐ ํจ์, ๋ณต์กํ ์ง๊ณ, ์๋ธ์ฟผ๋ฆฌ)์์๋ PostgreSQL์ ํ๋๋๊ฐ ๋ ์ ๊ตํ ์คํ ๊ณํ์ ์ธ์ด๋ค.
์ฐ๊ธฐ ์ฑ๋ฅ
PostgreSQL์ dead tuple ๋๋ฌธ์ ์ฐ๊ธฐ๊ฐ ๋ง์ ํ
์ด๋ธ์์ VACUUM ๋ถํ๊ฐ ์๊ธด๋ค. autovacuum ์ค์ ์ ์ ํ๋ํด์ผ ํ๋ค.
MySQL InnoDB๋ ์ธ๋ ๋ก๊ทธ ๋ฐฉ์์ด๋ผ ํ ์ด๋ธ bloat์ ์์ง๋ง, ์ธ๋ ๋ก๊ทธ๊ฐ ๊ธธ์ด์ง๋ ๊ธด ํธ๋์ญ์ ์๋ ์ทจ์ฝํ๋ค.
์ธ์ ๋ฌด์์ ์ ํํ ๊น
PostgreSQL์ ์ ํํ๋ ๊ฒฝ์ฐ
- ๋ณต์กํ ์ฟผ๋ฆฌ, ์๋์ฐ ํจ์, CTE๋ฅผ ๋ง์ด ์ธ ๋
- JSON ๋ฐ์ดํฐ๋ฅผ ์กฐ๊ฑด ๊ฒ์ํ๊ฑฐ๋ ์ธ๋ฑ์ฑํด์ผ ํ ๋
- ์ง๋ฆฌ ๋ฐ์ดํฐ(PostGIS ํ์ฅ)๋ฅผ ๋ค๋ฃฐ ๋
- SQL ํ์ค์ ์๊ฒฉํ๊ฒ ๋ฐ๋ฅด๋ ํ๊ฒฝ
- SERIALIZABLE ๊ฒฉ๋ฆฌ๊ฐ ์ง์ง๋ก ํ์ํ ๊ธ์ต, ๊ฒฐ์ ์์คํ
MySQL์ ์ ํํ๋ ๊ฒฝ์ฐ
- ๋จ์ CRUD ์ค์ฌ์ ์น ์ ํ๋ฆฌ์ผ์ด์
- ์ฝ๊ธฐ ์ฒ๋ฆฌ๋์ด ์๋์ ์ผ๋ก ๋ง์ ์๋น์ค (SNS ํผ๋ ๋ฑ)
- ๊ธฐ์กด ๋ ๊ฑฐ์ ์ฝ๋๋ ์ธํ๋ผ๊ฐ MySQL ๊ธฐ๋ฐ์ผ ๋
- ๊ด๋ฆฌ ์ธ๋ ฅ์ด MySQL์ ์ต์ํ ํ๊ฒฝ
ํด๋ผ์ฐ๋ ๋งค๋์ง๋ ์๋น์ค
| ์๋น์ค | PostgreSQL | MySQL |
|---|---|---|
| AWS | RDS for PostgreSQL, Aurora PostgreSQL | RDS for MySQL, Aurora MySQL |
| GCP | Cloud SQL for PostgreSQL, AlloyDB | Cloud SQL for MySQL |
| Azure | Azure Database for PostgreSQL | Azure Database for MySQL |
Aurora PostgreSQL๊ณผ Aurora MySQL์ AWS๊ฐ ํด๋ผ์ฐ๋์ ๋ง๊ฒ ์ฌ์ค๊ณํ ์์ง์ด๋ผ ๊ธฐ์กด ํธํ์ฑ์ ์ ์งํ๋ฉด์ ์ฑ๋ฅ์ ๋์ธ ๋ฒ์ ์ด๋ค.
์ ๋ฆฌ
PostgreSQL์ ๊ธฐ๋ฅ์ด ํ๋ถํ๊ณ SQL ํ์ค์ ์ ๋ฐ๋ฅด๋ฉฐ, ๋ณต์กํ ์ฟผ๋ฆฌ์ ๋ค์ํ ๋ฐ์ดํฐ ํ์ ์ด ํ์ํ ์์คํ ์ ์ ํฉํ๋ค. MySQL์ ๋จ์ OLTP ์ํฌ๋ก๋์ ๋๋ ์ฝ๊ธฐ ์ฒ๋ฆฌ์์ ๊ฐ๋ณ๊ณ ๋น ๋ฅด๋ค๋ ์ฅ์ ์ด ์๋ค. ์ด๋ค ๊ฒ ๋ซ๋ค๊ธฐ๋ณด๋ค๋ ์๋น์ค์ ์ฟผ๋ฆฌ ํจํด๊ณผ ํ์ ๊ฒฝํ์ ๊ธฐ๋ฐ์ผ๋ก ์ ํํ๋ ๊ฒ ํ์ค์ ์ด๋ค.