PostgreSQL์—์„œ EAV๋ฅผ JSONB๋กœ ๊ต์ฒดํ•˜๊ธฐ

์š”์•ฝ: JSONB๋Š” ์ฟผ๋ฆฌ ์„ฑ๋Šฅ์„ ์ €ํ•˜์‹œํ‚ค์ง€ ์•Š๊ณ ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๊ฐœ๋ฐœ์„ ํฌ๊ฒŒ ๋‹จ์ˆœํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์†Œ๊ฐœ

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

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ DB ํ…Œ์ด๋ธ”์— ๊ฐ ์†์„ฑ ๊ฐ’์— ๋Œ€ํ•œ ์—ด์„ ๋งŒ๋“ค๊ณ  ํŠน์ • ์—”ํ„ฐํ‹ฐ ์ธ์Šคํ„ด์Šค์— ํ•„์š”ํ•œ ๊ฐ’๋งŒ ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ข‹์Šต๋‹ˆ๋‹ค! ๋ฌธ์ œ๋Š” ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค... ํ…Œ์ด๋ธ”์— ์ˆ˜๋ฐฑ๋งŒ ๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ  ์ƒˆ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ๋•Œ๊นŒ์ง€๋Š” ๋ง์ด์ฃ .

EAV ํŒจํ„ด์„ ๊ณ ๋ คํ•ด ๋ณด์ž(์—”ํ‹ฐํ‹ฐ-์†์„ฑ-๊ฐ’), ์ด๋Š” ๋งค์šฐ ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ํ•œ ํ…Œ์ด๋ธ”์—๋Š” ์—”ํ„ฐํ‹ฐ(๋ ˆ์ฝ”๋“œ)๊ฐ€ ์žˆ๊ณ , ๋‹ค๋ฅธ ํ…Œ์ด๋ธ”์—๋Š” ์†์„ฑ ์ด๋ฆ„(์†์„ฑ)์ด ์žˆ์œผ๋ฉฐ, ์„ธ ๋ฒˆ์งธ ํ…Œ์ด๋ธ”์—๋Š” ์—”ํ„ฐํ‹ฐ์™€ ํ•ด๋‹น ์†์„ฑ์„ ์—ฐ๊ฒฐํ•˜๊ณ  ํ˜„์žฌ ์—”ํ„ฐํ‹ฐ์— ๋Œ€ํ•œ ํ•ด๋‹น ์†์„ฑ ๊ฐ’์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ๊ฐ์ฒด์— ๋Œ€ํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ์†์„ฑ ์ง‘ํ•ฉ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ "์ฆ‰์‹œ" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ EVA ๋ฐฉ์‹์— ๋‹จ์ ์ด ์—†๋‹ค๋ฉด ์ด ๊ธ€์„ ์“ฐ์ง€ ์•Š์•˜์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐ๊ฐ ํ•˜๋‚˜์˜ ์†์„ฑ์„ ๊ฐ€์ง„ ํ•˜๋‚˜ ์ด์ƒ์˜ ์—”ํ„ฐํ‹ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด ์ฟผ๋ฆฌ์— ๋‘ ๊ฐœ์˜ ์กฐ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋Š” ์†์„ฑ ํ…Œ์ด๋ธ”๊ณผ์˜ ์กฐ์ธ์ด๊ณ , ๋‘ ๋ฒˆ์งธ๋Š” ๊ฐ’ ํ…Œ์ด๋ธ”๊ณผ์˜ ์กฐ์ธ์ž…๋‹ˆ๋‹ค. ์—”ํ„ฐํ‹ฐ์— ์†์„ฑ์ด ๋‘ ๊ฐœ ์žˆ์œผ๋ฉด ์กฐ์ธ ๋„ค ๊ฐœ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค! ๊ฒŒ๋‹ค๊ฐ€ ๋ชจ๋“  ์†์„ฑ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ž์—ด๋กœ ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ์™€ WHERE ์กฐ๊ฑด ๋ชจ๋‘์— ํ˜•๋ณ€ํ™˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ๋ฅผ ๋งŽ์ด ์ž‘์„ฑํ•˜๋ฉด ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ์ธก๋ฉด์—์„œ ๋งค์šฐ ๋‚ญ๋น„์ ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ช…๋ฐฑํ•œ ๋‹จ์ ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  EAV๋Š” ์˜ค๋žซ๋™์•ˆ ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜์–ด ์™”์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ถˆ๊ฐ€ํ”ผํ•œ ๋‹จ์ ์ด์—ˆ๊ณ , ๋” ๋‚˜์€ ๋Œ€์•ˆ์€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ PostgreSQL์— ์ƒˆ๋กœ์šด "๊ธฐ์ˆ "์ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

PostgreSQL 9.4๋ถ€ํ„ฐ ๋ฐ”์ด๋„ˆ๋ฆฌ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด JSONB ๋ฐ์ดํ„ฐ ์œ ํ˜•์ด ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. JSONB ํ˜•์‹์œผ๋กœ JSON์„ ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ผ๋ฐ˜ ํ…์ŠคํŠธ JSON๋ณด๋‹ค ๊ณต๊ฐ„๊ณผ ์‹œ๊ฐ„์ด ์•ฝ๊ฐ„ ๋” ๋งŽ์ด ์†Œ์š”๋˜์ง€๋งŒ, ์ž‘์—… ์ˆ˜ํ–‰ ์†๋„๋Š” ํ›จ์”ฌ ๋น ๋ฆ…๋‹ˆ๋‹ค. JSONB๋Š” ์ธ๋ฑ์‹ฑ๋„ ์ง€์›ํ•˜์—ฌ ์ฟผ๋ฆฌ ์†๋„๊ฐ€ ๋”์šฑ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค.

JSONB ๋ฐ์ดํ„ฐ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ์—”ํ„ฐํ‹ฐ ํ…Œ์ด๋ธ”์— JSONB ์—ด ํ•˜๋‚˜๋งŒ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฒˆ๊ฑฐ๋กœ์šด EAV ํŒจํ„ด์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋ฅผ ํฌ๊ฒŒ ๊ฐ„์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์ด ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ดˆ๋ž˜ํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”๋กœ ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์ด ๊ธ€์„ ์ผ์Šต๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

์ด ๋น„๊ต๋ฅผ ์œ„ํ•ด $9.5 ๋นŒ๋“œ์—์„œ PostgreSQL 80๋ฅผ ์ƒˆ๋กœ ์„ค์น˜ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. DigitalOcean Ubuntu 14.04 postgresql.conf ํŒŒ์ผ์—์„œ ๋ช‡ ๊ฐ€์ง€ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•œ ํ›„ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด psql์„ ์‚ฌ์šฉํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ํ…Œ์ด๋ธ”์€ ๋ฐ์ดํ„ฐ๋ฅผ EAV๋กœ ํ‘œํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

CREATE TABLE entity ( 
  id           SERIAL PRIMARY KEY, 
  name         TEXT, 
  description  TEXT
);
CREATE TABLE entity_attribute (
  id          SERIAL PRIMARY KEY, 
  name        TEXT
);
CREATE TABLE entity_attribute_value (
  id                  SERIAL PRIMARY KEY, 
  entity_id           INT    REFERENCES entity(id), 
  entity_attribute_id INT    REFERENCES entity_attribute(id), 
  value               TEXT
);

์•„๋ž˜๋Š” ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜์ง€๋งŒ JSONB ์œ ํ˜• ์—ด์— ์†์„ฑ์ด ์žˆ๋Š” ํ…Œ์ด๋ธ”์ž…๋‹ˆ๋‹ค. ์†์„ฑ.

CREATE TABLE entity_jsonb (
  id          SERIAL PRIMARY KEY, 
  name        TEXT, 
  description TEXT,
  properties  JSONB
);

ํ›จ์”ฌ ๊ฐ„๋‹จํ•ด ๋ณด์ด์ง€ ์•Š๋‚˜์š”? ๊ทธ๋Ÿฐ ๋‹ค์Œ ์—”ํ„ฐํ‹ฐ ํ…Œ์ด๋ธ”์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค(์‹ค์žฌ & ์—”ํ‹ฐํ‹ฐ_jsonb) 10๋งŒ ๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์žˆ๊ณ , ์ด์— ๋”ฐ๋ผ EAV ํŒจํ„ด๊ณผ JSONB ์ปฌ๋Ÿผ์„ ์‚ฌ์šฉํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋กœ ํ…Œ์ด๋ธ”์ด ์ฑ„์›Œ์กŒ์Šต๋‹ˆ๋‹ค. entity_jsonb.์†์„ฑ. ๋”ฐ๋ผ์„œ ์ „์ฒด ์†์„ฑ ์ง‘ํ•ฉ์—์„œ ์—ฌ๋Ÿฌ ์œ ํ˜•์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ:

{
  id:          1
  name:        "Entity1"
  description: "Test entity no. 1"
  properties:  {
    color:        "red"
    lenght:       120
    width:        3.1882420
    hassomething: true
    country:      "Belgium"
  } 
}

์ด์ œ ๋‘ ์˜ต์…˜์— ๋Œ€ํ•ด ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๊ตฌํ˜„์„ ๋น„๊ตํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค!

๋””์ž์ธ ๋‹จ์ˆœํ™”

์•ž์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๊ฐ€ ํฌ๊ฒŒ ๊ฐ„์†Œํ™”๋˜์—ˆ๋‹ค๊ณ  ์–ธ๊ธ‰ํ–ˆ์Šต๋‹ˆ๋‹ค. EAV์— ์„ธ ๊ฐœ์˜ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹ , ์†์„ฑ์— JSONB ์—ด์„ ์‚ฌ์šฉํ•˜๋Š” ๋‹จ์ผ ํ…Œ์ด๋ธ”๋กœ ๊ฐ„์†Œํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๊ฐ„์†Œํ™”๋Š” ์ฟผ๋ฆฌ์— ์–ด๋–ป๊ฒŒ ๋ฐ˜์˜๋ ๊นŒ์š”? ์—”ํ„ฐํ‹ฐ์˜ ์†์„ฑ ํ•˜๋‚˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

-- EAV
UPDATE entity_attribute_value 
SET value = 'blue' 
WHERE entity_attribute_id = 1 
  AND entity_id = 120;

-- JSONB
UPDATE entity_jsonb 
SET properties = jsonb_set(properties, '{"color"}', '"blue"') 
WHERE id = 120;

๋ณด์‹œ๋‹ค์‹œํ”ผ ๋งˆ์ง€๋ง‰ ์ฟผ๋ฆฌ๋Š” ๋” ์ด์ƒ ๊ฐ„๋‹จํ•ด ๋ณด์ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. JSONB ๊ฐ์ฒด์˜ ์†์„ฑ ๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ๋‹ค์Œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. jsonb_set()์ƒˆ ๊ฐ’์„ JSONB ๊ฐ์ฒด๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹๋ณ„์ž๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. EAV ์˜ˆ์ œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด, ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด entity_id์™€ entity_attribute_id๋ฅผ ๋ชจ๋‘ ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. JSONB ์—ด์˜ ์†์„ฑ์„ ์—”ํ‹ฐํ‹ฐ ์ด๋ฆ„์„ ๊ธฐ์ค€์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ ค๋ฉด ์ด ๋ชจ๋“  ์ž‘์—…์ด ํ•œ ์ค„๋กœ ๊ฐ„๋‹จํ•˜๊ฒŒ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ ์ƒˆ๋กœ์šด ์ƒ‰์ƒ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐฉ๊ธˆ ์—…๋ฐ์ดํŠธํ•œ ์—”ํ„ฐํ‹ฐ๋ฅผ ์„ ํƒํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

-- EAV
SELECT e.name 
FROM entity e 
  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id
  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id
WHERE ea.name = 'color' AND eav.value = 'blue';

-- JSONB
SELECT name 
FROM entity_jsonb 
WHERE properties ->> 'color' = 'blue';

๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์ด ๋” ์งง๊ณ (์กฐ์ธ ์—†์Œ!), ๋”ฐ๋ผ์„œ ๋” ์ฝ๊ธฐ ์‰ฝ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ด ๋ถ€๋ถ„์—์„œ๋Š” JSONB๊ฐ€ ์Šน๋ฆฌํ•ฉ๋‹ˆ๋‹ค! JSON ->> ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JSONB ๊ฐ์ฒด์—์„œ ์ƒ‰์ƒ์„ ํ…์ŠคํŠธ ๊ฐ’์œผ๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. JSONB ๋ชจ๋ธ์—์„œ @> ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๋Š” ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

-- JSONB 
SELECT name 
FROM entity_jsonb 
WHERE properties @> '{"color": "blue"}';

์ด ๋ฐฉ๋ฒ•์€ ์ข€ ๋” ๋ณต์žกํ•ฉ๋‹ˆ๋‹ค. properties ์—ด์˜ JSON ๊ฐ์ฒด์— @> ์—ฐ์‚ฐ์ž ์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋…์„ฑ์€ ๋–จ์–ด์ง€์ง€๋งŒ ์„ฑ๋Šฅ์€ ๋” ์ข‹์Šต๋‹ˆ๋‹ค(์•„๋ž˜ ์ฐธ์กฐ).

์—ฌ๋Ÿฌ ์†์„ฑ์„ ํ•œ ๋ฒˆ์— ์„ ํƒํ•ด์•ผ ํ•  ๋•Œ JSONB๋ฅผ ๋”์šฑ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. JSONB ์ ‘๊ทผ ๋ฐฉ์‹์ด ์ง„์ •์œผ๋กœ ๋น›์„ ๋ฐœํ•˜๋Š” ๋ถ€๋ถ„์€ ๋ฐ”๋กœ ์ด ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์กฐ์ธ ์—†์ด ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์—์„œ ์†์„ฑ์„ ์ถ”๊ฐ€ ์—ด๋กœ ์„ ํƒํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

-- JSONB 
SELECT name
  , properties ->> 'color'
  , properties ->> 'country'
FROM entity_jsonb 
WHERE id = 120;

EAV๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌํ•˜๋ ค๋Š” ๊ฐ ์†์„ฑ์— ๋Œ€ํ•ด ๋‘ ๊ฐœ์˜ ์กฐ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ œ ์ƒ๊ฐ์—๋Š” ์œ„์˜ ์ฟผ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„๋ฅผ ํฌ๊ฒŒ ๋‹จ์ˆœํ™”ํ•œ ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. JSONB ์ฟผ๋ฆฌ ์ž‘์„ฑ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์šฐํŽธ.
์ด์ œ ์„ฑ๋Šฅ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ะŸั€ะพะธะทะฒะพะดะธั‚ะตะปัŒะฝะพัั‚ัŒ

๋‚ด๊ฐ€ ์‚ฌ์šฉํ•œ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•˜๋ ค๋ฉด ๋ถ„์„ ์„ค๋ช… ์ฟผ๋ฆฌ์—์„œ ์‹คํ–‰ ์‹œ๊ฐ„์„ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์ตœ์†Œ ์„ธ ๋ฒˆ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ํ”Œ๋ž˜๋„ˆ๊ฐ€ ์ฒ˜์Œ ์‹คํ–‰ํ•  ๋•Œ ์‹œ๊ฐ„์ด ๋” ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋จผ์ € ์ธ๋ฑ์Šค ์—†์ด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. EAV์— ํ•„์š”ํ•œ ์กฐ์ธ์—์„œ ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์—(์™ธ๋ž˜ ํ‚ค ํ•„๋“œ์— ์ธ๋ฑ์Šค๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—) JSONB์˜ ์žฅ์ ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ ํ›„ EAV ๊ฐ’ ํ…Œ์ด๋ธ”์˜ ๋‘ ์™ธ๋ž˜ ํ‚ค ์—ด์— ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ง„ JSONB ์—ด์— ๋Œ€ํ•œ.

๋ฐ์ดํ„ฐ ์ƒˆ๋กœ ๊ณ ์นจ ๊ฒฐ๊ณผ ์‹œ๊ฐ„(ms)์— ๋”ฐ๋ฅธ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹จ์œ„๋Š” ๋Œ€์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

PostgreSQL์—์„œ EAV๋ฅผ JSONB๋กœ ๊ต์ฒดํ•˜๊ธฐ

์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ JSONB๊ฐ€ ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ์ด์œ ๋กœ EAV๋ณด๋‹ค ํ›จ์”ฌ(>50000-x) ๋” ๋น ๋ฅธ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ‚ค ์—ด์„ ์ธ๋ฑ์‹ฑํ•˜๋ฉด ์ฐจ์ด๊ฐ€ ๊ฑฐ์˜ ์‚ฌ๋ผ์ง€์ง€๋งŒ, JSONB๋Š” ์—ฌ์ „ํžˆ EAV๋ณด๋‹ค 1,3๋ฐฐ ๋” ๋น ๋ฆ…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” JSONB ์—ด์˜ ์ธ๋ฑ์Šค๊ฐ€ ์•„๋ฌด๋Ÿฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋ฐ, ์ด๋Š” ํ‰๊ฐ€ ๊ธฐ์ค€์—์„œ ์†์„ฑ ์—ด์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์†์„ฑ ๊ฐ€์น˜์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ํƒํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค(์ •๊ทœ ์ฒ™๋„).

PostgreSQL์—์„œ EAV๋ฅผ JSONB๋กœ ๊ต์ฒดํ•˜๊ธฐ

JSONB๊ฐ€ ์ธ๋ฑ์Šค๊ฐ€ ์—†์„ ๋•Œ๋Š” EAV๋ณด๋‹ค ๋น ๋ฅด์ง€๋งŒ, EAV์— ์ธ๋ฑ์Šค๋ฅผ ์ ์šฉํ•˜๋ฉด JSONB๋ณด๋‹ค ์—ฌ์ „ํžˆ ๋น ๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ JSONB ์ฟผ๋ฆฌ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๋™์ผํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๊ณ , ์ด๋ฅผ ํ†ตํ•ด GIN ์ธ๋ฑ์Šค๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์†์„ฑ์ด ์ฑ„์›Œ์ง„ ์—ด์— GIN ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํฌํ•จ ์—ฐ์‚ฐ์ž @>๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ƒˆ ํ…Œ์ŠคํŠธ์—์„œ ์ด ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ–ˆ๋”๋‹ˆ ์‹คํ–‰ ์‹œ๊ฐ„์— ์—„์ฒญ๋‚œ ์ฐจ์ด๊ฐ€ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋‹จ 0,153ms! ์ด๋Š” EAV๋ณด๋‹ค 15000๋ฐฐ, ->> ์—ฐ์‚ฐ์ž๋ณด๋‹ค 25000๋ฐฐ ๋น ๋ฅธ ์†๋„์ž…๋‹ˆ๋‹ค.

๊ฝค ๋น ๋ฅธ ๊ฒƒ ๊ฐ™์•„์š”!

DB ํ…Œ์ด๋ธ” ํฌ๊ธฐ

๋‘ ์ ‘๊ทผ ๋ฐฉ์‹์˜ ํ…Œ์ด๋ธ” ํฌ๊ธฐ๋ฅผ ๋น„๊ตํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. psql์—์„œ๋Š” ๋‹ค์Œ ๋ช…๋ น์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ํ…Œ์ด๋ธ”๊ณผ ์ธ๋ฑ์Šค์˜ ํฌ๊ธฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋””ํ‹ฐ+

PostgreSQL์—์„œ EAV๋ฅผ JSONB๋กœ ๊ต์ฒดํ•˜๊ธฐ

EAV ๋ฐฉ์‹์˜ ๊ฒฝ์šฐ ํ…Œ์ด๋ธ” ํฌ๊ธฐ๋Š” ์•ฝ 3068MB์ด๊ณ  ์ธ๋ฑ์Šค๋Š” ์ตœ๋Œ€ 3427MB๋กœ ์ด 6,43GB์ž…๋‹ˆ๋‹ค. JSONB ๋ฐฉ์‹์€ ํ…Œ์ด๋ธ”์— 1817MB, ์ธ๋ฑ์Šค์— 318MB๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด๋Š” 2,08GB์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” 3๋ฐฐ๋‚˜ ์ ์€ ์šฉ๋Ÿ‰์ž…๋‹ˆ๋‹ค! ๊ฐ JSONB ๊ฐ์ฒด์— ์†์„ฑ ์ด๋ฆ„์„ ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ์‚ฌ์‹ค์€ ๋‹ค์†Œ ๋†€๋ž์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ˆซ์ž๋งŒ ๋ด๋„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. EAV์—์„œ๋Š” ์†์„ฑ ๊ฐ’๋‹น 2๊ฐœ์˜ ์ •์ˆ˜ ์™ธ๋ž˜ ํ‚ค๋ฅผ ์ €์žฅํ•˜์—ฌ 8๋ฐ”์ดํŠธ์˜ ์ถ”๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ EAV์—์„œ๋Š” ๋ชจ๋“  ์†์„ฑ ๊ฐ’์ด ํ…์ŠคํŠธ๋กœ ์ €์žฅ๋˜๋Š” ๋ฐ˜๋ฉด, JSONB๋Š” ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ ๋‚ด๋ถ€์ ์œผ๋กœ ์ˆซ์ž ๋ฐ ๋ถ€์šธ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ๋” ์ž‘์€ ๊ณต๊ฐ„์„ ์ฐจ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๊ณผ

์ „๋ฐ˜์ ์œผ๋กœ JSONB์— ์—”ํ‹ฐํ‹ฐ ์†์„ฑ์„ ์ €์žฅํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ๋ฐ ์œ ์ง€ ๊ด€๋ฆฌ๊ฐ€ ํ›จ์”ฌ ์ˆ˜์›”ํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ๋ฅผ ๋งŽ์ด ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ, ๋ชจ๋“  ํ•ญ๋ชฉ์„ ์—”ํ‹ฐํ‹ฐ์™€ ๋™์ผํ•œ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๋ฉด ์‹ค์ œ๋กœ ๋” ํšจ์œจ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์ƒํ˜ธ์ž‘์šฉ์ด ๊ฐ„์†Œํ™”๋œ๋‹ค๋Š” ์ ๋„ ์žฅ์ ์ด์ง€๋งŒ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํฌ๊ธฐ๊ฐ€ 3๋ฐฐ ๋” ์ž‘์•„์ง‘๋‹ˆ๋‹ค.

๋˜ํ•œ, ์ €ํฌ๊ฐ€ ์ˆ˜ํ–‰ํ•œ ๋ฒค์น˜๋งˆํฌ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด ์„ฑ๋Šฅ โ€‹โ€‹์ €ํ•˜๊ฐ€ ๋งค์šฐ ๋ฏธ๋ฏธํ•˜๋‹ค๋Š” ๊ฒฐ๋ก ์„ ๋‚ด๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ JSONB๊ฐ€ EAV๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๋”์šฑ ์šฐ์ˆ˜ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด ๋ฒค์น˜๋งˆํฌ๊ฐ€ ๋ชจ๋“  ์ธก๋ฉด(์˜ˆ: ๋งค์šฐ ๋งŽ์€ ์†์„ฑ์„ ๊ฐ€์ง„ ์—”ํ‹ฐํ‹ฐ, ๊ธฐ์กด ๋ฐ์ดํ„ฐ์˜ ์†์„ฑ ์ˆ˜ ๋Œ€ํญ ์ฆ๊ฐ€ ๋“ฑ)์„ ํฌ๊ด„ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐœ์„  ๋ฐฉ์•ˆ์— ๋Œ€ํ•œ ์ œ์•ˆ์ด ์žˆ์œผ์‹œ๋ฉด ์–ธ์ œ๋“ ์ง€ ๋Œ“๊ธ€๋กœ ๋‚จ๊ฒจ์ฃผ์„ธ์š”!

์ถœ์ฒ˜ : habr.com

DDoS ๋ณดํ˜ธ, VPS VDS ์„œ๋ฒ„๊ฐ€ ์žˆ๋Š” ์‚ฌ์ดํŠธ๋ฅผ ์œ„ํ•œ ์•ˆ์ •์ ์ธ ํ˜ธ์ŠคํŒ… ๊ตฌ์ž… ๐Ÿ”ฅ DDoS ๊ณต๊ฒฉ ๋ฐฉ์ง€ ๊ธฐ๋Šฅ์ด ํƒ‘์žฌ๋œ ์•ˆ์ •์ ์ธ ์›น์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ…, VPS ๋ฐ VDS ์„œ๋ฒ„๋ฅผ ๊ตฌ๋งคํ•˜์„ธ์š” | ProHoster