๐๐ JSON ํ์ด๋ก๋์์ ์ ๋ฐ์ดํธ/์ฝ์ /์ผ์ ์ญ์
21706 ๋จ์ด databasepostgresyugabytedbsql
k1 int
)๋ฅผ ๋ณด์ ํฉ๋๋ค. ์ฌ๋ฌ ๋ ์ฝ๋(ํค์ ๋ํ k2 int
๋ฐ ๊ฐ์ ๋ํ v1 int
, v2 int
๋ด ์์ ์์์) deleted boolean
) ts timestamptz
๋ฅผ ์ถ๊ฐํฉ๋๋ค.๊ทธ๋ค์ ํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ Python Pandas๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ๋ ์ฝ๋๋ฅผ ๊ฒ์ํ๊ณ ์ ํ์ด๋ก๋์ ๋น๊ตํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ค์ ์๋๋ค. YugabyteDB์ ๊ฐ์ ๋ถ์ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ ๋๋ค. ์๋ํ๋ฉด ํ์ฅ์ด ๊ฐ๋ฅํ๊ณ (๋ชจ๋ ๋ ธ๋์ ์ฐ๊ฒฐํ ์ ์์) ํ๋์ ์๋ฒ ์ธก ํธ๋์ญ์ ์์ ์คํ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค(ํด๋ญ ์คํ์ ๊ฒฝ์ฐ ํฌ๋ช ํ ์ฌ์๋๋ฅผ ์ฒ๋ฆฌํ๊ธฐ๊ฐ ๋ ์ฝ์ต๋๋ค). ์๋ณต์ ์ค์ ๋๋ค(ํ ๋ฐฐ์น ์์ ). ๊ทธ๋ฆฌ๊ณ PostgreSQL ํธํ์ฑ์ ํตํด JSON ์ฒ๋ฆฌ ๋ฐ INSERT ... ON CONFLICT ... UPDATE ๋์์ ๊นจ๋ํ๊ณ ๊ฐ๋จํ SQL๋ก ๋์ผํ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
์์
๋ค์์ ๋ด ํ ์ด๋ธ์ ์ ์ธํ๋ ๊ฐ๋จํ ์์ ๋๋ค.
create table demo (
k1 int, k2 int, v1 int, v2 text
, deleted boolean, ts timestamptz
, primary key ( k1,k2 )
);
์ด๊ฒ์ PostgreSQL ๋ฐ YugabyteDB์์ ์๋ํฉ๋๋ค. YugabyteDB์์ ๊ธฐ๋ณธ ํค๋ ๊ธฐ๋ณธ์ ์ผ๋ก
lsm (k1 HASH, k2 ASC)
๋ก ์ค์ ๋์ด ์์ด ID์ ๋ฐฐํฌํ๊ณ ๋ ์ฝ๋๋ฅผ ๊ฐ์ ์์น์ ๋ฐฐ์นํ๋ ์ ์ฌ์ฉ ์ฌ๋ก์ ์ ํฉํฉ๋๋ค.ํ๋์ SQL ์ฟผ๋ฆฌ์์ ์ ์ฒด ๋ ผ๋ฆฌ๋ฅผ ๊ตฌํํ๊ฒ ์ต๋๋ค. WITH ์ (์ผ๋ช CTE - Common Table Expressions)์ ๊ฐ๋ ์ฑ์ ์ ์งํฉ๋๋ค.
payload
๋ $2
๋๋ถ์ ๋ ์ฝ๋์ jsonb_to_recordset
๋ก ์ ๋ฌ๋ ๋ด JSON ํ์ด๋ก๋๋ฅผ ์ฝ๊ณ ID$1 as k1
๋ฅผ ์ถ๊ฐํฉ๋๋ค.to_upsert
๋ payload
๋ฅผ ๋์ ํ์์ผ๋ก ์ง์ ํ๊ณ is_deleted
๋ฅผ false๋ก ์ค์ ํ๊ณ ํ์์คํฌํto_soft_delete
๋ ํ์ฌ ๋ ์ฝ๋๋ฅผ ๊ฒ์ํ๊ณ payload
์ ๋น๊ตํ์ฌ is_deleted
๊ฐ trueto_upsert
์ to_soft_delete
์ ๊ฒฐํฉ์ด INSERT ... ON CONFLICT ... UPDATE๋ค์์ ์ค๋น๋ ์ง์ ์ ๋๋ค.
prepare etl (int, jsonb) as
with
payload as (
select $1 as k1,* from jsonb_to_recordset($2) as payload( k2 int, v1 int, v2 text )
),
to_upsert as (
select k1, k2, v1, v2, false as deleted, now() as ts from payload
),
to_soft_delete as (
select k1, k2, v1, v2, true as deleted, now() as ts from demo
where k1=$1
and (k1, k2) not in ( select k1, k2 from payload)
)
insert into demo select * from to_upsert union select * from to_soft_delete
on conflict (k1, k2) do
update set v1=excluded.v1, v2=excluded.v2, deleted=excluded.deleted, ts=excluded.ts
;
์ด๊ฒ์ ์ ์ฅ ํ๋ก์์ ๋ก ์ฝ๋ฉ๋ ์ ์์ง๋ง ์ค๋น๋ ๋ช ๋ น๋ฌธ์ ๊ฐ ํธ์ถ์ ๋ํด ์ด๋ฅผ ๊ตฌ๋ฌธ ๋ถ์ํ๊ณ ์ต์ ํํ์ง ์์ต๋๋ค. ์ฐ๊ฒฐ ํ ์ด๊ธฐํ ๋ช ๋ น์์ ์ฝ๊ฒ ์ค๋นํ ์ ์์ต๋๋ค.
๋ด ํ ์ด๋ธ์ด ๋น์ด ์์ต๋๋ค. 3๊ฐ์ ๋ ์ฝ๋๊ฐ ์๋
k1=0
์ ๋ํ ํ์ด๋ก๋๋ฅผ ์ฝ์
ํ๊ฒ ์ต๋๋ค.execute etl(0, $$
[
{"k2":1,"v1":1,"v2":"my first insert"},
{"k2":2,"v1":1,"v2":"my first insert"},
{"k2":3,"v1":1,"v2":"my first insert"}
]
$$::jsonb);
3๊ฐ์ ํ์ด ์ฝ์ ๋์์ต๋๋ค.
yugabyte=# select * from demo;
k1 | k2 | v1 | v2 | deleted | ts
----+----+----+-----------------+---------+-------------------------------
0 | 1 | 1 | my first insert | f | 2022-04-29 14:39:08.467744+00
0 | 2 | 1 | my first insert | f | 2022-04-29 14:39:08.467744+00
0 | 3 | 1 | my first insert | f | 2022-04-29 14:39:08.467744+00
(3 rows)
์ด์ ๋์ผํ
k1=0
์์ ์ ํ์ด๋ก๋๊ฐ 2๊ฐ์ ํ์ ์
๋ฐ์ดํธํ๊ณ ๋ค๋ฅธ ํ์ ์ ๊ฑฐํ๊ณ ์ ํ์ ์ถ๊ฐํฉ๋๋ค.execute etl(0,$$
[
{"k2":1,"v1":1,"v2":"my update"},
{"k2":2,"v1":1,"v2":"my update"},
{"k2":4,"v1":1,"v2":"my second insert"}
]
$$::jsonb);
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
yugabyte=# select * from demo;
k1 | k2 | v1 | v2 | deleted | ts
----+----+----+------------------+---------+-------------------------------
0 | 1 | 1 | my update | f | 2022-04-29 14:41:00.109561+00
0 | 2 | 1 | my update | f | 2022-04-29 14:41:00.109561+00
0 | 3 | 1 | my first insert | t | 2022-04-29 14:41:00.109561+00
0 | 4 | 1 | my second insert | f | 2022-04-29 14:41:00.109561+00
(4 rows)
์ฑ๋ฅ
ํญ์ ์คํ ๊ณํ์ ํ์ธํ์ญ์์ค.
yugabyte=# explain (costs off, analyze) execute etl(0,$$ [9/3108]
[
{"k2":1,"v1":1,"v2":"my update"},
{"k2":2,"v1":1,"v2":"my update"},
{"k2":4,"v1":1,"v2":"my second insert"}
]
$$::jsonb);
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Insert on demo (actual time=11.787..11.787 rows=0 loops=1)
Conflict Resolution: UPDATE
Conflict Arbiter Indexes: demo_pkey
Tuples Inserted: 0
Conflicting Tuples: 4
CTE payload
-> Function Scan on jsonb_to_recordset payload (actual time=0.011..0.012 rows=3 loops=1)
CTE to_upsert
-> CTE Scan on payload payload_1 (actual time=0.013..0.014 rows=3 loops=1)
CTE to_soft_delete
-> Index Scan using demo_pkey on demo demo_1 (actual time=1.079..1.081 rows=1 loops=1)
Index Cond: (k1 = 0)
Filter: (NOT (hashed SubPlan 3))
Rows Removed by Filter: 3
SubPlan 3
-> CTE Scan on payload payload_2 (actual time=0.001..0.001 rows=3 loops=1)
-> HashAggregate (actual time=1.105..1.110 rows=4 loops=1)
Group Key: to_upsert.k1, to_upsert.k2, to_upsert.v1, to_upsert.v2, to_upsert.deleted, to_upsert.ts
-> Append (actual time=0.014..1.099 rows=4 loops=1)
-> CTE Scan on to_upsert (actual time=0.014..0.016 rows=3 loops=1)
-> CTE Scan on to_soft_delete (actual time=1.080..1.082 rows=1 loops=1)
Planning Time: 0.262 ms
Execution Time: 11.864 ms
(23 rows)
์ด๊ฒ์
Index Cond: (k1 = 0)
์ ๋ํ ํ๋์ ํ๋ธ๋ฆฟ๋ง ์ฝ๊ณ ๋ชจ๋ ํ์ด ํจ๊ป ๋ฐฐ์น๋ ๊ธฐ๋ณธ ํค์ Index Scan
๊ฐ ์์ต๋๋ค. ์ฌ๊ธฐ์ (k1 HASH, k2 ASC)
๊ฐ ์ค์ํฉ๋๋ค.๋ฌผ๋ก ์ด๊ฒ์ ๋์ผํ ๊ฐ์ ์ ๋ฐ์ดํธํ์ง ์๋ ๊ฒ๊ณผ ๊ฐ์ด ์ต์ ํ๋ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ฝ๋๋ฅผ ๋จ์ํ๊ฒ ์ ์งํ๋ ๊ฒ์ด ์ต์ ์ ์ ํ์ผ ๊ฒ์ ๋๋ค. ์ต์ ํ๋ ๋ณด๋ค ์ ํํ ์ฌ์ฉ ์ฌ๋ก์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค.
์ด ์ฟผ๋ฆฌ๋ ์ ๋ณด ์คํค๋ง์์ ์ฝ๊ฒ ์์ฑํ ์๋ ์์ต๋๋ค. ์ด ์ ์(
k1 HASH, k2 ASC
), ์ด ๋ชฉ๋ก( k1, k2, v1, v2
), ํค ์ด( k1, k2
) ๋ฐ ์งํฉ ์ ( v1=excluded.v1, v2=excluded.v2
)์ด ํ์ํฉ๋๋ค.
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(๐๐ JSON ํ์ด๋ก๋์์ ์ ๋ฐ์ดํธ/์ฝ์ /์ผ์ ์ญ์ ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://dev.to/yugabyte/updateinsertsoft-delete-from-a-json-payload-4l0ํ ์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค