Ver3.0 ๐ PostgreSQL Foreign Data Wrapper๋ก ์ธ๋ถ DB์ ์นํด์ง๊ธฐ

๐ PostgreSQL Foreign Data Wrapper๋ก ์ธ๋ถ DB์ ์นํด์ง๊ธฐ
๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ๋๋ก ์ฐ๊ฒฐํ๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ์
์๋ ! ์ค๋์ ์ ๋ง ์ฌ๋ฏธ์๋ ์ฃผ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ด. ๐ PostgreSQL์ Foreign Data Wrapper(FDW)๋ผ๋ ๋ ์์ธ๋ฐ, ์ด๊ฒ ๋ญ๋๋ฉด ๋ง์ด์ผ... ๋ง์น ์ฌ๋ฌ ๋๋ผ ์ธ์ด๋ฅผ ๋์ํต์ญํด์ฃผ๋ ํต์ญ์ฌ ๊ฐ์ ์กด์ฌ๋ผ๊ณ ๋ณด๋ฉด ๋ผ. ์๋ก ๋ค๋ฅธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ค์ด ๊ฐ์์ ์ธ์ด๋ก ๋งํ๊ณ ์์ ๋, PostgreSQL์ด ์ค๊ฐ์์ "์ผ, ๋ํฌ๋ค ๋ํ ์ข ํ์!"๋ผ๊ณ ์ฐ๊ฒฐํด์ฃผ๋ ๊ฑฐ์ง.
์์ฆ ์ค๋ฌด์์๋ ํ๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ง ์ฐ๋ ๊ฒฝ์ฐ๊ฐ ๊ฑฐ์ ์์ด. MySQL์ ๊ณ ๊ฐ ๋ฐ์ดํฐ๊ฐ ์๊ณ , MongoDB์ ๋ก๊ทธ ๋ฐ์ดํฐ๊ฐ ์๊ณ , Oracle์ ๋ ๊ฑฐ์ ์์คํ ์ด ๋์๊ฐ๊ณ ... ์ด๋ฐ ์ํฉ์์ ๋ฐ์ดํฐ๋ฅผ ํตํฉํด์ ๋ด์ผ ํ ๋๊ฐ ๋ง๊ฑฐ๋ . ๊ทธ๋ด ๋ FDW๊ฐ ์ง์ง ๋น์ ๋ฐํ์ง! ๐
๐ฏ Foreign Data Wrapper๊ฐ ๋ญ๋ฐ?
FDW๋ PostgreSQL 9.1 ๋ฒ์ ๋ถํฐ ์ ์์ผ๋ก ๋์ ๋ ๊ธฐ๋ฅ์ด์ผ. SQL/MED(Management of External Data) ํ์ค์ ๋ฐ๋ฅด๋ ๊ฑด๋ฐ, ์ฝ๊ฒ ๋งํ๋ฉด ์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ง์น ๋ก์ปฌ ํ ์ด๋ธ์ฒ๋ผ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ์ธํฐํ์ด์ค๋ผ๊ณ ๋ณด๋ฉด ๋ผ.
์ผ๋ฐ์ ์ผ๋ก ๋ค๋ฅธ DB์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ๊ฐ DB์ ์ฐ๊ฒฐํ๊ณ , ์ฟผ๋ฆฌ ๋ ๋ฆฌ๊ณ , ๊ฒฐ๊ณผ ๋ฐ์์ ์กฐํฉํ๊ณ ... ์์ฒญ ๋ณต์กํ์ง? ๊ทผ๋ฐ FDW๋ฅผ ์ฐ๋ฉด PostgreSQL ์์์ SELECT ๋ฌธ ํ๋๋ก ๋๋. ๋ง์น ๊ฐ์ DB์ ์๋ ํ ์ด๋ธ์ฒ๋ผ ๋ง์ด์ผ!
์๋ฅผ ๋ค์ด๋ณผ๊ฒ. ๋ํฌ ํ์ฌ์ MySQL์ ๊ณ ๊ฐ ์ ๋ณด๊ฐ ์๊ณ , PostgreSQL์ ์ฃผ๋ฌธ ์ ๋ณด๊ฐ ์๋ค๊ณ ํด๋ณด์. ๊ณ ๊ฐ๋ณ ์ฃผ๋ฌธ ํต๊ณ๋ฅผ ๋ฝ์ผ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น? ๐ค
FDW ์์ด:
// ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์์
1. MySQL ์ฐ๊ฒฐ โ ๊ณ ๊ฐ ๋ฐ์ดํฐ ์กฐํ
2. PostgreSQL ์ฐ๊ฒฐ โ ์ฃผ๋ฌธ ๋ฐ์ดํฐ ์กฐํ
3. ๋ฉ๋ชจ๋ฆฌ์์ ๋ ๋ฐ์ดํฐ ์กฐ์ธ
4. ๊ฒฐ๊ณผ ์ฒ๋ฆฌ
FDW ์ฌ์ฉ:
-- PostgreSQL์์ ๋ฐ๋ก
SELECT c.name, COUNT(o.order_id) as order_count
FROM mysql_customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.name;
์ด๋? ํจ์ฌ ๊ฐ๋จํ์ง? ๐
๐ง FDW์ ์๋ ์๋ฆฌ
์, ์ด์ FDW๊ฐ ์ด๋ป๊ฒ ๋ง๋ฒ์ ๋ถ๋ฆฌ๋์ง ์ข ๋ ๊น์ด ๋ค์ด๊ฐ๋ณผ๊น? ์ฌ์ค ๋ง๋ฒ์ด ์๋๋ผ ์์ฃผ ์ฒด๊ณ์ ์ธ ๊ตฌ์กฐ๋ก ๋์ด ์์ด.
๐ FDW ์ํคํ ์ฒ
FDW๋ ํฌ๊ฒ ์ธ ๊ฐ์ง ๋ ์ด์ด๋ก ๊ตฌ์ฑ๋ผ:
1. SQL ์ธํฐํ์ด์ค ๋ ์ด์ด ๐จ
์ฌ์ฉ์๊ฐ ์ผ๋ฐ SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ฉด, PostgreSQL์ ์ฟผ๋ฆฌ ํ์๊ฐ ์ด๊ฑธ ๋ฐ์. ์ด๋ ์ธ๋ถ ํ
์ด๋ธ(Foreign Table)์ธ์ง ๋ก์ปฌ ํ
์ด๋ธ์ธ์ง ๊ตฌ๋ถํ์ง.
2. FDW ํธ๋ค๋ฌ ๋ ์ด์ด โ๏ธ
๊ฐ ์ธ๋ถ ๋ฐ์ดํฐ ์์ค๋ง๋ค ์ ์ฉ ํธ๋ค๋ฌ๊ฐ ์์ด. mysql_fdw, oracle_fdw, mongo_fdw ๊ฐ์ ๊ฑฐ์ง. ์ด ํธ๋ค๋ฌ๋ค์ด PostgreSQL์ ์ฟผ๋ฆฌ๋ฅผ ํด๋น DB์ ์ธ์ด๋ก ๋ฒ์ญํด์ค.
3. ๋ฐ์ดํฐ ์ ์ก ๋ ์ด์ด ๐
์ค์ ๋ก ์ธ๋ถ DB์ ํต์ ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ถ๋ถ์ด์ผ. ๋คํธ์ํฌ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ง.
์ฌ๊ธฐ์ ์ฌ๋ฏธ์๋ ๊ฑด, PostgreSQL์ ์ฟผ๋ฆฌ ์ตํฐ๋ง์ด์ ๊ฐ FDW๋ฅผ ํตํ ์ฟผ๋ฆฌ๋ ์ต์ ํํ๋ค๋ ๊ฑฐ์ผ. ์๋ฅผ ๋ค์ด WHERE ์กฐ๊ฑด์ด ์์ผ๋ฉด, ๊ฐ๋ฅํ ๊ฒฝ์ฐ ๊ทธ ์กฐ๊ฑด์ ์ธ๋ถ DB๋ก ํธ์๋ค์ด(push-down)ํด์ ๋คํธ์ํฌ ํธ๋ํฝ์ ์ค์ฌ. ๋๋ํ์ง? ๐ง
๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ค ๊ฐ์ ธ์์ PostgreSQL์์ ํํฐ๋งํ๋ ๊ฒ ์๋๋ผ, ํํฐ๋ง ์กฐ๊ฑด์ ์ธ๋ถ DB์ ๋ณด๋ด์ ๊ฑฐ๊ธฐ์ ๋จผ์ ๊ฑธ๋ฌ์ง ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ค๋ ๊ฑฐ์ผ. ์๋ฅผ ๋ค์ด "WHERE age > 30" ์กฐ๊ฑด์ด ์์ผ๋ฉด, ์ด ์กฐ๊ฑด์ MySQL์ ๋ณด๋ด์ MySQL์ด 30์ธ ์ด์๋ง ์ถ๋ ค์ ๋ณด๋ด์ฃผ๋ ์์ด์ง.
๐ ๏ธ ์ค์ ! FDW ์ค์นํ๊ณ ์ค์ ํ๊ธฐ
์ด๋ก ์ ์ด ์ ๋๋ฉด ์ถฉ๋ถํ๊ณ , ์ด์ ์ง์ ์์ผ๋ก ํด๋ณด์! MySQL FDW๋ฅผ ์์๋ก ์ค๋ช ํ ๊ฒ. ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ์กฐํฉ์ด๊ฑฐ๋ . ๐ช
Step 1: Extension ์ค์น
๋จผ์ PostgreSQL์ mysql_fdw extension์ ์ค์นํด์ผ ํด. ์์คํ ์ ๋ฐ๋ผ ๋ฐฉ๋ฒ์ด ์กฐ๊ธ์ฉ ๋ฌ๋ผ.
# Ubuntu/Debian
sudo apt-get install postgresql-14-mysql-fdw
# CentOS/RHEL
sudo yum install mysql_fdw_14
# macOS (Homebrew)
brew install mysql_fdw
์ค์น๊ฐ ๋๋ฌ์ผ๋ฉด PostgreSQL์์ extension์ ํ์ฑํํด:
-- PostgreSQL์ ์ ์ํด์
CREATE EXTENSION mysql_fdw;
Step 2: ์๋ฒ ๊ฐ์ฒด ์์ฑ
์ด์ ์ธ๋ถ MySQL ์๋ฒ๋ฅผ PostgreSQL์ ๋ฑ๋กํด์ผ ํด. ์ด๊ฑธ "์๋ฒ ๊ฐ์ฒด"๋ผ๊ณ ๋ถ๋ฌ.
CREATE SERVER mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (
host '192.168.1.100',
port '3306'
);
์ฌ๊ธฐ์ host์ port๋ ๋น์ฐํ ๋์ MySQL ์๋ฒ ์ ๋ณด๋ก ๋ฐ๊ฟ์ผ๊ฒ ์ง? ๐
Step 3: ์ฌ์ฉ์ ๋งคํ
PostgreSQL ์ฌ์ฉ์์ MySQL ์ฌ์ฉ์๋ฅผ ์ฐ๊ฒฐํด์ค์ผ ํด. ๋ณด์์ ์ํด์ ๋ง์ด์ผ.
CREATE USER MAPPING FOR postgres
SERVER mysql_server
OPTIONS (
username 'mysql_user',
password 'mysql_password'
);
๋น๋ฐ๋ฒํธ๋ฅผ ํ๋ฌธ์ผ๋ก ์ ์ฅํ๋ ๊ฒ ์ฐ์ฐํ์ง? ์ค์ ์ด์ ํ๊ฒฝ์์๋ .pgpass ํ์ผ์ ์ฌ์ฉํ๊ฑฐ๋, PostgreSQL์ ์ํธํ ๊ธฐ๋ฅ์ ํ์ฉํ๋ ๊ฒ ์ข์. ๋ํ ์ต์ ๊ถํ ์์น์ ์ง์ผ์ ํ์ํ ๊ถํ๋ง ๋ถ์ฌํ์!
Step 4: ์ธ๋ถ ํ ์ด๋ธ ์์ฑ
์ด์ ๋๋์ด ์ธ๋ถ ํ ์ด๋ธ์ ๋ง๋ค ์ฐจ๋ก์ผ. MySQL์ ํ ์ด๋ธ ๊ตฌ์กฐ๋ฅผ PostgreSQL์ ์ ์ํ๋ ๊ฑฐ์ง.
CREATE FOREIGN TABLE customers (
customer_id INT,
name VARCHAR(100),
email VARCHAR(100),
created_at TIMESTAMP
)
SERVER mysql_server
OPTIONS (
dbname 'mydb',
table_name 'customers'
);
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด, ์ปฌ๋ผ ํ์ ์ PostgreSQL ํ์ ์ผ๋ก ์ ์ํ๋ค๋ ๊ฑฐ์ผ. mysql_fdw๊ฐ ์์์ ํ์ ๋ณํ์ ํด์ค. ๐
Step 5: ํ ์คํธ!
์, ์ด์ ์ง์ง๋ก ์๋ํ๋์ง ํ์ธํด๋ณด์!
-- ๊ฐ๋จํ ์กฐํ
SELECT * FROM customers LIMIT 10;
-- ์กฐ์ธ๋ ํด๋ณด๊ณ
SELECT c.name, o.order_date, o.total_amount
FROM customers c
JOIN local_orders o ON c.customer_id = o.customer_id
WHERE o.order_date > '2024-01-01';
-- ์ง๊ณ๋ ๊ฐ๋ฅ!
SELECT
DATE_TRUNC('month', created_at) as month,
COUNT(*) as new_customers
FROM customers
GROUP BY month
ORDER BY month DESC;
์์ฐ! ๋ง์น ๊ฐ์ DB์ ์๋ ๊ฒ์ฒ๋ผ ์์ฐ์ค๋ฝ๊ฒ ์ฟผ๋ฆฌ๊ฐ ์คํ๋์ง? ๐
๐ ๋ค์ํ FDW ์ข ๋ฅ๋ค
MySQL๋ง ์ฐ๊ฒฐํ ์ ์๋ ๊ฒ ์๋์ผ. PostgreSQL ์ปค๋ฎค๋ํฐ๋ ์ ๋ง ๋ค์ํ FDW๋ฅผ ๋ง๋ค์ด๋จ์ด. ํ๋ฒ ์ดํด๋ณผ๊น?
| FDW ์ด๋ฆ | ์ฐ๊ฒฐ ๋์ | ํน์ง |
|---|---|---|
| postgres_fdw | PostgreSQL | ๋ค๋ฅธ PostgreSQL ์๋ฒ ์ฐ๊ฒฐ. ์ค๋ฉ์ด๋ ๋ถ์ฐ DB ๊ตฌ์ฑ์ ์ ์ฉ |
| mysql_fdw | MySQL/MariaDB | ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ FDW ์ค ํ๋. ์์ ์ ์ด๊ณ ์ฑ๋ฅ ์ข์ |
| oracle_fdw | Oracle | ๋ ๊ฑฐ์ ์์คํ ์ฐ๋์ ํ์. ์์ฉ์ด์ง๋ง ๋ฌด๋ฃ ๋ฒ์ ๋ ์์ |
| mongo_fdw | MongoDB | NoSQL ๋ฐ์ดํฐ๋ฅผ SQL๋ก ์กฐํ ๊ฐ๋ฅ. JSON ํ์ ๊ณผ ๊ถํฉ ์ข์ |
| redis_fdw | Redis | ์บ์ ๋ฐ์ดํฐ๋ฅผ SQL๋ก ๋ถ์ํ ๋ ์ ์ฉ |
| file_fdw | CSV/ํ ์คํธ ํ์ผ | PostgreSQL ๊ธฐ๋ณธ ์ ๊ณต. ๋ก๊ทธ ๋ถ์์ ์ข์ |
| multicorn | Python์ผ๋ก ์ปค์คํ | Python์ผ๋ก ์ง์ FDW๋ฅผ ๋ง๋ค ์ ์์ด. API ์ฐ๋๋ ๊ฐ๋ฅ! |
Multicorn์ ์ ๋ง ์ฌ๋ฏธ์๋ ๋ ์์ด์ผ. Python์ผ๋ก FDW๋ฅผ ์ง์ ๋ง๋ค ์ ์๊ฑฐ๋ . ์๋ฅผ ๋ค์ด REST API๋ฅผ FDW๋ก ๋ง๋ค๋ฉด, HTTP API๋ฅผ SQL๋ก ์กฐํํ ์ ์์ด! ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ API๋ฅผ PostgreSQL ํ ์ด๋ธ์ฒ๋ผ ์ฟผ๋ฆฌํ ์๋ ์๋ค๋ ๊ฑฐ์ง. ์์๋ง ํด๋ ์ ๋์ง ์์? ๐
๊ฐ FDW๋ง๋ค ์ค์ ๋ฐฉ๋ฒ์ด ์กฐ๊ธ์ฉ ๋ค๋ฅด๊ธด ํ์ง๋ง, ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ ๋น์ทํด. ์๋ฒ ์์ฑ โ ์ฌ์ฉ์ ๋งคํ โ ์ธ๋ถ ํ ์ด๋ธ ์์ฑ, ์ด ์ธ ๋จ๊ณ๋ง ๊ธฐ์ตํ๋ฉด ๋ผ!
โก ์ฑ๋ฅ ์ต์ ํ ํ
FDW๋ฅผ ์ฐ๋ค ๋ณด๋ฉด ์ฑ๋ฅ ์ด์๋ฅผ ๋ง๋ ์ ์์ด. ๋คํธ์ํฌ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฑฐ๋๊น ๋น์ฐํ ๋ก์ปฌ ํ ์ด๋ธ๋ณด๋ค๋ ๋๋ฆด ์๋ฐ์ ์์ง. ํ์ง๋ง ๋ช ๊ฐ์ง ํ์ ์๋ฉด ํจ์ฌ ๋น ๋ฅด๊ฒ ์ธ ์ ์์ด! ๐จ
1. ์ฟผ๋ฆฌ ํธ์๋ค์ด ํ์ฉ
์์์ ์ ๊น ์ธ๊ธํ๋ ์ฟผ๋ฆฌ ํธ์๋ค์ด์ ์ ๊ทน ํ์ฉํ์. PostgreSQL์ด ์๋์ผ๋ก ํด์ฃผ๊ธด ํ์ง๋ง, ์ฟผ๋ฆฌ๋ฅผ ์ด๋ป๊ฒ ์์ฑํ๋๋์ ๋ฐ๋ผ ํจ์จ์ด ๋ฌ๋ผ์ ธ.
-- ๋์ ์: ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ ํ ํํฐ๋ง
SELECT * FROM (
SELECT * FROM foreign_table
) sub
WHERE created_at > '2024-01-01';
-- ์ข์ ์: ์กฐ๊ฑด์ ์ง์ ๋ช
์
SELECT * FROM foreign_table
WHERE created_at > '2024-01-01';
๋ ๋ฒ์งธ ๋ฐฉ์์ด ํจ์ฌ ํจ์จ์ ์ด์ผ. PostgreSQL์ด WHERE ์กฐ๊ฑด์ ์ธ๋ถ DB๋ก ๋ณด๋ด์ ๊ฑฐ๊ธฐ์ ๋จผ์ ํํฐ๋งํ๊ฑฐ๋ .
2. use_remote_estimate ์ต์
์ด ์ต์ ์ ์ผ๋ฉด PostgreSQL์ด ์ธ๋ถ DB์๊ฒ "์ด ์ฟผ๋ฆฌ ์คํํ๋ฉด ๋์ถฉ ๋ช ๊ฑด ๋์?"๋ผ๊ณ ๋ฌผ์ด๋ด. ๊ทธ๋ผ ์ฟผ๋ฆฌ ํ๋์ ๋ ์ ํํ๊ฒ ์ธ์ธ ์ ์์ง.
ALTER SERVER mysql_server
OPTIONS (ADD use_remote_estimate 'true');
๋จ, ์ด ์ต์ ์ ์ผ๋ฉด ์ฟผ๋ฆฌ ์คํ ์ ์ ํ ๋ฒ ๋ ํต์ ํ๋๊น ๊ฐ๋จํ ์ฟผ๋ฆฌ์์๋ ์คํ๋ ค ๋๋ ค์ง ์ ์์ด. ๋ณต์กํ ์กฐ์ธ์ด๋ ์ง๊ณ ์ฟผ๋ฆฌ์์ ํจ๊ณผ์ ์ด์ผ.
3. ๋ฐฐ์น ์ฌ์ด์ฆ ์กฐ์
๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ํ ๋ฒ์ ๋ช ๊ฑด์ฉ ๊ฐ์ ธ์ฌ์ง ์กฐ์ ํ ์ ์์ด.
ALTER SERVER mysql_server
OPTIONS (ADD fetch_size '10000');
๊ธฐ๋ณธ๊ฐ์ ๋ณดํต 100์ธ๋ฐ, ๋๋ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋๋ ์ด๊ฑธ ๋๋ฆฌ๋ฉด ๋คํธ์ํฌ ์๋ณต ํ์๊ฐ ์ค์ด์ ๋นจ๋ผ์ ธ. ํ์ง๋ง ๋๋ฌด ํฌ๊ฒ ํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ๋จน์ผ๋ ์ ์ ํ ์กฐ์ ํด์ผ ํด. ๐ฏ
4. ์ธ๋ฑ์ค ํ์ฉ
์ธ๋ถ ํ ์ด๋ธ์๋ ์ธ๋ฑ์ค ์ ๋ณด๋ฅผ ์๋ ค์ค ์ ์์ด. ๋ฌผ๋ก ์ค์ ์ธ๋ฑ์ค๋ ์ธ๋ถ DB์ ์์ด์ผ ํ์ง๋ง, PostgreSQL์๊ฒ "์ด ์ปฌ๋ผ์ ์ธ๋ฑ์ค ์์ด!"๋ผ๊ณ ์๋ ค์ฃผ๋ฉด ์ฟผ๋ฆฌ ํ๋์ ๋ ์ ์ง.
-- ์ธ๋ถ ํ
์ด๋ธ์ ์ธ๋ฑ์ค ์ ๋ณด ์ถ๊ฐ
CREATE INDEX ON foreign_table (customer_id);
-- ์ค์ ๋ก๋ ์์ฑ๋์ง ์๊ณ , ํํธ๋ง ์ ๊ณต๋จ
์ฟผ๋ฆฌ๊ฐ ๋๋ฆฌ๋ค ์ถ์ผ๋ฉด EXPLAIN (ANALYZE, VERBOSE)๋ฅผ ์จ์ ์คํ ๊ณํ์ ํ์ธํด๋ด. ์ด๋ ๋ถ๋ถ์์ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋์ง, ํธ์๋ค์ด์ด ์ ๋๋ก ๋๊ณ ์๋์ง ํ๋์ ๋ณผ ์ ์์ด!
5. ๋จธํฐ๋ฆฌ์ผ๋ผ์ด์ฆ๋ ๋ทฐ ํ์ฉ
์์ฃผ ์กฐํํ๋ ์ธ๋ถ ๋ฐ์ดํฐ๋ ๋จธํฐ๋ฆฌ์ผ๋ผ์ด์ฆ๋ ๋ทฐ๋ก ๋ง๋ค์ด์ ๋ก์ปฌ์ ์บ์ฑํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ.
-- ์ธ๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ์ ์บ์ฑ
CREATE MATERIALIZED VIEW cached_customers AS
SELECT * FROM foreign_customers
WHERE active = true;
-- ์ฃผ๊ธฐ์ ์ผ๋ก ๊ฐฑ์
REFRESH MATERIALIZED VIEW cached_customers;
์ค์๊ฐ์ฑ์ด ์ค์ํ์ง ์์ ๋ฐ์ดํฐ๋ผ๋ฉด ์ด ๋ฐฉ๋ฒ์ด ์์ฒญ ํจ๊ณผ์ ์ด์ผ. ๋คํธ์ํฌ ์ง์ฐ ์์ด ๋ก์ปฌ ์๋๋ก ์กฐํํ ์ ์๊ฑฐ๋ ! โก
๐ ๋ณด์ ๊ณ ๋ ค์ฌํญ
FDW๋ฅผ ์ธ ๋ ๋ณด์์ ์ ๋ง ์ค์ํด. ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฑฐ๋๊น ๋ณด์ ๊ตฌ๋ฉ์ด ์๊ธฐ๋ฉด ํฐ์ผ์ด๊ฑฐ๋ . ๐ฐ
๐ก๏ธ ํ์ ๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ
1. ๋คํธ์ํฌ ๋ณด์
์ธ๋ถ DB์์ ํต์ ์ ๋ฐ๋์ ์ํธํ๋ ์ฑ๋์ ์ฌ์ฉํด์ผ ํด. SSL/TLS๋ฅผ ์ค์ ํ์.
CREATE SERVER secure_mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (
host '192.168.1.100',
port '3306',
secure_auth 'true',
init_command 'SET SESSION sql_mode=ANSI_QUOTES'
);
2. ์ต์ ๊ถํ ์์น
์ธ๋ถ DB ์ฌ์ฉ์์๊ฒ๋ ๊ผญ ํ์ํ ๊ถํ๋ง ๋ถ์ฌํด. SELECT๋ง ํ์ํ๋ฉด SELECT๋ง ์ฃผ๊ณ , ์ ๋ DROP์ด๋ DELETE ๊ถํ์ ์ฃผ์ง ๋ง!
-- MySQL์์
GRANT SELECT ON mydb.customers TO 'fdw_user'@'%';
FLUSH PRIVILEGES;
3. ๋น๋ฐ๋ฒํธ ๊ด๋ฆฌ
USER MAPPING์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋ฌธ์ผ๋ก ์ ์ฅํ๋ ๊ฑด ์ํํด. .pgpass ํ์ผ์ด๋ ํ๊ฒฝ ๋ณ์๋ฅผ ํ์ฉํ์.
# ~/.pgpass ํ์ผ (๊ถํ 0600์ผ๋ก ์ค์ )
192.168.1.100:3306:mydb:fdw_user:secure_password
4. ๋ฐฉํ๋ฒฝ ์ค์
์ธ๋ถ DB๋ PostgreSQL ์๋ฒ์ IP์์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋๋ก ๋ฐฉํ๋ฒฝ์ ์ค์ ํด์ผ ํด.
5. ๊ฐ์ฌ ๋ก๊น
๋๊ฐ ์ธ์ ์ธ๋ถ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋์ง ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ๊ฒ๋ ์ค์ํด. PostgreSQL์ ๋ก๊น ๊ธฐ๋ฅ์ ํ์ฉํ์.
ํ ํ์ฌ์์ FDW๋ก ์ฐ๊ฒฐ๋ ์ธ๋ถ DB์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ๋ณ๊ฒฝํ์ง ์์์, ํด์ฌํ ์ง์์ด ์ฌ์ ํ ์ ๊ทผํ ์ ์์๋ ์ฌ๋ก๊ฐ ์์ด. ์ ๊ธฐ์ ์ธ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ๊ณผ ์ ๊ทผ ๊ถํ ๊ฒํ ๋ ํ์์ผ!
๐ช ์ค์ ํ์ฉ ์ฌ๋ก
์ด๋ก ๊ณผ ์ค์ ์ ์ถฉ๋ถํ ๋ดค์ผ๋, ์ด์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ๋์ง ์ฌ๋ฏธ์๋ ์ฌ๋ก๋ค์ ์ดํด๋ณด์! ๐
์ฌ๋ก 1: ๋ ๊ฑฐ์ ์์คํ ํตํฉ
ํ ๊ธ์ตํ์ฌ ์ด์ผ๊ธฐ์ผ. ์ค๋๋ Oracle ์์คํ ์ ๊ณ ๊ฐ ๊ณ์ข ์ ๋ณด๊ฐ ์๊ณ , ์๋ก์ด PostgreSQL ์์คํ ์ ๋ชจ๋ฐ์ผ ์ฑ ๋ฐ์ดํฐ๊ฐ ์์์ด. ๋ ์์คํ ์ ํตํฉํด์ ๊ณ ๊ฐ 360๋ ๋ทฐ๋ฅผ ๋ง๋ค์ด์ผ ํ์ง.
-- Oracle์ ๊ณ์ข ์ ๋ณด์ PostgreSQL์ ์ฑ ์ฌ์ฉ ๋ก๊ทธ ์กฐ์ธ
SELECT
o.customer_name,
o.account_balance,
COUNT(a.login_time) as monthly_logins,
MAX(a.login_time) as last_login
FROM oracle_accounts o
LEFT JOIN app_logs a ON o.customer_id = a.customer_id
WHERE a.login_time > CURRENT_DATE - INTERVAL '30 days'
GROUP BY o.customer_name, o.account_balance
ORDER BY monthly_logins DESC;
FDW ๋๋ถ์ ๋ ์์คํ ์ ๋ฌผ๋ฆฌ์ ์ผ๋ก ํตํฉํ์ง ์๊ณ ๋ ๋ฐ์ดํฐ๋ฅผ ํ๋๋ก ๋ณผ ์ ์์์ด. ๋ง์ด๊ทธ๋ ์ด์ ๋น์ฉ๊ณผ ์๊ฐ์ ์์ฒญ ์ ์ฝํ ๊ฑฐ์ง! ๐ฐ
์ฌ๋ก 2: ์ค์๊ฐ ๋์๋ณด๋
์ด์ปค๋จธ์ค ํ์ฌ์์ ์ค์๊ฐ ๋์๋ณด๋๋ฅผ ๋ง๋ค ๋ FDW๋ฅผ ํ์ฉํ์ด. MySQL์ ์ฃผ๋ฌธ ๋ฐ์ดํฐ, MongoDB์ ์ํ ๋ฆฌ๋ทฐ, Redis์ ์ค์๊ฐ ์ฌ๊ณ ๊ฐ ์์๊ฑฐ๋ .
-- ์ฌ๋ฌ ์์ค์ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์กฐํ
CREATE VIEW realtime_dashboard AS
SELECT
p.product_name,
m.order_count,
r.avg_rating,
red.current_stock
FROM products p
LEFT JOIN (
SELECT product_id, COUNT(*) as order_count
FROM mysql_orders
WHERE order_date = CURRENT_DATE
GROUP BY product_id
) m ON p.product_id = m.product_id
LEFT JOIN (
SELECT product_id, AVG(rating) as avg_rating
FROM mongo_reviews
GROUP BY product_id
) r ON p.product_id = r.product_id
LEFT JOIN redis_inventory red ON p.product_id = red.product_id;
์ด๋ ๊ฒ ํ๋์ ๋ทฐ๋ก ๋ง๋ค์ด๋์ผ๋๊น, BI ํด์์ ๊ฐ๋จํ๊ฒ ์ฐ๊ฒฐํด์ ๋์๋ณด๋๋ฅผ ๋ง๋ค ์ ์์์ด. ๊ฐ๋ฐ์๋ค์ด ์ฌ๋ฌ DB๋ฅผ ์๋ค๊ฐ๋ค ํ ํ์๊ฐ ์์ด์ง ๊ฑฐ์ง! ๐
์ฌ๋ก 3: ๋ฐ์ดํฐ ๋ง์ด๊ทธ๋ ์ด์
MySQL์์ PostgreSQL๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ ๋๋ FDW๊ฐ ์ ์ฉํด. ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ์ด ๊ฐ๋ฅํ๊ฑฐ๋ .
-- 1๋จ๊ณ: ์ธ๋ถ ํ
์ด๋ธ๋ก MySQL ๋ฐ์ดํฐ ์ ๊ทผ
CREATE FOREIGN TABLE mysql_users ...;
-- 2๋จ๊ณ: ๋ก์ปฌ ํ
์ด๋ธ๋ก ๋ณต์ฌ
CREATE TABLE local_users AS
SELECT * FROM mysql_users;
-- 3๋จ๊ณ: ์ ์ง์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ํ
-- ์ผ๋ถ๋ local_users, ์ผ๋ถ๋ mysql_users ์ฌ์ฉ
-- 4๋จ๊ณ: ๋ชจ๋ ์ ํ ์๋ฃ ํ ์ธ๋ถ ํ
์ด๋ธ ์ ๊ฑฐ
DROP FOREIGN TABLE mysql_users;
๋น ๋ฑ ๋ฐฉ์์ผ๋ก ํ ๋ฒ์ ์ ํํ๋ ๊ฒ ์๋๋ผ, ๋จ๊ณ์ ์ผ๋ก ์์ ํ๊ฒ ๋ง์ด๊ทธ๋ ์ด์ ํ ์ ์์ด. ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ์ธ์ ๋ ๋กค๋ฐฑ๋ ๊ฐ๋ฅํ๊ณ ! ๐
์ฌ๋ก 4: ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ ๋ฐ์ดํฐ ๋ถ์
์ฌ๋ฅ ๊ณต์ ํ๋ซํผ์ ์ด์ํ๋ค๊ณ ์๊ฐํด๋ด. ์ฌ์ฉ์ ์ ๋ณด๋ PostgreSQL์, ๊ฑฐ๋ ๋ด์ญ์ MySQL์, ์ฑํ ๋ก๊ทธ๋ MongoDB์ ์์ ์ ์์ด. ์ด๋ฐ ๋ฐ์ดํฐ๋ฅผ ํตํฉ ๋ถ์ํ๋ ค๋ฉด?
-- ์ฌ๋ฅ ์นดํ
๊ณ ๋ฆฌ๋ณ ๊ฑฐ๋ ๋ถ์
SELECT
u.user_name,
u.talent_category,
COUNT(t.transaction_id) as total_transactions,
SUM(t.amount) as total_revenue,
AVG(c.satisfaction_score) as avg_satisfaction
FROM users u
JOIN mysql_transactions t ON u.user_id = t.seller_id
LEFT JOIN mongo_chat_logs c ON t.transaction_id = c.transaction_id
WHERE t.transaction_date >= '2024-01-01'
GROUP BY u.user_name, u.talent_category
HAVING COUNT(t.transaction_id) > 10
ORDER BY total_revenue DESC;
์ด๋ฐ ์์ผ๋ก ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋งค์ ์ฑ๊ณผ๋ฅผ ๋ถ์ํ๊ฑฐ๋, ์ธ๊ธฐ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ํ์ ํ๋ ๋ฐ FDW๋ฅผ ํ์ฉํ ์ ์์ด. ๋ฐ์ดํฐ๊ฐ ์ฌ๊ธฐ์ ๊ธฐ ํฉ์ด์ ธ ์์ด๋ ๋ฌธ์ ์์ง! ๐ฏ
๐ ํธ๋ฌ๋ธ์ํ ๊ฐ์ด๋
FDW๋ฅผ ์ฐ๋ค ๋ณด๋ฉด ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ๋ฅผ ๋ง๋ ์ ์์ด. ์์ฃผ ๋ฐ์ํ๋ ์ด์๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ ๋ฆฌํด๋ดค์ด. ๐
๋ฌธ์ 1: ์ฐ๊ฒฐ ํ์์์
์ฆ์: "could not connect to server" ์๋ฌ๊ฐ ๊ณ์ ๋ฐ์
์์ธ:
โข ๋คํธ์ํฌ ๋ฐฉํ๋ฒฝ ๋ฌธ์
โข ์ธ๋ถ DB ์๋ฒ๊ฐ ๋ค์ด๋จ
โข ์ฐ๊ฒฐ ์ ์ ํ ์ด๊ณผ
ํด๊ฒฐ:
-- ํ์์์ ์ค์ ๋๋ฆฌ๊ธฐ
ALTER SERVER mysql_server
OPTIONS (ADD connect_timeout '30');
-- ์ฐ๊ฒฐ ํ ์ค์
ALTER SERVER mysql_server
OPTIONS (ADD keep_connections 'on');
๋ฌธ์ 2: ํ์ ๋ถ์ผ์น
์ฆ์: "column type mismatch" ์๋ฌ
์์ธ:
์ธ๋ถ ํ ์ด๋ธ ์ ์์ ์ค์ ํ ์ด๋ธ์ ์ปฌ๋ผ ํ์ ์ด ๋ค๋ฆ
ํด๊ฒฐ:
-- ์ธ๋ถ ํ
์ด๋ธ ์ฌ์ ์
DROP FOREIGN TABLE IF EXISTS customers;
CREATE FOREIGN TABLE customers (
customer_id BIGINT, -- INT์์ BIGINT๋ก ๋ณ๊ฒฝ
name TEXT, -- VARCHAR์์ TEXT๋ก
created_at TIMESTAMP WITH TIME ZONE -- ํ์์กด ์ถ๊ฐ
)
SERVER mysql_server
OPTIONS (dbname 'mydb', table_name 'customers');
PostgreSQL์ ํ์ ์ด ๋ ์๊ฒฉํ๋๊น, ๋๋ํ๊ฒ ์ ์ํ๋ ๊ฒ ์ข์!
๋ฌธ์ 3: ์ฑ๋ฅ ์ ํ
์ฆ์: ์ฟผ๋ฆฌ๊ฐ ๋๋ฌด ๋๋ฆผ
์์ธ:
โข ํธ์๋ค์ด์ด ์ ๋๊ณ ์์
โข ๋๋ฌด ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด
โข ๋คํธ์ํฌ ์ง์ฐ
ํด๊ฒฐ:
-- ์คํ ๊ณํ ํ์ธ
EXPLAIN (ANALYZE, VERBOSE)
SELECT * FROM foreign_table WHERE id > 1000;
-- ํธ์๋ค์ด ํ์ธ
-- "Foreign Scan"์์ "Remote SQL" ํ์ธ
-- ํ์ํ ์ปฌ๋ผ๋ง ์ ํ
SELECT id, name FROM foreign_table -- ๋ชจ๋ ์ปฌ๋ผ ๋์
WHERE created_at > '2024-01-01'
LIMIT 1000; -- LIMIT ์ถ๊ฐ
๋ฌธ์ 4: ํธ๋์ญ์ ๋ฌธ์
์ฆ์: ์ธ๋ถ ํ ์ด๋ธ์ INSERT/UPDATE๊ฐ ์ ๋จ
์์ธ:
์ผ๋ถ FDW๋ ์ฝ๊ธฐ ์ ์ฉ์ด๊ฑฐ๋, ์ฐ๊ธฐ ์ง์์ด ์ ํ์ ์
ํด๊ฒฐ:
-- FDW๊ฐ ์ฐ๊ธฐ๋ฅผ ์ง์ํ๋์ง ํ์ธ
SELECT fdwname, fdwhandler
FROM pg_foreign_data_wrapper;
-- ์ฐ๊ธฐ๊ฐ ํ์ํ๋ฉด ์ง์ ์ธ๋ถ DB์ ์ฐ๊ฒฐํ๊ฑฐ๋
-- dblink ์ฌ์ฉ ๊ณ ๋ ค
FDW๋ ์ฃผ๋ก ์ฝ๊ธฐ ์ฉ๋๋ก ์ฐ๋ ๊ฒ ์์ ํด. ์ฐ๊ธฐ๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์์ ์ฒ๋ฆฌํ๋ ๊ฒ ์ข์!
๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด PostgreSQL ๋ก๊ทธ๋ฅผ ํ์ธํด๋ด. log_min_messages๋ฅผ DEBUG1๋ก ์ค์ ํ๋ฉด FDW๊ฐ ์ค์ ๋ก ์ด๋ค ์ฟผ๋ฆฌ๋ฅผ ์ธ๋ถ DB์ ๋ณด๋ด๋์ง ๋ณผ ์ ์์ด. ์์ฒญ ์ ์ฉํ์ง!
๐ ๊ณ ๊ธ ํ์ฉ ๊ธฐ๋ฒ
๊ธฐ๋ณธ์ ๋ง์คํฐํ์ผ๋, ์ด์ ์ข ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ์์๋ณด์! ์ด๋ฐ ๊ฑธ ์๋ฉด ์ง์ง FDW ์ ๋ฌธ๊ฐ๊ฐ ๋ ์ ์์ด. ๐
1. ํํฐ์ ๋๊ณผ FDW ์กฐํฉ
PostgreSQL์ ํํฐ์ ๋๊ณผ FDW๋ฅผ ์กฐํฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋ถ์ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ง๋ค ์ ์์ด.
-- ํํฐ์
ํ
์ด๋ธ ์์ฑ
CREATE TABLE orders (
order_id BIGINT,
order_date DATE,
customer_id INT,
amount DECIMAL(10,2)
) PARTITION BY RANGE (order_date);
-- ์ต๊ทผ ๋ฐ์ดํฐ๋ ๋ก์ปฌ์
CREATE TABLE orders_2024 PARTITION OF orders
FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
-- ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ ์ธ๋ถ ์๋ฒ์
CREATE FOREIGN TABLE orders_2023 PARTITION OF orders
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01')
SERVER archive_server
OPTIONS (table_name 'orders_2023');
-- ์ฟผ๋ฆฌ๋ ๋์ผํ๊ฒ!
SELECT * FROM orders
WHERE order_date BETWEEN '2023-06-01' AND '2024-06-01';
์ด๋ ๊ฒ ํ๋ฉด ์๋์ผ๋ก ์ ์ ํ ํํฐ์ (๋ก์ปฌ ๋๋ ์ธ๋ถ)์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์. ์ ํ๋ฆฌ์ผ์ด์ ์ ํํฐ์ ๋์ ์ ํ ์ ๊ฒฝ ์ธ ํ์๊ฐ ์์ด! ๐
2. ์ปค์คํ FDW ๋ง๋ค๊ธฐ
Multicorn์ ์ฌ์ฉํ๋ฉด Python์ผ๋ก ์ง์ FDW๋ฅผ ๋ง๋ค ์ ์์ด. REST API๋ฅผ ํ ์ด๋ธ์ฒ๋ผ ์ธ ์ ์๋ค๋, ์ ๊ธฐํ์ง?
-- Multicorn ์ค์น
CREATE EXTENSION multicorn;
-- Python FDW ์ ์
CREATE SERVER api_server
FOREIGN DATA WRAPPER multicorn
OPTIONS (
wrapper 'multicorn.restfdw.RestFDW',
url 'https://api.example.com/users'
);
-- ์ธ๋ถ ํ
์ด๋ธ ์์ฑ
CREATE FOREIGN TABLE api_users (
id INT,
name TEXT,
email TEXT
) SERVER api_server;
์ด์ API๋ฅผ SQL๋ก ์กฐํํ ์ ์์ด! ์ฌ๋ฅ๋ท API๋ฅผ ์ด๋ฐ ์์ผ๋ก ์ฐ๋ํ๋ฉด, ํ๋ซํผ ๋ฐ์ดํฐ๋ฅผ PostgreSQL์์ ์ง์ ๋ถ์ํ ์๋ ์๊ฒ ์ง? ๐ฅ
3. ๋น๋๊ธฐ ์ฟผ๋ฆฌ ์คํ
PostgreSQL 14๋ถํฐ๋ FDW ์ฟผ๋ฆฌ๋ฅผ ๋น๋๊ธฐ๋ก ์คํํ ์ ์์ด. ์ฌ๋ฌ ์ธ๋ถ ์๋ฒ์์ ๋์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋ ์ ์ฉํด.
-- ๋น๋๊ธฐ ์คํ ํ์ฑํ
ALTER SERVER mysql_server
OPTIONS (ADD async_capable 'true');
ALTER SERVER oracle_server
OPTIONS (ADD async_capable 'true');
-- ์ฌ๋ฌ ์ธ๋ถ ํ
์ด๋ธ ์กฐ์ธ
SELECT
m.customer_name,
o.order_total,
p.product_name
FROM mysql_customers m
JOIN oracle_orders o ON m.customer_id = o.customer_id
JOIN postgres_products p ON o.product_id = p.product_id;
๋น๋๊ธฐ๋ก ์คํํ๋ฉด ๊ฐ ์๋ฒ์์ ๋ณ๋ ฌ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๊น ํจ์ฌ ๋นจ๋ผ์ ธ! โก
4. ๋์ ์ธ๋ถ ํ ์ด๋ธ ์์ฑ
IMPORT FOREIGN SCHEMA๋ฅผ ์ฌ์ฉํ๋ฉด ์ธ๋ถ DB์ ํ ์ด๋ธ ๊ตฌ์กฐ๋ฅผ ์๋์ผ๋ก ๊ฐ์ ธ์ฌ ์ ์์ด.
-- MySQL์ ๋ชจ๋ ํ
์ด๋ธ์ ์๋์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
IMPORT FOREIGN SCHEMA mydb
FROM SERVER mysql_server
INTO public;
-- ํน์ ํ
์ด๋ธ๋ง ๊ฐ์ ธ์ค๊ธฐ
IMPORT FOREIGN SCHEMA mydb
LIMIT TO (customers, orders)
FROM SERVER mysql_server
INTO public;
-- ํน์ ํ
์ด๋ธ ์ ์ธ
IMPORT FOREIGN SCHEMA mydb
EXCEPT (temp_table, backup_table)
FROM SERVER mysql_server
INTO public;
์ผ์ผ์ด CREATE FOREIGN TABLE์ ์ธ ํ์๊ฐ ์์ด์ ธ์ ์์ฒญ ํธํด! ํนํ ํ ์ด๋ธ์ด ๋ง์ ๋ ์๊ฐ์ ๋ง์ด ์ ์ฝํ ์ ์์ด. ๐
๐ผ ์ค๋ฌด ๋ฒ ์คํธ ํ๋ํฐ์ค
์ด์ ์ค๋ฌด์์ FDW๋ฅผ ์ ๋๋ก ํ์ฉํ๊ธฐ ์ํ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ฅผ ์ ๋ฆฌํด๋ณผ๊ฒ. ์ด๊ฑด ๋ด๊ฐ ์ฌ๋ฌ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ๋ฐฐ์ด ๋ ธํ์ฐ๋ค์ด์ผ! ๐
1. ๋ช ํํ ๋ค์ด๋ฐ ์ปจ๋ฒค์
์ธ๋ถ ํ ์ด๋ธ๊ณผ ๋ก์ปฌ ํ ์ด๋ธ์ ๊ตฌ๋ถํ๊ธฐ ์ฝ๊ฒ ์ด๋ฆ์ ์ง์ด์ผ ํด.
-- ์ข์ ์
CREATE FOREIGN TABLE mysql_customers ...
CREATE FOREIGN TABLE oracle_legacy_orders ...
CREATE FOREIGN TABLE mongo_product_reviews ...
-- ๋์ ์
CREATE FOREIGN TABLE customers ... -- ๋ก์ปฌ์ธ์ง ์ธ๋ถ์ธ์ง ๋ชจํธํจ
์ ๋์ฌ๋ฅผ ๋ถ์ด๋ฉด ์ฟผ๋ฆฌ๋ฅผ ๋ณผ ๋ "์, ์ด๊ฑด ์ธ๋ถ ๋ฐ์ดํฐ๊ตฌ๋" ํ๊ณ ๋ฐ๋ก ์ ์ ์์ด. ์ฑ๋ฅ ๊ณ ๋ ค๋ ์ฌ์์ง์ง!
2. ์คํค๋ง ๋ถ๋ฆฌ
์ธ๋ถ ํ ์ด๋ธ๋ค์ ๋ณ๋ ์คํค๋ง์ ๋ชจ์๋๋ฉด ๊ด๋ฆฌ๊ฐ ํธํด.
-- ์ธ๋ถ ๋ฐ์ดํฐ์ฉ ์คํค๋ง ์์ฑ
CREATE SCHEMA external;
-- ์ธ๋ถ ํ
์ด๋ธ์ ํด๋น ์คํค๋ง์ ์์ฑ
CREATE FOREIGN TABLE external.mysql_customers ...
CREATE FOREIGN TABLE external.oracle_orders ...
-- ๋ทฐ๋ก ํตํฉ
CREATE VIEW public.customer_360 AS
SELECT
c.*,
o.order_count
FROM external.mysql_customers c
LEFT JOIN external.oracle_orders o ON c.customer_id = o.customer_id;
์ด๋ ๊ฒ ํ๋ฉด ์ธ๋ถ ๋ฐ์ดํฐ์ ๋ก์ปฌ ๋ฐ์ดํฐ๊ฐ ๋ช ํํ ๊ตฌ๋ถ๋ผ์ ์ข์!
3. ๋ชจ๋ํฐ๋ง ์ค์
FDW ์ฟผ๋ฆฌ์ ์ฑ๋ฅ์ ์ง์์ ์ผ๋ก ๋ชจ๋ํฐ๋งํด์ผ ํด.
-- ๋๋ฆฐ ์ฟผ๋ฆฌ ๋ก๊น
ALTER DATABASE mydb SET log_min_duration_statement = 1000;
-- FDW ํต๊ณ ํ์ธ
SELECT * FROM pg_stat_foreign_tables;
-- ์ฐ๊ฒฐ ์ํ ํ์ธ
SELECT * FROM pg_stat_activity
WHERE query LIKE '%FOREIGN%';
์ ๊ธฐ์ ์ผ๋ก ์ฒดํฌํด์ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๋ ๊ฒ ์ค์ํด!
4. ๋ฌธ์ํ
์ด๋ค ์ธ๋ถ ํ ์ด๋ธ์ด ์ด๋ค ์๋ฒ์ ์ฐ๊ฒฐ๋์ด ์๋์ง ๋ฌธ์ํํ์.
-- ์ฃผ์์ผ๋ก ๋ฌธ์ํ
COMMENT ON FOREIGN TABLE mysql_customers IS
'MySQL ํ๋ก๋์
์๋ฒ์ customers ํ
์ด๋ธ
์ฐ๊ฒฐ ์ ๋ณด: mysql-prod-01.example.com:3306
๋ด๋น์: ํ๊ธธ๋ (gildong@example.com)
๊ฐฑ์ ์ฃผ๊ธฐ: ์ค์๊ฐ
์ฉ๋: ๊ณ ๊ฐ ์ ๋ณด ์กฐํ ์ ์ฉ';
-- ์๋ฒ ์ ๋ณด๋ ์ฃผ์ ์ถ๊ฐ
COMMENT ON SERVER mysql_server IS
'MySQL ํ๋ก๋์
์๋ฒ
IP: 192.168.1.100
๋ฒ์ : MySQL 8.0.32
๋ฐฑ์
์๊ฐ: ๋งค์ผ 02:00-03:00 (์ด ์๊ฐ๋ ์ฑ๋ฅ ์ ํ ๊ฐ๋ฅ)';
๋์ค์ ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ๋ด๋ ์ดํดํ๊ธฐ ์ฝ๊ฒ ๋ง๋ค์ด์ผ ํด!
5. ์๋ฌ ํธ๋ค๋ง
์ธ๋ถ DB๊ฐ ๋ค์ด๋๊ฑฐ๋ ๋คํธ์ํฌ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์ผ๋, ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ ํด์ผ ํด.
-- ํจ์๋ก ๋ํํด์ ์๋ฌ ์ฒ๋ฆฌ
CREATE OR REPLACE FUNCTION get_customer_data(p_customer_id INT)
RETURNS TABLE(customer_id INT, name TEXT, email TEXT)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT c.customer_id, c.name, c.email
FROM external.mysql_customers c
WHERE c.customer_id = p_customer_id;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE '์ธ๋ถ DB ์ฐ๊ฒฐ ์คํจ: %', SQLERRM;
-- ์บ์๋ ๋ฐ์ดํฐ ๋ฐํํ๊ฑฐ๋ ๋น ๊ฒฐ๊ณผ ๋ฐํ
RETURN;
END;
$$;
์ด๋ ๊ฒ ํ๋ฉด ์ธ๋ถ DB์ ๋ฌธ์ ๊ฐ ์๊ฒจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์ ํ ๋ฉ์ถ์ง ์์!
์ด์ ํ๊ฒฝ์ ๋ฐฐํฌํ๊ธฐ ์ ์ ์ด๊ฒ๋ค์ ๊ผญ ํ์ธํด:
โ ๋คํธ์ํฌ ๋ณด์ ์ค์ ์๋ฃ
โ ๋น๋ฐ๋ฒํธ ์ํธํ ์ฒ๋ฆฌ
โ ๋ชจ๋ํฐ๋ง ์ค์ ์๋ฃ
โ ๋ฐฑ์ ๊ณํ ์๋ฆฝ
โ ์ฅ์ ๋์ ๋งค๋ด์ผ ์์ฑ
โ ์ฑ๋ฅ ํ ์คํธ ์๋ฃ
โ ๋ฌธ์ํ ์๋ฃ
๐ ํ์ต ๋ก๋๋งต
FDW๋ฅผ ๋ง์คํฐํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ์์๋ก ๊ณต๋ถํ๋ ๊ฑธ ์ถ์ฒํด! ๐
โข PostgreSQL ๊ธฐ๋ณธ ๊ฐ๋ ๋ณต์ต
โข SQL/MED ํ์ค ์ดํด
โข ๊ฐ๋จํ postgres_fdw๋ก ์ค์ต
โข ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ์์ฑ ์ฐ์ต
โข mysql_fdw ์ค์น ๋ฐ ์ค์
โข ๋ค๋ฅธ RDBMS FDW ์๋ (Oracle, SQL Server ๋ฑ)
โข NoSQL FDW ๊ฒฝํ (MongoDB, Redis)
โข ๊ฐ FDW์ ํน์ฑ ํ์
โข EXPLAIN ๋ถ์ ๋ฅ๋ ฅ ํค์ฐ๊ธฐ
โข ์ฟผ๋ฆฌ ํธ์๋ค์ด ์ดํด
โข ์ธ๋ฑ์ค ์ ๋ต ์๋ฆฝ
โข ์บ์ฑ ๊ธฐ๋ฒ ์ ์ฉ
โข ์ค์ ์ ๋ฌด ๋ฌธ์ ํด๊ฒฐ
โข ๋ณต์กํ ๋ฐ์ดํฐ ํตํฉ ์๋๋ฆฌ์ค
โข ๋์ฉ๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
โข ์ฅ์ ๋์ ๊ฒฝํ
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ DB ๊ด๋ จ ์ฌ๋ฅ์ ๊ณต์ ํ๋ ์ ๋ฌธ๊ฐ๋ค์ ๊ฐ์๋ฅผ ๋ค์ด๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์ค๋ฌด ๊ฒฝํ์ด ํ๋ถํ ์ฌ๋๋ค์ ๋ ธํ์ฐ๋ฅผ ๋ฐฐ์ธ ์ ์๊ฑฐ๋ ! ๐ก
๐ฎ FDW์ ๋ฏธ๋
FDW ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ป๊ฒ ๋ณํ ์ง ํ๋ฒ ์ดํด๋ณผ๊น? ๐
ํธ๋ ๋ 1: ํด๋ผ์ฐ๋ ๋ค์ดํฐ๋ธ
AWS RDS, Google Cloud SQL, Azure Database ๊ฐ์ ํด๋ผ์ฐ๋ DB ์๋น์ค๋ค๊ณผ์ ํตํฉ์ด ๋ ์ฌ์์ง๊ณ ์์ด. ์ด๋ฏธ ๋ง์ ํด๋ผ์ฐ๋ ์ ๊ณต์๋ค์ด FDW๋ฅผ ์ง์ํ๊ณ ์์ง.
-- AWS RDS PostgreSQL ๊ฐ ์ฐ๊ฒฐ
CREATE SERVER rds_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
host 'mydb.abc123.us-east-1.rds.amazonaws.com',
port '5432',
dbname 'production'
);
๋ฉํฐ ํด๋ผ์ฐ๋ ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฅผ ํตํฉํ๋ ๊ฒ ์ ์ ์ค์ํด์ง๊ณ ์์ด!
ํธ๋ ๋ 2: ์ค์๊ฐ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ
Kafka, Kinesis ๊ฐ์ ์คํธ๋ฆฌ๋ฐ ํ๋ซํผ๊ณผ FDW๋ฅผ ์ฐ๊ฒฐํ๋ ์๋๋ค์ด ๋์ค๊ณ ์์ด. ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ SQL๋ก ์กฐํํ ์ ์๋ค๋, ๋ฉ์ง์ง?
ํธ๋ ๋ 3: AI/ML ํตํฉ
๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ FDW๋ก ์ฐ๊ฒฐํด์, SQL ์ฟผ๋ฆฌ๋ก ์์ธก์ ์ํํ๋ ๊ฒ๋ ๊ฐ๋ฅํด์ง๊ณ ์์ด.
-- ๋ฏธ๋์ ๋ชจ์ต (์ปจ์
)
SELECT
customer_id,
predict_churn(customer_features) as churn_probability
FROM customers
WHERE predict_churn(customer_features) > 0.7;
๋ฐ์ดํฐ ๋ถ์๊ณผ ๋จธ์ ๋ฌ๋์ด ๋ ๊ฐ๊น์์ง๋ ๊ฑฐ์ง!
ํธ๋ ๋ 4: ์ฑ๋ฅ ๊ฐ์
PostgreSQL ์ปค๋ฎค๋ํฐ๋ ๊ณ์ํด์ FDW ์ฑ๋ฅ์ ๊ฐ์ ํ๊ณ ์์ด. ๋ณ๋ ฌ ์ฒ๋ฆฌ, ๋ ๋์ ํธ์๋ค์ด, ์ค๋งํธ ์บ์ฑ ๋ฑ์ด ๋ฐ์ ํ๊ณ ์์ง.
PostgreSQL 15, 16์์ FDW ๊ด๋ จ ๋ง์ ๊ฐ์ ์ด ์์๊ณ , ์์ผ๋ก๋ ๊ณ์ ์ข์์ง ๊ฑฐ์ผ! ๐
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ฝ์๋ค๋ฉด ์ ๋ง ๋๋จํด! ๐ FDW์ ๋ํด ๊ฝค ๋ง์ ๊ฑธ ๋ฐฐ์ ์ ๊ฑฐ์ผ.
์ ๋ฆฌํ์๋ฉด, PostgreSQL Foreign Data Wrapper๋:
โจ ์ฌ๋ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ๋๋ก ์ฐ๊ฒฐํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ
๐ ๋ณต์กํ ๋ฐ์ดํฐ ํตํฉ์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ๊ธฐ์
๐ก ๋ ๊ฑฐ์ ์์คํ
๊ณผ ํ๋์ ์ํคํ
์ฒ๋ฅผ ์ฐ๊ฒฐํ๋ ๋ค๋ฆฌ
๐ฏ ์ค๋ฌด์์ ์ ๋ง ์ ์ฉํ ์ค์ ๊ธฐ์
๋ฌผ๋ก FDW๊ฐ ๋ง๋ฅ์ ์๋์ผ. ๋คํธ์ํฌ ์ง์ฐ, ๋ณด์ ์ด์, ์ฑ๋ฅ ์ ์ฝ ๊ฐ์ ํ๊ณ๋ ์์ง. ํ์ง๋ง ์ ์ ํ ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋ฌด๊ธฐ๊ฐ ๋ ์ ์์ด!
์ด์ ์ง์ ํด๋ณด๋ ๊ฒ ์ค์ํด. ๊ฐ๋จํ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ ๋ณต์กํ ์๋๋ฆฌ์ค๋ฅผ ๋ค๋ค๋ณด์. ์ค์๋ ํ๊ณ , ๋ฌธ์ ๋ ๋ง๋๊ณ , ํด๊ฒฐํ๋ฉด์ ์ง์ง ์ค๋ ฅ์ด ๋์ด๋๋ ๊ฑฐ์ผ!
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ๊ฒฝํ์ ๊ณต์ ํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ํผ์ ๊ณ ๋ฏผํ์ง ๋ง๊ณ , ์ปค๋ฎค๋ํฐ์ ํ์ ๋น๋ ค๋ณด์! ๐ค
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๊ณ๋ ๊ณ์ ์งํํ๊ณ ์์ด. FDW๋ ๊ทธ ์งํ์ ์ค์ํ ๋ถ๋ถ์ด์ง. ์ด ๊ธฐ์ ์ ์ ํ์ฉํ๋ฉด, ๋ณต์กํ ๋ฐ์ดํฐ ํ๊ฒฝ์์๋ ์ฐ์ํ ์๋ฃจ์ ์ ๋ง๋ค ์ ์์ ๊ฑฐ์ผ.
์, ์ด์ ๋์ ํ๋ก์ ํธ์ FDW๋ฅผ ์ ์ฉํด๋ณผ ์ค๋น๊ฐ ๋์ด? ํ์ดํ ! ๐ช
โข FDW๋ ์ธ๋ถ DB๋ฅผ ๋ก์ปฌ ํ ์ด๋ธ์ฒ๋ผ ์ฌ์ฉํ๊ฒ ํด์ฃผ๋ ๊ธฐ์
โข ์ค์น โ ์๋ฒ ์์ฑ โ ์ฌ์ฉ์ ๋งคํ โ ์ธ๋ถ ํ ์ด๋ธ ์์ฑ ์์๋ก ์ค์
โข ์ฟผ๋ฆฌ ํธ์๋ค์ด, ๋ฐฐ์น ์ฌ์ด์ฆ ์กฐ์ ๋ฑ์ผ๋ก ์ฑ๋ฅ ์ต์ ํ ๊ฐ๋ฅ
โข ๋ณด์, ๋ชจ๋ํฐ๋ง, ๋ฌธ์ํ๊ฐ ์ค๋ฌด์์ ์ค์
โข ๋ค์ํ FDW๋ฅผ ๊ฒฝํํ๋ฉด์ ์ค๋ ฅ์ ํค์๊ฐ์!
๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋ ๋ ๊น์ด ์๋ ๋ด์ฉ์ ์๊ณ ์ถ๋ค๋ฉด, PostgreSQL ๊ณต์ ๋ฌธ์๋ ์ปค๋ฎค๋ํฐ๋ฅผ ์ฐธ๊ณ ํด๋ด. ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ก ๋ถ๋ชํ๋ณด๋ ๊ฒ ์ต๊ณ ์ ํ์ต ๋ฐฉ๋ฒ์ด๋ผ๋ ๊ฑธ ์์ง ๋ง! ๐
ํ๋ณตํ ์ฝ๋ฉ ๋๊ธธ ๋ฐ๋ผ! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.