PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

๋ฐ˜๋…„ ์ „ ์šฐ๋ฆฌ๋Š” ์ œ์‹œ explain.tensor.ru - ๊ณต๊ณต์˜ ์ฟผ๋ฆฌ ๊ณ„ํš ๊ตฌ๋ฌธ ๋ถ„์„ ๋ฐ ์‹œ๊ฐํ™” ์„œ๋น„์Šค PostgreSQL์—.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์ง€๋‚œ ๋ช‡ ๋‹ฌ ๋™์•ˆ ์šฐ๋ฆฌ๋Š” ๊ทธ์— ๋Œ€ํ•ด PGConf.Russia 2020์—์„œ ๋ณด๊ณ , ์š”์•ฝ ์ค€๋น„ SQL ์ฟผ๋ฆฌ ์†๋„ ํ–ฅ์ƒ์— ๋Œ€ํ•œ ๋ฌธ์„œ ์ œ๊ณต๋˜๋Š” ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜์ง€๋งŒ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ๊ท€ํ•˜์˜ ํ”ผ๋“œ๋ฐฑ์„ ์ˆ˜์ง‘ํ•˜๊ณ  ์‹ค์ œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ดค๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์ œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ์•Œ๋ ค๋“œ๋ฆด ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์–‘ํ•œ ๊ณ„ํš ํ˜•์‹ ์ง€์›

์š”์ฒญ๊ณผ ํ•จ๊ป˜ ๋กœ๊ทธ์—์„œ ๊ณ„ํš

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

        Query Text: INSERT INTO  dicquery_20200604  VALUES ($1.*) ON CONFLICT (query)
                           DO NOTHING;
        Insert on dicquery_20200604  (cost=0.00..0.05 rows=1 width=52) (actual time=40.376..40.376 rows=0 loops=1)
          Conflict Resolution: NOTHING
          Conflict Arbiter Indexes: dicquery_20200604_pkey
          Tuples Inserted: 1
          Conflicting Tuples: 0
          Buffers: shared hit=9 read=1 dirtied=1
          ->  Result  (cost=0.00..0.05 rows=1 width=52) (actual time=0.001..0.001 rows=1 loops=1)

... ์•„๋ฌด๊ฒƒ๋„ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ณ  ๊ณ„ํš์˜ ํ•„๋“œ์— ์ง์ ‘ ๋ณต์‚ฌํ•œ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋˜์ง‘๋‹ˆ๋‹ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์ถœ๋ ฅ์—์„œ ๋ถ„ํ•ด ๊ณ„ํš์— ๋Œ€ํ•œ ๋ณด๋„ˆ์Šค๋„ ์–ป์Šต๋‹ˆ๋‹ค. ์ปจํ…์ŠคํŠธ ํƒญ, ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ์˜ ์š”์ฒญ์€ ๋ชจ๋“  ์˜๊ด‘์œผ๋กœ ์ œ์‹œ๋ฉ๋‹ˆ๋‹ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

JSON ๋ฐ YAML

EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT * FROM pg_class;

"[
  {
    "Plan": {
      "Node Type": "Seq Scan",
      "Parallel Aware": false,
      "Relation Name": "pg_class",
      "Alias": "pg_class",
      "Startup Cost": 0.00,
      "Total Cost": 1336.20,
      "Plan Rows": 13804,
      "Plan Width": 539,
      "Actual Startup Time": 0.006,
      "Actual Total Time": 1.838,
      "Actual Rows": 10266,
      "Actual Loops": 1,
      "Shared Hit Blocks": 646,
      "Shared Read Blocks": 0,
      "Shared Dirtied Blocks": 0,
      "Shared Written Blocks": 0,
      "Local Hit Blocks": 0,
      "Local Read Blocks": 0,
      "Local Dirtied Blocks": 0,
      "Local Written Blocks": 0,
      "Temp Read Blocks": 0,
      "Temp Written Blocks": 0
    },
    "Planning Time": 5.135,
    "Triggers": [
    ],
    "Execution Time": 2.389
  }
]"

์™ธ๋ถ€ ๋”ฐ์˜ดํ‘œ๊ฐ€ ์žˆ๋”๋ผ๋„ pgAdmin์ด ๋ณต์‚ฌํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋™์ผํ•œ ํ•„๋“œ์— ๋„ฃ์ง€ ์•Š์•„๋„ ์ถœ๋ ฅ์€ ์•„๋ฆ„๋‹ต์Šต๋‹ˆ๋‹ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

๊ณ ๊ธ‰ ์‹œ๊ฐํ™”

๊ณ„ํš์‹œ๊ฐ„ / ์‹คํ–‰์‹œ๊ฐ„

์ด์ œ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ถ”๊ฐ€ ์‹œ๊ฐ„์ด ์†Œ์š”๋œ ์œ„์น˜๋ฅผ ๋” ์ž˜ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์ž…์ถœ๋ ฅ ํƒ€์ด๋ฐ

๋•Œ๋•Œ๋กœ ๋ฆฌ์†Œ์Šค ์ธก๋ฉด์—์„œ ๋„ˆ๋ฌด ๋งŽ์ด ์ฝ๊ณ  ์“ฐ์ง€ ์•Š์€ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์–ด๋–ค ์ด์œ ๋กœ ๋ถ€์ ์ ˆํ•˜๊ฒŒ ํฐ ๊ฒƒ ๊ฐ™์€ ์ƒํ™ฉ์— ๋Œ€์ฒ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.์•„, ์•„๋งˆ๋„ ๊ทธ ์ˆœ๊ฐ„ ์„œ๋ฒ„์˜ ๋””์Šคํฌ๊ฐ€ ๋„ˆ๋ฌด ๊ณผ๋ถ€ํ•˜๋˜์–ด์„œ ์ฝ๋Š” ๋ฐ ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ฑธ๋ ธ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค!"ํ•˜์ง€๋งŒ ์–ด์ฉ์ง€ ๊ทธ๋‹ค์ง€ ์ •ํ™•ํ•˜์ง€ ์•Š์•„...

๊ทธ๋Ÿฌ๋‚˜ ์ ˆ๋Œ€์ ์œผ๋กœ ์•ˆ์ •์ ์œผ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค PG ์„œ๋ฒ„์˜ ๊ตฌ์„ฑ ์˜ต์…˜ ์ค‘์—๋Š” ๋‹ค์Œ์ด ์žˆ์Šต๋‹ˆ๋‹ค. track_io_timing:

์‹œ๊ฐ„ ์ง€์ • I/O ์ž‘์—…์„ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ ์šด์˜ ์ฒด์ œ๊ฐ€ ํ˜„์žฌ ์‹œ๊ฐ„์„ ์ง€์†์ ์œผ๋กœ ์ฟผ๋ฆฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ผ๋ถ€ ํ”Œ๋žซํผ์—์„œ ์†๋„๊ฐ€ ํฌ๊ฒŒ ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. pg_test_timing ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋žซํผ์—์„œ ํƒ€์ด๋ฐ์˜ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์ถ”์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. I/O ํ†ต๊ณ„๋Š” pg_stat_database ๋ทฐ๋ฅผ ํ†ตํ•ด ์–ป์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, EXPLAIN ์ถœ๋ ฅ์—์„œ(BUFFERS ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์‚ฌ์šฉ๋œ ๊ฒฝ์šฐ) ๊ทธ๋ฆฌ๊ณ  pg_stat_statements ๋ณด๊ธฐ๋ฅผ ํ†ตํ•ด.

์ด ์˜ต์…˜์€ ๋กœ์ปฌ ์„ธ์…˜ ๋‚ด์—์„œ๋„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SET track_io_timing = TRUE;

์ด์ œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ์‹คํ–‰ ํŠธ๋ฆฌ์˜ ๋ชจ๋“  ๋ณ€ํ™˜์„ ๊ณ ๋ คํ•˜์—ฌ ์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ดํ•ดํ•˜๊ณ  ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์—ฌ๊ธฐ์—์„œ ์ด ์‹คํ–‰ ์‹œ๊ฐ„์˜ 0.790ms ์ค‘ 0.718ms๋Š” ํ•œ ํŽ˜์ด์ง€์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š” ๋ฐ, 0.044ms๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ๋ฐ, ๊ทธ๋ฆฌ๊ณ  0.028ms๋งŒ์ด ๋‹ค๋ฅธ ๋ชจ๋“  ์œ ์šฉํ•œ ํ™œ๋™์— ์‚ฌ์šฉ๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

PostgreSQL 13์˜ ๋ฏธ๋ž˜

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ „์ฒด ๊ฐœ์š”๋Š” ๋‹ค์Œ์„ ์ฐธ์กฐํ•˜์„ธ์š”. ์ž์„ธํ•œ ๊ธฐ์‚ฌ์—์„œ, ํŠนํžˆ ๊ณ„ํš ๋ณ€๊ฒฝ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ณ„ํš ๋ฒ„ํผ

์Šค์ผ€์ค„๋Ÿฌ์— ํ• ๋‹น๋œ ์ž์›์— ๋Œ€ํ•œ ๊ณ„์‚ฐ์€ pg_stat_statements์™€ ๊ด€๋ จ๋˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ํŒจ์น˜์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. BUFFERS ์˜ต์…˜์ด ์žˆ๋Š” EXPLAIN์€ ๊ณ„ํš ๋‹จ๊ณ„์—์„œ ์‚ฌ์šฉ๋œ ๋ฒ„ํผ ์ˆ˜๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

 Seq Scan on pg_class (actual rows=386 loops=1)
   Buffers: shared hit=9 read=4
 Planning Time: 0.782 ms
   Buffers: shared hit=103 read=11
 Execution Time: 0.219 ms

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์ฆ๋ถ„ ์ •๋ ฌ

๋งŽ์€ ํ‚ค(k1, k2, k3โ€ฆ)๋กœ ์ •๋ ฌํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ํ”Œ๋ž˜๋„ˆ๋Š” ์ด์ œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ด๋ฏธ ์—ฌ๋Ÿฌ ์ฒซ ๋ฒˆ์งธ ํ‚ค(์˜ˆ: k1 ๋ฐ k2)๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กœ ์žฌ์ •๋ ฌํ•  ์ˆ˜๋Š” ์—†๊ณ  k1๊ณผ k2์˜ ๊ฐ™์€ ๊ฐ’์œผ๋กœ ์—ฐ์†์ ์ธ ๊ทธ๋ฃน์œผ๋กœ ๋‚˜๋ˆ„๊ณ  k3 ํ‚ค๋กœ "์žฌ์ •๋ ฌ"ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ „์ฒด ์ •๋ ฌ์€ ๋” ์ž‘์€ ํฌ๊ธฐ์˜ ์—ฌ๋Ÿฌ ์—ฐ์† ์ •๋ ฌ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์–‘์ด ์ค„์–ด๋“ค๊ณ  ๋ชจ๋“  ์ •๋ ฌ์ด ์™„๋ฃŒ๋˜๊ธฐ ์ „์— ์ฒซ ๋ฒˆ์งธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

 Incremental Sort (actual rows=2949857 loops=1)
   Sort Key: ticket_no, passenger_id
   Presorted Key: ticket_no
   Full-sort Groups: 92184 Sort Method: quicksort Memory: avg=31kB peak=31kB
   ->  Index Scan using tickets_pkey on tickets (actual rows=2949857 loops=1)
 Planning Time: 2.137 ms
 Execution Time: 2230.019 ms

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด
PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

UI/UX ๊ฐœ์„ 

์Šคํฌ๋ฆฐ์ƒท์€ ์–ด๋””์—๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค!

์ด์ œ ๊ฐ ํƒญ์—์„œ ํƒญ์˜ ์Šคํฌ๋ฆฐ์ƒท์„ ํด๋ฆฝ๋ณด๋“œ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ ํƒญ์˜ ์ „์ฒด ๋„ˆ๋น„์™€ ๊นŠ์ด - "์‹œ๋ ฅ" ์˜ค๋ฅธ์ชฝ ์ƒ๋‹จ:

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์‹ค์ œ๋กœ ์ด ์ถœํŒ๋ฌผ์— ์‹ค๋ฆฐ ๋Œ€๋ถ€๋ถ„์˜ ์‚ฌ์ง„์€ ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์ž…์ˆ˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋…ธ๋“œ์— ๋Œ€ํ•œ ๊ถŒ์žฅ ์‚ฌํ•ญ

๋” ๋งŽ์€ ๊ฒƒ์ด ์žˆ์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ๊ฐ์— ๋Œ€ํ•ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธ€์„ ์ž์„ธํžˆ ์ฝ์–ด๋ณด์„ธ์š”๋งํฌ๋ฅผ ๋”ฐ๋ผ:

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

์•„์นด์ด๋ธŒ์—์„œ ์ œ๊ฑฐ

์–ด๋–ค ์‚ฌ๋žŒ๋“ค์€ ๋Šฅ๋ ฅ์„ ์š”๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค. "์ ˆ๋Œ€" ์‚ญ์ œ ์•„์นด์ด๋ธŒ์— ๊ฒŒ์‹œ๋˜์ง€ ์•Š์€ ๊ณ„ํš๋„ ํ•ด๋‹น ์•„์ด์ฝ˜์„ ํด๋ฆญํ•˜์‹ญ์‹œ์˜ค.

PostgreSQL ์ฟผ๋ฆฌ ๊ณ„ํš์„ ํ›จ์”ฌ ๋” ํŽธ๋ฆฌํ•˜๊ฒŒ ์ดํ•ด

๊ธ€์Ž„, ์šฐ๋ฆฌ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋ง์ž ์ง€์› ๊ทธ๋ฃน์˜๊ฒฌ๊ณผ ์ œ์•ˆ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€