์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ PostgreSQL Foreign Data Wrapper๋กœ ์™ธ๋ถ€ DB์™€ ์นœํ•ด์ง€๊ธฐ

๐Ÿš€ PostgreSQL Foreign Data Wrapper๋กœ ์™ธ๋ถ€ DB์™€ ์นœํ•ด์ง€๊ธฐ

๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํ•˜๋‚˜๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ์ˆ 

์•ˆ๋…•! ์˜ค๋Š˜์€ ์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. ๐Ÿ˜Š PostgreSQL์˜ Foreign Data Wrapper(FDW)๋ผ๋Š” ๋…€์„์ธ๋ฐ, ์ด๊ฒŒ ๋ญ๋ƒ๋ฉด ๋ง์ด์•ผ... ๋งˆ์น˜ ์—ฌ๋Ÿฌ ๋‚˜๋ผ ์–ธ์–ด๋ฅผ ๋™์‹œํ†ต์—ญํ•ด์ฃผ๋Š” ํ†ต์—ญ์‚ฌ ๊ฐ™์€ ์กด์žฌ๋ผ๊ณ  ๋ณด๋ฉด ๋ผ. ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋“ค์ด ๊ฐ์ž์˜ ์–ธ์–ด๋กœ ๋งํ•˜๊ณ  ์žˆ์„ ๋•Œ, PostgreSQL์ด ์ค‘๊ฐ„์—์„œ "์•ผ, ๋„ˆํฌ๋“ค ๋Œ€ํ™” ์ข€ ํ•˜์ž!"๋ผ๊ณ  ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ฑฐ์ง€.

์š”์ฆ˜ ์‹ค๋ฌด์—์„œ๋Š” ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋งŒ ์“ฐ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๊ฑฐ์˜ ์—†์–ด. MySQL์— ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ณ , MongoDB์— ๋กœ๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๊ณ , Oracle์— ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ์ด ๋Œ์•„๊ฐ€๊ณ ... ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ฉํ•ด์„œ ๋ด์•ผ ํ•  ๋•Œ๊ฐ€ ๋งŽ๊ฑฐ๋“ . ๊ทธ๋Ÿด ๋•Œ FDW๊ฐ€ ์ง„์งœ ๋น›์„ ๋ฐœํ•˜์ง€! ๐ŸŒŸ

PostgreSQL MySQL Oracle MongoDB Redis FDW๋กœ ๋ชจ๋“  DB๋ฅผ ํ•˜๋‚˜๋กœ!

๐ŸŽฏ 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์˜ ๋งˆ๋ฒ•

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์œผ๋กœ ๋ถ„์„ํ•˜๊ธฐ

์ฟผ๋ฆฌ๊ฐ€ ๋А๋ฆฌ๋‹ค ์‹ถ์œผ๋ฉด EXPLAIN (ANALYZE, VERBOSE)๋ฅผ ์จ์„œ ์‹คํ–‰ ๊ณ„ํš์„ ํ™•์ธํ•ด๋ด. ์–ด๋А ๋ถ€๋ถ„์—์„œ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š”์ง€, ํ‘ธ์‹œ๋‹ค์šด์ด ์ œ๋Œ€๋กœ ๋˜๊ณ  ์žˆ๋Š”์ง€ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ์–ด!

5. ๋จธํ‹ฐ๋ฆฌ์–ผ๋ผ์ด์ฆˆ๋“œ ๋ทฐ ํ™œ์šฉ

์ž์ฃผ ์กฐํšŒํ•˜๋Š” ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋Š” ๋จธํ‹ฐ๋ฆฌ์–ผ๋ผ์ด์ฆˆ๋“œ ๋ทฐ๋กœ ๋งŒ๋“ค์–ด์„œ ๋กœ์ปฌ์— ์บ์‹ฑํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ.

-- ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ์ปฌ์— ์บ์‹ฑ
CREATE MATERIALIZED VIEW cached_customers AS
SELECT * FROM foreign_customers
WHERE active = true;

-- ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ฐฑ์‹ 
REFRESH MATERIALIZED VIEW cached_customers;

์‹ค์‹œ๊ฐ„์„ฑ์ด ์ค‘์š”ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ผ๋ฉด ์ด ๋ฐฉ๋ฒ•์ด ์—„์ฒญ ํšจ๊ณผ์ ์ด์•ผ. ๋„คํŠธ์›Œํฌ ์ง€์—ฐ ์—†์ด ๋กœ์ปฌ ์†๋„๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! โšก

์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žต ์ฟผ๋ฆฌ ํ‘ธ์‹œ๋‹ค์šด ์กฐ๊ฑด์„ ์™ธ๋ถ€๋กœ ๋ฐฐ์น˜ ์‚ฌ์ด์ฆˆ ํ•œ๋ฒˆ์— ๋งŽ์ด ์ธ๋ฑ์Šค ํ™œ์šฉ ํžŒํŠธ ์ œ๊ณต ์บ์‹ฑ ๋กœ์ปฌ ์ €์žฅ ๐Ÿ“Š โšก ์ตœ์ ํ™” ์ „ ์ตœ์ ํ™” ํ›„ ์„ฑ๋Šฅ 3~10๋ฐฐ ํ–ฅ์ƒ!

๐Ÿ”’ ๋ณด์•ˆ ๊ณ ๋ ค์‚ฌํ•ญ

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 ๊ณ ๊ธ‰ ํ™œ์šฉ ๋กœ๋“œ๋งต ๊ธฐ๋ณธ ์—ฐ๋™ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒŒํ‹ฐ์…”๋‹ ์ปค์Šคํ…€ FDW ์ดˆ๊ธ‰ ๊ธฐ๋ณธ ์„ค์ • ๋‹จ์ˆœ ์กฐํšŒ 1-2๊ฐœ DB ์—ฐ๋™ ์ค‘๊ธ‰ ์ฟผ๋ฆฌ ์ตœ์ ํ™” ๋ณต์žกํ•œ ์กฐ์ธ 3-5๊ฐœ DB ์—ฐ๋™ ๊ณ ๊ธ‰ ๋ถ„์‚ฐ ์•„ํ‚คํ…์ฒ˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ ์ „๋ฌธ๊ฐ€ ์ปค์Šคํ…€ ๊ฐœ๋ฐœ API ํ†ตํ•ฉ ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ

๐Ÿ’ผ ์‹ค๋ฌด ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค

์ด์ œ ์‹ค๋ฌด์—์„œ 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๋ฅผ ๋งˆ์Šคํ„ฐํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์ˆœ์„œ๋กœ ๊ณต๋ถ€ํ•˜๋Š” ๊ฑธ ์ถ”์ฒœํ•ด! ๐Ÿ“š

1๋‹จ๊ณ„: ๊ธฐ์ดˆ ๋‹ค์ง€๊ธฐ (1-2์ฃผ)

โ€ข PostgreSQL ๊ธฐ๋ณธ ๊ฐœ๋… ๋ณต์Šต
โ€ข SQL/MED ํ‘œ์ค€ ์ดํ•ด
โ€ข ๊ฐ„๋‹จํ•œ postgres_fdw๋กœ ์‹ค์Šต
โ€ข ๊ธฐ๋ณธ ์ฟผ๋ฆฌ ์ž‘์„ฑ ์—ฐ์Šต
2๋‹จ๊ณ„: ๋‹ค์–‘ํ•œ FDW ๊ฒฝํ—˜ (2-3์ฃผ)

โ€ข mysql_fdw ์„ค์น˜ ๋ฐ ์„ค์ •
โ€ข ๋‹ค๋ฅธ RDBMS FDW ์‹œ๋„ (Oracle, SQL Server ๋“ฑ)
โ€ข NoSQL FDW ๊ฒฝํ—˜ (MongoDB, Redis)
โ€ข ๊ฐ FDW์˜ ํŠน์„ฑ ํŒŒ์•…
3๋‹จ๊ณ„: ์„ฑ๋Šฅ ์ตœ์ ํ™” (2-3์ฃผ)

โ€ข EXPLAIN ๋ถ„์„ ๋Šฅ๋ ฅ ํ‚ค์šฐ๊ธฐ
โ€ข ์ฟผ๋ฆฌ ํ‘ธ์‹œ๋‹ค์šด ์ดํ•ด
โ€ข ์ธ๋ฑ์Šค ์ „๋žต ์ˆ˜๋ฆฝ
โ€ข ์บ์‹ฑ ๊ธฐ๋ฒ• ์ ์šฉ
4๋‹จ๊ณ„: ์‹ค์ „ ํ”„๋กœ์ ํŠธ (4์ฃผ ์ด์ƒ)

โ€ข ์‹ค์ œ ์—…๋ฌด ๋ฌธ์ œ ํ•ด๊ฒฐ
โ€ข ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ํ†ตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค
โ€ข ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
โ€ข ์žฅ์•  ๋Œ€์‘ ๊ฒฝํ—˜

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ 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
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.