[AWS] Athena: DynamoDB ํ†ตํ•ฉ

[์›๋ณธ ๋งํฌ]

DynamoDB๋Š” ๋‚ ๊ฐ•๋„๋“ค ๊ฐ€๋“ํ•œ AWS์—์„œ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•˜๊ณ  ๋น„์šฉ ํšจ์œจ์ ์ธ DB ์„œ๋น„์Šค๋‹ค.
RDB๋งŒํผ ์œ ์—ฐํ•œ ๊ฒ€์ƒ‰๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง„ ์•Š์ง€๋งŒ, ๋ฌด์ œํ•œ ์“ฐ๊ธฐ ํ™•์žฅ์ด ๊ฐ€๋Šฅํ•˜๊ณ , ๋น„์šฉ๋„ ์„ฑ๋Šฅ ๋Œ€๋น„ ์ €๋ ดํ•œ ํŽธ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋กœ๊ทธ ๊ฐ™์€ APPEND Only ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์ฐฝ๊ณ ๋กœ ์“ฐ๊ธฐ ์ข‹๋‹ค.

๊ทธ๋ฆฌ๊ณ  DynamoDB์˜ ๋˜ ํ•˜๋‚˜์˜ ์žฅ์ ์€, Athena & S3 ํŒŒ์ดํ”„๋ผ์ธ๊ณผ๋„ ์•„์ฃผ ์ž˜ ํ†ตํ•ฉ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
์„œ๋น„์Šค๊ฐ€ DynamoDB์— ๋ฐ์ดํ„ฐ๋ฅผ ์Œ“์œผ๋ฉด, ๊ทธ๊ฑธ S3์— parquet์œผ๋กœ ๋ง์•„์„œ ์˜ฌ๋ฆฌ๊ณ , Athena๋กœ ๋Œ€๋Ÿ‰ ์ง‘๊ณ„๋ฅผ ๋‚ ๋ฆฌ๋Š” ์ผ๋ จ์˜ ํŒจํ„ด์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.




์•„ํ‚คํ…์ณ ์„ ํƒ์ง€

Athena์—์„œ DynamoDB๋ฅผ ์†Œ์Šค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.


1. ์ง๊ตฌ๋กœ ๊ฝ‚๊ธฐ

๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์‚ฌ์šฉ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
DynamoDB Connector๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์งํ†ต์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๊ฑด ์„ฑ๋Šฅ์ด๋‚˜ ๋น„์šฉ์ƒ์˜ ์ด์ ์ด ๊ฑฐ์˜ ์—†๋‹ค.
๊ทธ๋ƒฅ DynamoDB์— ๋Œ€์‹  READ ๋‚ ๋ ค์„œ SQL Query์— ๋งž์ถฐ์„œ ๊ฐ€๊ณต์„ ํ•˜๋Š” ๊ฒƒ์— ๋ถˆ๊ณผํ•˜๊ณ , DynamoDB์˜ ์„ฑ๋Šฅ ํ•œ๊ณ„๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›๋Š”๋‹ค. ๋น„์šฉ๋„ DynamoDB RCU ๊ธฐ๋ฐ˜์œผ๋กœ ๋œฏ๋Š”๋‹ค.
์ด๊ฑด ์ž‘์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด์„œ JOIN ํ•˜๋Š” ์šฉ๋„๋กœ๋งŒ ์“ฐ๊ฑฐ๋‚˜ ํ•ด์•ผํ•œ๋‹ค.



2. S3 Export Only

๊ทธ ๋‹ค์Œ์œผ๋กœ ๋‚˜์€ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
DynamoDB๋Š” PITR ๋ฐฑ์—… ๊ธฐ๋ฐ˜์œผ๋กœ S3์— exportํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ๊ทธ๋Ÿฌ๊ณ  ๋‚˜๋ฉด ์•„ํ…Œ๋‚˜์—์„œ ๋ฐ”๋กœ ๊ฝ‚์•„์„œ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.

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



3. S3 Export & CTAS (parquet)

Athena CTAS๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์“ฐ๋ฉด Athena๋งŒ์„ ์œ„ํ•œ ์ตœ์ ํ™”๋œ ๊ตฌ์กฐ๋กœ ์žฌ์ƒ์„ฑ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.



4. Best Practice?

Best Practice๋กœ ๊ฐ„์ฃผ๋˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์ฆ๋ถ„ํ˜• S3 export๋ฅผ ํ†ตํ•ด์„œ S3์— ๋ถ“๊ณ , ๊ทธ๊ฑธ Athena๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์žฌ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
ETL ์ œ์–ด์—๋Š” Step Function์ด ๊ฐ€์žฅ ๋‹จ์ˆœํ•˜๊ณ  ํŽธ๋ฆฌํ•œ ํŽธ์ด๋‹ค.




๋น„์šฉ

https://aws.amazon.com/ko/dynamodb/pricing/on-demand/

๋น„์šฉ ๋ฐœ์ƒ ์š”์†Œ๋Š” ๋ช‡๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค.
PITR์ด๋ผ๋Š” ๋ณต๊ตฌ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•ด์•ผ ๊ทธ๊ฑธ๋กœ S3์— ๋ณด๋‚ด๋Š”๊ฑด๋ฐ, ๊ทธ๊ฒƒ๋„ ๋ฐฑ์—…๋ณธ ์ „์ฒด ์šฉ๋Ÿ‰ ๊ธฐ์ค€์œผ๋กœ ์š”๊ธˆ์„ ๋œฏ๋Š”๋‹ค.

์ „์ฒด ๋ฐฑ์—…์ด 200GB๋ฉด 40๋‹ฌ๋Ÿฌ ์ •๋„ ๋œฏ๋Š” ์…ˆ์ด๋‹ค.
๊ทผ๋ฐ ์ตœ์†Œํ•œ๋„์ธ 1์ผ๋กœ ์žก์œผ๋ฉด ๊ทธ ๋‚ ์˜ ๋ณ€๊ฒฝ๋ถ„๋งŒ ๋ณด์กด๋ ํ…Œ๋‹ˆ, ๊ทธ๋ ‡๊ฒŒ ํฌ๊ฒŒ ๋“ค์ง„ ์•Š์„ ๊ฒƒ์ด๋‹ค.

๋‚ด๋ณด๋‚ด๋Š” ํ–‰์œ„ ์ž์ฒด์—๋„ ํฌ๊ธฐ๋‹น ๋น„์šฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.

100GB ์ •๋„ ๋œ๋‹ค๋ฉด ๋งŒ์›์ฏค ๋‚˜์˜ค๋Š” ์…ˆ์ด๋‹ค.


๊ทธ๋ฆฌ๊ณ  ๋‹น์—ฐํžˆ S3 ๋น„์šฉ๋„ ๋ฐœ์ƒํ•œ๋‹ค.
์ €์žฅ ๋น„์šฉ์€ ๋‹น์—ฐํžˆ ๋”ฐ๋กœ๊ณ , parquet์œผ๋กœ ์žฌ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์—์„œ S3 ํ‘œ์ค€ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋น„์šฉ์ด ๋ฐœ์ƒํ•œ๋‹ค.




์‚ฌ์šฉํ•ด๋ณด๊ธฐ

DynamoDB ํ…Œ์ด๋ธ”์„ ์ง์ ‘ ๋งŒ๋“ค๊ณ , ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉด์„œ ์‚ฌ์šฉ๋ฒ•์„ ์ตํ˜€๋ณด์ž.

์ ๋‹นํžˆ ํ…Œ์ด๋ธ” ํ•˜๋‚˜ ๋งŒ๋“ค๊ณ 


๋ฐฑ์—… ์˜ต์…˜์ธ PITR์„ ์ผœ์•ผ ํ•œ๋‹ค.
์ด๊ฑด ๋ณธ์งˆ์ ์œผ๋กœ ๋ฐฑ์—… ๋ฐ์ดํ„ฐ๋ผ์„œ, ๋”ฐ๋กœ ๋ฐฑ์—…์ •์ฑ…์ด ํ•„์š”์—†๊ณ  S3 Export์šฉ์œผ๋กœ๋งŒ ์“ธ๊ฑฐ๋ผ๋ฉด 1์ผ๋กœ ํ•ด๋„ ๋œ๋‹ค.


๊ทธ๋ฆฌ๊ณ  ์ ๋‹นํžˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๋Š”๋‹ค.




S3 Export

์ด์ œ S3๋กœ ๋ถ€์„ ์ค€๋น„๋Š” ๋๋‹ค.
Dynamo์— ๋ณด๋ฉด ๋‚ด๋ณด๋‚ด๊ธฐ ํƒญ์ด ์žˆ๋Š”๋ฐ,


์—ฌ๊ธฐ์„œ ๋‚ด๋ณด๋‚ผ S3 ๋ฒ„ํ‚ท ๊ณ ๋ฅด๊ณ  ํŠธ๋ฆฌ๊ฑฐํ•˜๋ฉด ๋œ๋‹ค.
์ „์ฒด ๋‚ด๋ณด๋‚ด๊ธฐ์™€ ์ฆ๋ถ„(๋ถ€๋ถ„) ๋‚ด๋ณด๋‚ด๊ธฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ผ๋‹จ์€ ์ „์ฒด๋กœ ๋จผ์ € ํ•ด๋ณธ๋‹ค.


๊ทธ๋ฆฌ๊ณ  ์ด ๋‚ด๋ณด๋‚ด๊ธฐ ์ž์ฒด๋Š” ๋น„๋™๊ธฐ API๋ผ์„œ ์ฆ‰์‹œ ๋˜์ง„ ์•Š๋Š”๋‹ค.

์ข€ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ์„ฑ๊ณต์œผ๋กœ ๋๋‚ ํ…๋ฐ

๊ทธ๋Ÿผ ๋ชฉ์ ์ง€์— ์ด๋Ÿฐ ํŒŒ์ผ๋“ค์ด ์ง€์ €๋ถ„ํ•˜๊ฒŒ ๋ง‰ ๋–จ์–ด์ ธ์žˆ์„ ๊ฒƒ์ด๋‹ค.
์ด๊ฒŒ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋“ค์ด๋‹ค.




๋งคํ•‘์šฉ External ํ…Œ์ด๋ธ” ๊ตฌ์„ฑํ•˜๊ธฐ

์ž. ์ด์ œ ์ค€๋น„๋Š” ๊ฑฐ์˜ ๋๋‹ค.
Athena๋กœ ์ด๋™ํ•ด์„œ ๋งคํ•‘ ํ…Œ์ด๋ธ”์„ ๊ตฌ์„ฑํ•ด์ค€๋‹ค.
๋ฐฉ๊ธˆ Export๋œ json.gz๋“ค์ด ์žˆ๋Š” ๊ฒฝ๋กœ๋ฅผ ๋„ฃ๊ณ , ์–ด๋–ค ํ•„๋“œ๋“ค์ด ์žˆ๋Š”์ง€ ์ฒดํฌํ•ด์„œ ํ‚ค ๋ชฉ๋ก์„ ์„ธํŒ…ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

CREATE EXTERNAL TABLE ์™ธ๋ถ€ํ…Œ์ด๋ธ”๊ตฌ์„ฑ (
    Item struct<
        ํ‚ค:   struct<S:ํƒ€์ž…>,
        ...
    >
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://๋ฒ„ํ‚ท/๊ฒฝ๋กœ/dynamo/AWSDynamoDB/?/data/';

์ด๊ฒƒ ์ž์ฒด๋Š” ๊ธฐ์กด ๋ฐ์ดํ„ฐ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋งŒ ์ถ”๊ฐ€ํ•ด์ฃผ๋Š”๊ฑฐ๋ผ์„œ, ๋น„์šฉ์ด๋‚˜ ์„ฑ๋Šฅ์„ ๋จน์ง€๋Š” ์•Š๋Š”๋‹ค.


๊ทธ๋Ÿผ ์ผ๋‹จ ์กฐํšŒ๋ฅผ ํ•ด๋ณผ ์ˆ˜๋Š” ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์šฐ๋ฆฐ ์—ฌ๊ธฐ์„œ ํ•œ๋ฐœ ๋” ๋‚˜์•„๊ฐ€์•ผ ํ•œ๋‹ค.
athena์˜ CTAS ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ parquet๋กœ ์žฌ์ƒ์„ฑํ•ด์ค€๋‹ค.

CREATE TABLE access_logs_parquet
WITH (
  format = 'PARQUET',
  parquet_compression = 'SNAPPY',
  external_location = 's3://์ €์žฅ๊ฒฝ๋กœ/'
) AS
SELECT
  Item.id.S   AS id,
  Item.ip.S   AS ip,
  Item.path.S AS path
FROM ์ž„์‹œํ…Œ์ด๋ธ”;

๊ทธ๋Ÿผ ์‹ค์ œ๋กœ ์ตœ์ข… ๋ชฉ์ ์ง€์— ์••์ถ•๋œ parquet ์—”ํŠธ๋ฆฌ๊ฐ€ ์ƒ์„ฑ๋  ๊ฒƒ์ด๊ณ 

์ฟผ๋ฆฌ๋„ ๋‚ ๋ ค๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

์ž˜ ๋“ค์–ด๊ฐ„ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ฟผ๋ฆฌ ๋‚ ๋ฆฌ๋Š”๊ฑด ๊ทธ๋ƒฅ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋œ๋‹ค.




DynamoDB์˜ ์ฆ๋ถ„ Export (Incremental Export)

๊ทผ๋ฐ ๋ฐฉ๊ธˆ ์‚ฌ์šฉํ•œ ๋ฐฉ์‹์€ ์ž˜ ๋™์ž‘ํ•˜๊ธด ํ•˜์ง€๋งŒ, ํ™•์žฅ์„ฑ์ด ์ข‹์€ ๋ฐฉ์‹์€ ์•„๋‹ˆ๋‹ค.
๋งค๋ฒˆ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋ฐ€์–ด๋„ฃ๋Š” ๊ฒƒ์€ ๋„ˆ๋ฌด ์˜ค๋ž˜๊ฑธ๋ฆฌ๊ณ , ๋น„์šฉ๋„ ๋น„์šฉ๋Œ€๋กœ ๋จน๋Š”๋‹ค.

๋‹คํ–‰ํžˆ DynamoDB๋Š” ์ด๋Ÿฐ ๋”œ๋ ˆ๋งˆ๋ฅผ ์ž˜ ํ•ด์†Œํ•ด์ค€๋‹ค.
DynamoDB์˜ ๋ฐฑ์—… ์‹œ์Šคํ…œ ์ž์ฒด๊ฐ€ ํƒ€์ž„์Šคํƒฌํ”„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•„ํ„ฐ๋ง์ด ๋˜๋Š” ๊ตฌ์กฐ๋กœ ๋˜์–ด์žˆ์–ด์„œ, "๋ช‡์‹œ๊ฐ„ ์ „๋ถ€ํ„ฐ์˜ ๋ฐ์ดํ„ฐ"๋‚˜ "์–ด์ œ๋ถ€ํ„ฐ์˜ ๋ฐ์ดํ„ฐ" ๊ฐ™์€ ์‹์œผ๋กœ ๋ถ€๋ถ„์  EXPORT๊ฐ€ ์ž˜ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๋ฒˆ์—๋Š” ํ–‰์„ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ์— ๋™๊ธฐํ™”๋ฅผ ๊ตฌ์„ฑํ•ด๋ณด์ž.

id=4444๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ 


์ฆ๋ถ„ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์„ ํƒํ•ด์„œ ํŠธ๋ฆฌ๊ฑฐํ•œ๋‹ค.


๊ทธ๋Ÿผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ json.gz๊ฐ€ ์ž”๋œฉ ๋–จ์–ด์ง„๋‹ค.

๊ทผ๋ฐ ์—ฌ๊ธฐ์„œ๋ถ€ํ„ด ์ข€ ๋‹ค๋ฅด๋‹ค.
์ „์ฒด ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” ์ง„์งœ ์ „์ฒด ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๋Š” ๊ฑฐ๋ผ์„œ ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•œ๋ฐ, ์ฆ๋ถ„ ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” UPDATE/DELETE ๊ฐ™์€ ์ƒํƒœ ์ •๋ณด๋„ ํ•จ๊ป˜ ํฌํ•จ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ ์‹œ๊ธฐ์˜ "๋ชจ๋“  ๋ณ€๊ฒฝ"์„ ๊ฐ€์ ธ์˜จ๋‹ค.

CREATE EXTERNAL TABLE access_log_incremental (
    Metadata struct<
        WriteTimestampMicros: string
    >,
    Keys struct<
        id: struct<S:string>
    >,
    NewImage struct<
        id:   struct<S:string>,
        ip:   struct<S:string>,
        path: struct<S:string>
    >,
    OldImage struct<
        id:   struct<S:string>,
        ip:   struct<S:string>,
        path: struct<S:string>
    >
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
LOCATION 's3://๋ฒ„ํ‚ท/๊ฒฝ๋กœ/dynamo/AWSDynamoDB/?/data/';

๊ทธ๋ž˜์„œ ์ด๋Ÿฐ ์‹์œผ๋กœ ๋ญ๊ฐ€ ์ข€ ๋งŽ๋‹ค.
INSERT/UPDATE๋Š” NewImage๋ฅผ ์ฝ์–ด์„œ ๋„ฃ์–ด์•ผ ํ•˜๊ณ , DELETE๋Š” Keys๋งŒ ์ „๋‹ฌ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ๊ธฐ๋Šฅ ๊ตฌ์กฐ์— ๋”ฐ๋ผ์„œ ์ œ์•ฝ์กฐ๊ฑด์ด ์ข€ ๋‹ฌ๋ผ์ง„๋‹ค.
APPEND-ONLY์˜ ๋‹จ์ˆœํ•œ ์‹œ์Šคํ…œ์ด๋ผ๋ฉด, ๊ทธ๋ƒฅ NewImage๋งŒ ๊ฑธ๋Ÿฌ์„œ ๊ธฐ์กด CTAS ํ…Œ์ด๋ธ”์— insertํ•˜๋ฉด ๋œ๋‹ค.

๊ทธ๋Ÿผ ์ž๋™์œผ๋กœ parquet ๋ฉ์–ด๋ฆฌ๊ฐ€ ์ถ”๊ฐ€๋  ๊ฒƒ์ด๊ณ 


์กฐํšŒ๋„ ์ž˜ ๋  ๊ฒƒ์ด๋‹ค.

๊ทผ๋ฐ UPDATE/DELETE๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์ข€ ๋ณต์žกํ•ด์ง„๋‹ค.

์‚ฌ์‹ค ์ง€๊ธˆ ์œ„์˜ ์ฟผ๋ฆฌ๋„, ๊ธฐ์กด parquet์— ๋“ค์–ด์žˆ๋˜ ID์™€ ๋™์ผํ•œ INSERT๊ฐ€ ๋“ค์–ด์˜จ๋‹ค๋ฉด ์ค‘๋ณต ID๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค. ์›๋ณธ ๋ฐ์ดํ„ฐ๋ถ€ํ„ฐ๊ฐ€ NEW INSERT ONLY๋ผ๋Š” ์ „์ œ ํ•˜์—์„œ๋งŒ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ตฌ์กฐ๋‹ค.




IceBerg๋ฅผ ์‚ฌ์šฉํ•œ UPDATE/DELETE

์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ๋˜๋Š” ๊ฒƒ์€ parquet ์ž์ฒด๊ฐ€ ์ˆ˜์ •์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ๊ทœ๊ฒฉ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๊ทธ์ € ๋ฐ์ดํ„ฐ๋ฅผ ์••์ถ•ํ•ด์„œ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํฌ๋งท์— ๋ถˆ๊ณผํ•˜๋‹ค.

๊ทธ๋ž˜์„œ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ์ดํ„ฐ์…‹์— ๋Œ€ํ•ด ์ˆ˜์ •๊ณผ ์‚ญ์ œ๋ฅผ ๊ณ ๋ คํ•ด์„œ parquet์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด, iceberg ์—”์ง„์„ ์‚ฌ์šฉํ•˜๋Š” ํŽธ์ด ๊ถŒ์žฅ๋œ๋‹ค.
๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‹์œผ๋กœ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๋ฉด iceberg์˜ ํŒŒํ‹ฐ์…˜ ๊ด€๋ฆฌ ๋Šฅ๋ ฅ์„ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

CREATE TABLE access_log_iceberg
WITH (
  table_type = 'ICEBERG',
  format = 'PARQUET',
  location = 's3://.../iceberg/access_log/',
  is_external = false
) AS
SELECT id, ip, path FROM access_log_parquet;

๊ทธ๋Ÿฌ๋ฉด ์ด์ œ MERGE ๊ตฌ๋ฌธ์„ ํ†ตํ•ด์„œ ํŒŒํ‹ฐ์…˜์„ ์žฌ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.
DELETE๋‚˜ UPDATE๋ฅผ ๋ฐ˜์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

MERGE INTO access_log_iceberg t
USING (
  SELECT
    COALESCE(NewImage.id.S, Keys.id.S) AS id,
    NewImage.ip.S   AS ip,
    NewImage.path.S AS path,
    NewImage IS NULL AS is_deleted,
    CAST(Metadata.WriteTimestampMicros AS bigint) AS change_ts,
    ROW_NUMBER() OVER (
      PARTITION BY COALESCE(NewImage.id.S, Keys.id.S)
      ORDER BY CAST(Metadata.WriteTimestampMicros AS bigint) DESC
    ) AS rn
  FROM access_log_incremental
) s
ON t.id = s.id AND s.rn = 1
WHEN MATCHED AND s.is_deleted THEN DELETE
WHEN MATCHED THEN UPDATE SET ip = s.ip, path = s.path
WHEN NOT MATCHED AND NOT s.is_deleted THEN INSERT (id, ip, path) VALUES (s.id, s.ip, s.path);



ํŒŒํ‹ฐ์…˜ ์ „๋žต

Athena๋ฅผ ๋น„์šฉํšจ์œจ์ ์œผ๋กœ, ๊ทธ๋ฆฌ๊ณ  ๋” ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํŒŒํ‹ฐ์…˜ ์ „๋žต์„ ์ž˜ ์žก์•„์•ผ ํ•œ๋‹ค.
์ด๊ฑด ๋ณ„๋„ ํฌ์ŠคํŠธ๋กœ ๊ฐˆ์Œํ•œ๋‹ค.
https://blog.naver.com/sssang97/223716323937



์ฐธ์กฐ
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/S3DataExport.HowItWorks.html

https://docs.aws.amazon.com/athena/latest/ug/connectors-dynamodb.html