์คํ๊ณํ(EXPLAIN) ์ฝ๋ ๋ฒ — MySQL·PostgreSQL·Oracle·SQL Server ํ ๋ฒ์. ์ถ๊ฐํ ์ธ๋ฑ์ค๊ฐ ์ค์ ๋ก ํ๋์ง ํ์ธํ๋ ค๋ฉด ์คํ๊ณํ์ ๋ด์ผ ํ๋ค. DB๋ง๋ค ๋ช ๋ น์ด๋ ์ถ๋ ฅ๋ ๋ค๋ฅด์ง๋ง, ๊ฒฐ๊ตญ ๋ณด๋ ๊ฒ์ ๋๊ฐ๋ค. ๋ค ๊ฐ์ง ์ฃผ์ DB์ ์คํ๊ณํ ํ์ธ๋ฒ์, ์ค์ ์ถ๋ ฅ๊ณผ ํจ๊ป ์ ๋ฆฌํ๋ค.
๋ค์ด๊ฐ๋ฉฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ธฐ ์ ์ ๋ณด๋ ๊ฒ๋ค ๊ธ์์ ์ธ๋ฑ์ค๊ฐ ์ ๋น ๋ฅธ์ง, ์ธ์ ์ถ๊ฐํด์ผ ํ๋์ง๋ฅผ ๋ค๋ค๋ค. ๊ทธ๋ฐ๋ฐ ํ ๊ฐ์ง ์ง๋ฌธ์ด ๋จ๋๋ค. ์ถ๊ฐํ ์ธ๋ฑ์ค๊ฐ ์ค์ ๋ก ์ฐ์ด๊ณ ์์๊น?
์ธ๋ฑ์ค๋ฅผ ๋ง๋ค์ด ๋๊ณ ๋ ์ฟผ๋ฆฌ๊ฐ ์ฌ์ ํ ๋๋ฆฐ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. ์ธ๋ฑ์ค๊ฐ ์๋๋ฐ ์ตํฐ๋ง์ด์ ๊ฐ ์ ์ฐ๊ธฐ๋ก ํ๊ฑฐ๋, ์ธ ์ ์๊ฒ ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋๊ฑฐ๋, ํต๊ณ๊ฐ ์ด๊ธ๋ ์ตํฐ๋ง์ด์ ๊ฐ ํ์ค์บ์ด ๋ ์ธ๋ค๊ณ ์คํํ ๊ฒฝ์ฐ๋ค. ์ด๊ฑธ ๋์ผ๋ก ํ์ธํ๋ ๋๊ตฌ๊ฐ ์คํ๊ณํ(execution plan)์ด๋ค.
์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๋ ๊ฑด ์์์ผ ๋ฟ์ด๋ค. ์ตํฐ๋ง์ด์ ๊ฐ ๊ทธ ์ธ๋ฑ์ค๋ฅผ ์ค์ ๋ก ๊ณจ๋๋์ง๊น์ง ์คํ๊ณํ์ผ๋ก ํ์ธํด์ผ ๋น๋ก์ ๊ฒ์ฆ์ด ๋๋๋ค.
๋ฌธ์ ๋ DB๋ง๋ค ์คํ๊ณํ์ ๋ณด๋ ๋ฐฉ๋ฒ์ด ์ ๊ฐ๊ฐ์ด๋ผ๋ ์ ์ด๋ค. MySQL์ EXPLAIN, PostgreSQL์ EXPLAIN ANALYZE, Oracle์ DBMS_XPLAN, SQL Server์ ์คํ ๊ณํ์ ๋ช
๋ น์ด๋ ์ถ๋ ฅ ํ์๋ ๋ค๋ฅด๋ค. ๊ทธ๋์ ํ DB์ ์ต์ํด๋ ๋ค๋ฅธ DB๋ก ๋์ด๊ฐ๋ฉด ๋ค์ ๋งํ๋ค.
์ด ๊ธ์์๋ ๋ค ๊ฐ์ง ์ฃผ์ DB(MySQL/MariaDB, PostgreSQL, Oracle, SQL Server)์์ ์คํ๊ณํ์ ์ด๋ป๊ฒ ๋ฝ๊ณ ์ด๋ป๊ฒ ์ฝ๋์ง๋ฅผ ์ค์ ์ถ๋ ฅ๊ณผ ํจ๊ป ์ ๋ฆฌํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ ๋ชจ๋ DB์ ๊ณตํต์ผ๋ก ์ ์ฉ๋๋ "์ธ๋ฑ์ค๊ฐ ์ ํ๋ ์ด์ "๋ฅผ ์ง๋๋ค.
1. ์คํ๊ณํ์ด๋ — ์ตํฐ๋ง์ด์ ์ ์์ ์ง์์
SQL์ "๋ฌด์์ ์ํ๋์ง"๋ง ์ ๋ ์ ์ธํ ์ธ์ด๋ค. "์ด๋ป๊ฒ ๊ฐ์ ธ์ฌ์ง"๋ ์ตํฐ๋ง์ด์ (optimizer)๊ฐ ์ ํ๋ค. ๊ฐ์ SELECT๋ผ๋ ํ
์ด๋ธ์ ํ์ค์บํ ์ง, ์ธ๋ฑ์ค๋ฅผ ํ์ง, ์ด๋ค ํ
์ด๋ธ๋ถํฐ ์กฐ์ธํ ์ง ์๋ง์ ๊ฒฝ๋ก๊ฐ ์๊ณ , ์ตํฐ๋ง์ด์ ๋ ๊ทธ์ค ๋น์ฉ(cost)์ด ๊ฐ์ฅ ๋ฎ๋ค๊ณ ์ถ์ ํ ๊ฒฝ๋ก๋ฅผ ๊ณ ๋ฅธ๋ค. ์ด๋ ๊ฒ ์ ํ๋ ๊ฒฝ๋ก๊ฐ ๋ฐ๋ก ์คํ๊ณํ์ด๋ค.
์ฌ๊ธฐ์ ๋ฐ๋์ ๊ตฌ๋ถํด์ผ ํ ๋ ๊ฐ์ง๊ฐ ์๋ค.
- ์์(estimated) ๊ณํ: ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ง ์๊ณ , ์ตํฐ๋ง์ด์ ๊ฐ "์ด๋ ๊ฒ ๋๋ฆด ๊ฒ ๊ฐ๋ค"๊ณ ๋ด๋๋ ๊ณํ. ๋น ๋ฅด๊ณ ์์ ํ์ง๋ง ์ด๋๊น์ง๋ ์ถ์ ์ด๋ค.
- ์ค์ (actual) ๊ณํ: ์ฟผ๋ฆฌ๋ฅผ ์ค์ ๋ก ๋๋ฆฐ ๋ค, ๊ฐ ๋จ๊ณ๊ฐ ํ์ ๋ช ๊ฐ ์ฒ๋ฆฌํ๊ณ ์๊ฐ์ด ์ผ๋ง ๊ฑธ๋ ธ๋์ง ์ธก์ ํ ๊ณํ. ์ ํํ์ง๋ง ์ฟผ๋ฆฌ๋ฅผ ์คํํด์ผ ํ๋ค.
ํ๋์ ํต์ฌ์ ์ด ๋์ ์ฐจ์ด๋ค. ์ตํฐ๋ง์ด์ ๊ฐ "์ด ๋จ๊ณ์์ 10ํ ๋์ฌ ๊ฒ"์ด๋ผ ์์ํ๋๋ฐ ์ค์ ๋ก 100๋ง ํ์ด ๋์๋ค๋ฉด, ํต๊ณ๊ฐ ํ๋ ธ๊ฑฐ๋ ์ ํ๋ ํ๋จ์ด ๋น๋๊ฐ ๊ฒ์ด๊ณ , ๊ทธ ์ง์ ์ด ๋๊ฐ ์ฑ๋ฅ ๋ฌธ์ ์ ์ง์์ง๋ค. ํฅ๋ฏธ๋ก์ด ๊ฑด, DB๋ง๋ค ๋ถ๋ฅด๋ ์ด๋ฆ๋ง ๋ค๋ฅผ ๋ฟ ์ ๋ถ ์ด "์์ vs ์ค์ "๋ฅผ ๋ณด์ฌ์ค๋ค๋ ์ ์ด๋ค.

2. ์ด๋ DB๋ ๋๊ฐ์ด ๋ด์ผ ํ 3๊ฐ์ง
๋ช ๋ น์ด๊ฐ ๋ฌ๋ผ๋, ์คํ๊ณํ์์ ์ฝ์ด์ผ ํ ํต์ฌ์ ์ด๋ DB๋ ๊ฐ๋ค.
- ์ค์บ ๋ฐฉ์ — ํ ์ด๋ธ์ ์ด๋ป๊ฒ ์ฝ๋๊ฐ. ์ธ๋ฑ์ค๋ก ํ์ํ ํ๋ง ์ฐพ์๊ฐ๋๊ฐ(seek / index scan), ์๋๋ฉด ์ ์ฒด๋ฅผ ํ๋๊ฐ(full scan / seq scan). ํฐ ํ ์ด๋ธ์์ ํ์ค์บ์ด ๋ณด์ด๋ฉด ์ผ๋จ ์์ฌํ๋ค.
- ์กฐ์ธ ๋ฐฉ์ — ์ฌ๋ฌ ํ ์ด๋ธ์ ์ด๋ป๊ฒ ํฉ์น๋๊ฐ. Nested Loop(์์ ์งํฉ์ ์ ๋ฆฌ), Hash Join(ํฐ ์งํฉ·๋น์ ๋ ฌ์ ์ ๋ฆฌ), Merge Join(์ ๋ ฌ๋ ์ ๋ ฅ์ ์ ๋ฆฌ). ๋ฐ์ดํฐ ํฌ๊ธฐ์ ์ ๋ง๋ ์กฐ์ธ์ด ์ ํ๋๋ฉด ๋๋ ค์ง๋ค.
- ์์ vs ์ค์ ํ ์ — ์ตํฐ๋ง์ด์ ์ ์ถ์ ์ด ํ์ค๊ณผ ์ผ๋ง๋ ๋ง๋๊ฐ. ์ฐจ์ด๊ฐ ํฌ๋ฉด ํต๊ณ๋ถํฐ ์์ฌํ๋ค.
์คํ๊ณํ์ ๋๋ถ๋ถ ํธ๋ฆฌ ๊ตฌ์กฐ๋ค. ๊ฐ์ฅ ๊น์ด ๋ค์ฌ ์ด(๋๋ ๊ฐ์ฅ ์์ชฝ) ๋ ธ๋๊ฐ ๋จผ์ ์คํ๋๊ณ , ๊ทธ ๊ฒฐ๊ณผ๊ฐ ์๋ก ํ๋ฌ ์กฐ์ธ์ ๊ฑฐ์ณ ์ต์ข ๊ฒฐ๊ณผ๊ฐ ๋๋ค. ์ฝ์ ๋๋ ์์ชฝ(์๋)๋ถํฐ ๋ฐ๊นฅ(์)์ผ๋ก ๋ฐ๋ผ๊ฐ๋ฉด ๋๋ค.

์ด ์ธ ๊ฐ์ง๋ฅผ ๊ธฐ์ค์ผ๋ก, ์ด์ DB๋ณ๋ก ์ด๋ป๊ฒ ๋ฝ๊ณ ์ฝ๋์ง ๋ณธ๋ค. ๋ชจ๋ ์์ ๋ orders ํ
์ด๋ธ์ customer_id ์ธ๋ฑ์ค(idx_cust)๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๋ค.
3. MySQL / MariaDB
๊ฐ์ฅ ํท๊ฐ๋ฆฌ๋ ๋ถ๋ถ๋ถํฐ ์ง์. ์์ ๊ณํ์ ๋ ๋ค EXPLAIN์ผ๋ก ๊ฐ์ง๋ง, ์ค์ ๊ณํ์ ๋ณด๋ ๋ช
๋ น์ MySQL๊ณผ MariaDB๊ฐ ๋ค๋ฅด๋ค.
์์ ๊ณํ — EXPLAIN (๊ณตํต)
EXPLAIN
SELECT * FROM orders WHERE customer_id = 12345;
์ ํต์ ์ธ ํ ํํ๋ก ๋์จ๋ค.
+----+-------------+--------+------+---------------+----------+---------+-------+------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------+---------------+----------+---------+-------+------+----------+-------+
| 1 | SIMPLE | orders | ref | idx_cust | idx_cust | 4 | const | 4 | 100.00 | NULL |
+----+-------------+--------+------+---------------+----------+---------+-------+------+----------+-------+
๋ด์ผ ํ ํต์ฌ ์ปฌ๋ผ์ ๋ค์๊ณผ ๊ฐ๋ค.
- type — ์ ๊ทผ ๋ฐฉ์. ์ข์ ์์๋ก
const>eq_ref>ref>range>index>ALL. ๋งจ ๋์ALL์ด ํ ํ ์ด๋ธ ์ค์บ์ผ๋ก, ํฐ ํ ์ด๋ธ์์ ๋ณด์ด๋ฉด ์ํ ์ ํธ๋ค. ์ ์์๋ref(์ธ๋ฑ์ค๋ก ์ฐพ์)๋ผ ์ํธํ๋ค. - key — ์ค์ ๋ก ์ฌ์ฉ๋ ์ธ๋ฑ์ค.
NULL์ด๋ฉด ์ธ๋ฑ์ค๋ฅผ ์ ํ ์ ํ๋ค๋ ๋ป์ด๋ค. - rows — ์ด ๋จ๊ณ์์ ์ฝ์ ๊ฒ์ผ๋ก ์์ํ ํ ์.
- Extra —
Using index(์ปค๋ฒ๋ง ์ธ๋ฑ์ค, ์ข์),Using where,Using filesort·Using temporary(์ ๋ ฌ·์์ ํ ์ด๋ธ, ๋ฌด๊ฑฐ์)๋ฅผ ์ฃผ์ํด์ ๋ณธ๋ค.
๋ง์ฝ ์ธ๋ฑ์ค๊ฐ ์์๋ค๋ฉด ๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ์ด๋ ๊ฒ ๋์จ๋ค. type=ALL, key=NULL, rows๊ฐ ํญ์ฆํ๋ ์ ํ์ ์ธ "์ธ๋ฑ์ค ์ ํ" ์ ํธ๋ค.
+----+-------------+--------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | orders | ALL | NULL | NULL | NULL | NULL | 1048576 | 10.00 | Using where |
+----+-------------+--------+------+---------------+------+---------+------+---------+----------+-------------+
์ค์ ๊ณํ — ์ฌ๊ธฐ์ ๊ฐ๋ฆฐ๋ค
MySQL 8.0.18 ์ด์์ EXPLAIN ANALYZE๋ฅผ ์ด๋ค. ์ฟผ๋ฆฌ๋ฅผ ์ค์ ๋ก ๋๋ฆฌ๊ณ , ํธ๋ฆฌ ํํ๋ก ์์๊ณผ ์ค์ ๋ฅผ ๊ฐ์ด ๋ณด์ฌ์ค๋ค.
EXPLAIN ANALYZE
SELECT * FROM orders WHERE customer_id = 12345\G
-> Index lookup on orders using idx_cust (customer_id=12345)
(cost=1.65 rows=4) (actual time=0.028..0.041 rows=4 loops=1)
(cost=... rows=...)๊ฐ ์์, (actual time=... rows=... loops=...)๊ฐ ์ค์ ๋ค. ์ฌ๊ธฐ์ ์์ 4ํ / ์ค์ 4ํ์ผ๋ก ์ ๋ง๋๋ค.
MariaDB๋ EXPLAIN ANALYZE(ํธ๋ฆฌ)๋ ์์ง๋ง, ์ ํต์ ์ผ๋ก๋ ANALYZE ๋ฌธ์ ์ด๋ค. ์ต์ํ EXPLAIN ํ์ ์ค์ธก ์ปฌ๋ผ์ด ๋ง๋ถ๋ ํํ๋ผ ๋น๊ต๊ฐ ์ง๊ด์ ์ด๋ค.
ANALYZE
SELECT * FROM orders WHERE customer_id = 12345;
+----+-------------+--------+------+---------------+----------+---------+-------+------+--------+----------+------------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | r_rows | filtered | r_filtered | Extra |
+----+-------------+--------+------+---------------+----------+---------+-------+------+--------+----------+------------+-------+
| 1 | SIMPLE | orders | ref | idx_cust | idx_cust | 4 | const | 5 | 4.00 | 100.00 | 100.00 | NULL |
+----+-------------+--------+------+---------------+----------+---------+-------+------+--------+----------+------------+-------+
EXPLAIN์ ์๋ ๋ ์ค์ธก ์ปฌ๋ผ์ด ์ถ๊ฐ๋๋ค.
r_rows—rows(์์)์ ๋์ํ๋ ์ค์ ํ ์. ์์์ ์์ 5 / ์ค์ 4๋ก ๊ทผ์ .r_filtered—filtered(์์ ์ ํ๋)์ ๋์ํ๋ ์ค์ ์ ํ๋(%).
์ค๋ฌด ๊ฐ๊ฐ์ผ๋ก, ํ์ค์บ(type=ALL)์ธ๋ฐ r_filtered๊ฐ 15%๋ณด๋ค ๋ฎ๋ค๋ฉด ์ฝ์ ํ์ ๋๋ถ๋ถ์ ๋ฒ๋ฆฌ๊ณ ์๋ค๋ ๋ป์ด๋ฏ๋ก ์ ์ ํ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ ๋๋ค. ๋ ์์ธํ ๋ณด๋ ค๋ฉด ANALYZE FORMAT=JSON์ ์ด๋ค.
4. PostgreSQL
PostgreSQL์ ๋ช ๋ น ์ฒด๊ณ๊ฐ ๊ฐ์ฅ ๊น๋ํ๋ค.
์์ ๊ณํ — EXPLAIN
EXPLAIN
SELECT * FROM orders WHERE customer_id = 12345;
QUERY PLAN
-----------------------------------------------------------------------
Index Scan using idx_cust on orders (cost=0.29..8.31 rows=5 width=64)
Index Cond: (customer_id = 12345)
cost=0.29..8.31— ์์ ๋น์ฉ..์ด ๋น์ฉ(๋์คํฌ ํ์ด์ง ํ์น ๋จ์์ ์ถ์ ๊ฐ).rows=5— ์์ ํ ์,width=64— ํ ํ๊ท ๋ฐ์ดํธ.Index Scan— ์ธ๋ฑ์ค๋ฅผ ํ๋ค๋ ๋ป. ์ด ์๋ฆฌ์Seq Scan์ด ๋ณด์ด๋ฉด ์ธ๋ฑ์ค๋ฅผ ๋ชป/์ ํ ๊ฒ์ด๋ค.
์ค์ ๊ณํ — EXPLAIN ANALYZE
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE customer_id = 12345;
QUERY PLAN
-----------------------------------------------------------------------
Index Scan using idx_cust on orders (cost=0.29..8.31 rows=5 width=64)
(actual time=0.018..0.024 rows=4 loops=1)
Index Cond: (customer_id = 12345)
Buffers: shared hit=3
Planning Time: 0.072 ms
Execution Time: 0.041 ms
actual time=0.018..0.024— ์ค์ ์์..์ข ๋ฃ ์๊ฐ(ms),rows=4— ์ค์ ํ ์,loops=1— ์ด ๋ ธ๋๊ฐ ๋ฐ๋ณต๋ ํ์.Buffers: shared hit=3— ์บ์์์ ์ฝ์ ๋ธ๋ก ์(hit์ ์บ์ ์ ์ค,read๋ ๋์คํฌ).
์ฃผ์ํ ํจ์ ์ด loops๋ค. Nested Loop ์์ชฝ ๋
ธ๋๋ ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณต๋๋๋ฐ, ํ์๋๋ rows๋ ๋ฐ๋ณต๋น ํ๊ท ์ด๋ค. ์ค์ ์ด ์ฒ๋ฆฌ ํ์๋ rows × loops๋ก ๊ณ์ฐํด์ผ ํ๋ค.
์ฐธ๊ณ ๋ก PostgreSQL 18๋ถํฐ๋ EXPLAIN ANALYZE์ BUFFERS๊ฐ ๊ธฐ๋ณธ ํฌํจ๋๋ค. 17 ์ดํ์์๋ ์์ฒ๋ผ BUFFERS๋ฅผ ์ง์ ๋ถ์ฌ์ผ I/O ์ ๋ณด๊ฐ ๋์จ๋ค. ์ถ๋ ฅ์ FORMAT JSON/XML/YAML๋ก๋ ๋ฐ์ ์ ์๋ค.
5. Oracle
Oracle์ ๊ณํ์ ๋ง๋๋ ๋ช
๋ น๊ณผ ๊ทธ๊ฑธ ๋ณด๊ธฐ ์ข๊ฒ ์ถ๋ ฅํ๋ ๋ช
๋ น(DBMS_XPLAN)์ด ๋ฐ๋ก๋ค.
์์ ๊ณํ — EXPLAIN PLAN
EXPLAIN PLAN FOR
SELECT * FROM orders WHERE customer_id = 12345;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 240 | 3 (0)| |
| 1 | TABLE ACCESS BY INDEX ROWID | ORDERS | 4 | 240 | 3 (0)| |
|* 2 | INDEX RANGE SCAN | IDX_CUST | 4 | | 1 (0)| |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CUSTOMER_ID"=12345)
Operation(TABLE ACCESS FULL=ํ์ค์บ, INDEX RANGE SCAN, TABLE ACCESS BY INDEX ROWID ๋ฑ), Rows(์์ ํ ์, E-Rows), Cost ์ปฌ๋ผ์ด ๋์จ๋ค. ๋จ, ์ด๊ฑด ๋ฐ์ธ๋ ๋ณ์ ๊ฐ์ ๋ฌด์ํ ์ถ์ ์ด๋ค.
์ค์ ๊ณํ — GATHER_PLAN_STATISTICS
์ค์ธก์ ๋ณด๋ ค๋ฉด ์ฟผ๋ฆฌ์ GATHER_PLAN_STATISTICS ํํธ๋ฅผ ๋ฃ์ด ์คํํ๊ณ , DISPLAY_CURSOR๋ฅผ ALLSTATS LAST ํฌ๋งท์ผ๋ก ์ถ๋ ฅํ๋ค.
SELECT /*+ GATHER_PLAN_STATISTICS */ *
FROM orders WHERE customer_id = 12345;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT => 'ALLSTATS LAST'));
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 4 |00:00:00.01 | 5 |
| 1 | TABLE ACCESS BY INDEX ROWID | ORDERS | 1 | 4 | 4 |00:00:00.01 | 5 |
|* 2 | INDEX RANGE SCAN | IDX_CUST | 1 | 4 | 4 |00:00:00.01 | 2 |
-------------------------------------------------------------------------------------------------
- E-Rows — ์ตํฐ๋ง์ด์ ์์ ํ ์, A-Rows — ์ค์ ํ ์.
- Starts — ํด๋น ๋จ๊ณ๊ฐ ์คํ๋ ํ์(PostgreSQL์
loops์ ํด๋น). - Buffers — ์ฝ์ ๋ธ๋ก ์.
E-Rows์ A-Rows์ ์ฐจ์ด๊ฐ ํฌ๊ฒ ๋ฒ์ด์ง๋ ์ฒซ ๋จ๊ณ๊ฐ ๋๊ฐ ๋ฌธ์ ์ ์ถ๋ฐ์ ์ด๋ค. SQL*Plus๋ SQLcl์์๋ SET AUTOTRACE ON์ผ๋ก ๊ฐ๋จํ ๊ณํ๊ณผ ํต๊ณ๋ฅผ ๊ฐ์ด ๋ณผ ์๋ ์๋ค.
๋ ํ๋, ์ถ๋ ฅ ํ๋จ Predicate Information์ด ์ธ๋ฑ์ค ํ์ฉ ํ๋จ์ ๊ฒฐ์ ์ ๋จ์๋ค. ์กฐ๊ฑด์ด access๋ก ์กํ๋ฉด ์ธ๋ฑ์ค ํ์์ ์ง์ ๊ตฌ๋ํ๋ค๋ ๋ป(์ข์)์ด๊ณ , filter๋ก ์กํ๋ฉด ํ์ ๋ค ๊ฐ์ ธ์จ ๋ค ๊ฑธ๋ฌ๋ธ๋ค๋ ๋ป(์ธ๋ฑ์ค๋ฅผ ๊ธฐ๋ํ๋ค๋ฉด ๋์จ)์ด๋ค.
6. SQL Server
SQL Server๋ SSMS(Management Studio)์ ๊ทธ๋ํฝ ์คํ ๊ณํ์ด ๊ฐ๋ ฅํ์ง๋ง, ์คํฌ๋ฆฝํธ๋ก๋ ๋ชจ๋ ๋ฝ์ ์ ์๋ค.
์์ ๊ณํ — SHOWPLAN
SET SHOWPLAN_TEXT ON;
GO
SELECT * FROM Orders WHERE CustomerID = 12345;
GO
SET SHOWPLAN_TEXT OFF;
GO
StmtText
------------------------------------------------------------------------------
|--Index Seek(OBJECT:([Sales].[Orders].[idx_cust]),
SEEK:([Orders].[CustomerID]=(12345)) ORDERED FORWARD)
Index Seek์ด ๋ณด์ด๋ฉด ์ธ๋ฑ์ค๋ฅผ ์ ํํ ์ฐพ์๊ฐ ๊ฒ์ด๋ค. ์ด ์๋ฆฌ์ Index Scan์ด๋ Clustered Index Scan/Table Scan์ด ํฐ ํ
์ด๋ธ์์ ๋ณด์ด๋ฉด ์ธ๋ฑ์ค๋ฅผ ๋ชป ํ๋ ํํ๋ค. SSMS์์๋ Ctrl+L๋ก ๊ทธ๋ํฝ ์์ ๊ณํ์ ๋ฐ๋ก ๋ณผ ์ ์๋ค.
์ค์ ๋ฆฌ์์ค — STATISTICS IO / TIME
์ค์ธก์์ ๊ฐ์ฅ ์์ฃผ ๋ณด๋ ๊ฑด ๋ ผ๋ฆฌ์ ์ฝ๊ธฐ(logical reads)๋ค. ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ํ๋ํ๋ฉฐ ์ด ์ซ์๊ฐ ์ค๋ฉด ๋ ์ ์ ํ์ด์ง๋ฅผ ์ฝ๋๋ค๋ ๋ป์ด๋ผ ์ ๋ขฐํ ์ ์๋ ์งํ๋ค.
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT * FROM Orders WHERE CustomerID = 12345;
Table 'Orders'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 1 ms.
์ธ๋ฑ์ค๊ฐ ์์ผ๋ฉด ๊ฐ์ ์ฟผ๋ฆฌ๊ฐ Clustered Index Scan์ผ๋ก ๋จ์ด์ง๋ฉด์ logical reads๊ฐ ์์ฒ์ผ๋ก ๋ด๋ค. ๊ทธ ์ซ์ ์์ฒด๊ฐ "์ด ์ธ๋ฑ์ค๊ฐ ํจ๊ณผ ์๋ค"์ ์ฆ๊ฑฐ๊ฐ ๋๋ค. ์ค์ ๊ทธ๋ํฝ ๊ณํ์ Ctrl+M ํ ์คํ(๋๋ SET STATISTICS XML ON)์ผ๋ก ๋ณธ๋ค.
๊ทธ๋ํฝ ๊ณํ์ ๋น ๋ฅด๊ฒ ํ์ ๋์ ์๋ น ๋ช ๊ฐ์ง.
- ํ์ดํ ๋๊ป = ํ๋ฅด๋ ํ ์. ๊ฐ์๊ธฐ ๊ตต์ด์ง๋ ์ง์ ์ด ๋ณดํต ๋ฒ์ธ์ด๋ค.
- Key Lookup์ด ๋๋ ๋ฐ๋ณต๋๋ฉด ๋น์ธ๋ค → ํ์ํ ์ปฌ๋ผ์
INCLUDEํ ์ปค๋ฒ๋ง ์ธ๋ฑ์ค๋ก ํด๊ฒฐํ๋ค. - ์ฐ์ฐ์ ๋น์ฉ %๋ ์ถ์ ์ด๋ผ ๊ทธ๋๋ก ๋ฏฟ์ง ๋ง๊ณ , ์ค์ ํ ์·๋ ผ๋ฆฌ์ ์ฝ๊ธฐ๋ฅผ ํจ๊ป ๋ณธ๋ค.
- ๊ทธ๋ํฝ ๊ณํ์ ๋ ธ๋ ๊ฒฝ๊ณ ์์ด์ฝ๊ณผ ๋ น์ ๋๋ฝ ์ธ๋ฑ์ค(missing index) ํํธ๋ ์ข์ ์ถ๋ฐ์ ์ด๋ค.
7. ์ธ๋ฑ์ค๊ฐ ์ ํ๋ ์ด์ ์ฐพ๊ธฐ
์ฌ๊ธฐ๊น์ง๊ฐ ๋๊ตฌ๋ผ๋ฉด, ์ด์ ๋ณธ๋ก ์ด๋ค. ์คํ๊ณํ์์ ํ์ค์บ(๋๋ filter, Index Scan)์ด ๋ณด์ผ ๋, ์ธ๋ฑ์ค๊ฐ ์๋๋ฐ๋ ์ ํ๋ ์ด์ ๋ DB๊ฐ ๋ฌ๋ผ๋ ๋๊ฐ ๊ฐ์ ํจํด์ด๋ค.
- WHERE ์ปฌ๋ผ์ ํจ์๋ ์ฐ์ฐ์ ์์.
WHERE YEAR(created_at) = 2024๋WHERE price + 1000 > 5000์ฒ๋ผ ์ปฌ๋ผ์ ๊ฐ๊ณตํ๋ฉด ์ธ๋ฑ์ค๋ฅผ ๋ชป ์ด๋ค(SARGableํ์ง ์์). ์ปฌ๋ผ์ ๊ทธ๋๋ก ๋๊ณ ์์ ์ชฝ์ ๊ฐ๊ณตํด์ผ ํ๋ค →WHERE created_at >= '2024-01-01'. - ํ์
๋ถ์ผ์น๋ก ์ธํ ์๋ฌต์ ํ๋ณํ. ๋ฌธ์์ด ์ปฌ๋ผ์ ์ซ์๋ฅผ ๋น๊ตํ๊ฑฐ๋ ๊ทธ ๋ฐ๋๋ฉด, DB๊ฐ ํ์ชฝ์ ๋ณํํ๋ฉด์ ์ธ๋ฑ์ค๊ฐ ๋ฌด๋ ฅํ๋๋ค. Oracle์ Predicate๊ฐ
filter๋ก ๋ฐ๋๋ ์ ํ์ ์์ธ์ด๋ค. - ์ ํ๋๊ฐ ๋ฎ์. ์กฐ๊ฑด์ ๋ง๋ ํ์ด ํ ์ด๋ธ์ ์๋น ๋น์จ์ด๋ฉด, ์ตํฐ๋ง์ด์ ๋ ์ธ๋ฑ์ค ํ์๋ณด๋ค ํ์ค์บ์ด ๋ ์ธ๋ค๊ณ ํ๋จํ๋ค. ์ด๊ฑด ์คํ์ด ์๋๋ผ ์ ์์ ์ธ ์ ํ์ผ ๋๊ฐ ๋ง๋ค.
- ํต๊ณ๊ฐ ์ค๋๋๊ฑฐ๋ ๋ถ์ ํํจ. ์ตํฐ๋ง์ด์ ๋ ํต๊ณ๋ก ํ ์๋ฅผ ์ถ์ ํ๋ค. ํต๊ณ๊ฐ ์ด๊ธ๋๋ฉด ์์๊ณผ ์ค์ (
rowsvsr_rows, E-Rows vs A-Rows)๊ฐ ํฌ๊ฒ ๋ฒ์ด์ง๊ณ ์๋ชป๋ ๊ณํ์ ๊ณ ๋ฅธ๋ค → ํต๊ณ๋ฅผ ๊ฐฑ์ ํ๋ค(ANALYZE TABLE,DBMS_STATS,UPDATE STATISTICS). - ์ ํ ์์ผ๋์นด๋ LIKE.
LIKE '%๊ฒ์์ด'๋ ์ธ๋ฑ์ค๋ฅผ ๋ชป ํ๋ค. ๋ค์ชฝ ์์ผ๋์นด๋('๊ฒ์์ด%')๋ ํ ์ ์๋ค. - ๋ณตํฉ ์ธ๋ฑ์ค์ ์ ๋ ์ปฌ๋ผ ๋๋ฝ.
(a, b, c)์ธ๋ฑ์ค๋a๋ถํฐ ์กฐ๊ฑด์ด ์์ด์ผ ํจ์จ์ ์ผ๋ก ํ๋ค.b๋ง์ผ๋ก ๊ฒ์ํ๋ฉด ์ธ๋ฑ์ค๋ฅผ ์ ๋๋ก ๋ชป ์ด๋ค.
๊ฐ DB์์ "์ธ๋ฑ์ค ์ ํ"์ด ์ด๋ป๊ฒ ๋ณด์ด๋์ง ์ ํธ๋ฅผ ์์ฝํ๋ฉด ์ด๋ ๋ค.
- MySQL / MariaDB:
key๊ฐNULL,type์ดALL. - PostgreSQL:
Index Scan์ด์ด์ผ ํ ์๋ฆฌ์Seq Scan. - Oracle: Predicate๊ฐ
access๊ฐ ์๋๋ผfilter, ๊ทธ๋ฆฌ๊ณTABLE ACCESS FULL. - SQL Server:
Index Scan·Table Scan, ๋ ธ๋ ๊ฒฝ๊ณ ๋ ๋๋ฝ ์ธ๋ฑ์ค ํํธ.
8. ๋ค DB ๋น๊ต
| DB | ์์ ๊ณํ (์คํ ์ ํจ) | ์ค์ ๊ณํ (์คํํจ) | ์ค์ ํ ์ ์ปฌ๋ผ | ์ธ๋ฑ์ค ๋ฏธ์ฌ์ฉ ์ ํธ |
| MySQL | EXPLAIN |
EXPLAIN ANALYZE |
(ํธ๋ฆฌ) actual rows | key=NULL, type=ALL |
| MariaDB | EXPLAIN |
ANALYZE ๋ฌธ |
r_rows |
key=NULL, type=ALL |
| PostgreSQL | EXPLAIN |
EXPLAIN ANALYZE |
actual rows | Seq Scan |
| Oracle | EXPLAIN PLAN + DISPLAY |
ํํธ + DISPLAY_CURSOR |
A-Rows | Predicate๊ฐ filter |
| SQL Server | SHOWPLAN_XML / Ctrl+L |
STATISTICS XML / Ctrl+M |
Actual Number of Rows | Index/Table Scan, ๋
ธ๋ ๊ฒฝ๊ณ |
๋ง์น๋ฉฐ
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ธฐ ์ ์ ๋ณด๋ ๊ฒ๋ค์์ "๋๋ฆฌ๋ค๊ณ ๋ฌด์์ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ์ง ๋ง๋ผ"๊ณ ํ๋ค. ์ด ๊ธ์ ๊ทธ ์ง์ด ๋๋ ๊ธ์ด๋ค. ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๋ค๋ฉด, ์คํ๊ณํ์ผ๋ก ๊ทธ๊ฒ ์ค์ ๋ก ์ฐ์ด๋์ง ๋ฐ๋์ ํ์ธํด์ผ ํ๋ค. ๋ง๋ค์๋ค๊ณ ๋์ด ์๋๋ผ, ์ตํฐ๋ง์ด์ ๊ฐ ๊ทธ ์ธ๋ฑ์ค๋ฅผ ๊ณจ๋๋์ง๊น์ง ๋ด์ผ ๊ฒ์ฆ์ด ๋๋๋ค.
DB๋ง๋ค ๋ช
๋ น์ด๋ EXPLAIN, ANALYZE, DBMS_XPLAN, SET STATISTICS๋ก ์ ๊ฐ๊ฐ์ด๊ณ ์ถ๋ ฅ ํํ๋ ํ์ ํธ๋ฆฌ๋ก ๊ฐ๋ฆฐ๋ค. ํ์ง๋ง ์ฝ๋ ๋์ ํ๋๋ค. ์ค์บ ๋ฐฉ์, ์กฐ์ธ ๋ฐฉ์, ๊ทธ๋ฆฌ๊ณ ์์๊ณผ ์ค์ ํ ์์ ์ฐจ์ด. ํนํ ์์๊ณผ ์ค์ ๊ฐ ํฌ๊ฒ ๋ฒ์ด์ง๋ ์ฒซ ์ง์ ์ ์ฐพ๋ ์ต๊ด๋ง ๋ค์ด๋ฉด, ์ด๋ DB๋ก ์ฎ๊ฒจ๊ฐ๋ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฌธ์ ์ ์ง์์ง๋ฅผ ์ง์ ์ ์๋ค.
์ฐธ๊ณ ์ถ์ฒ
- MySQL 8.0 Reference Manual — EXPLAIN Output Format — https://dev.mysql.com/doc/refman/8.0/en/explain-output.html
- MariaDB Documentation — ANALYZE Statement — https://mariadb.com/docs/server/reference/sql-statements/administrative-sql-statements/analyze-and-explain-statements/analyze-statement
- PostgreSQL Documentation — EXPLAIN — https://www.postgresql.org/docs/current/sql-explain.html
- Oracle Database SQL Tuning Guide — Generating and Displaying Execution Plans — https://docs.oracle.com/en/database/oracle/oracle-database/19/tgsql/generating-and-displaying-execution-plans.html
- Microsoft Learn — Showplan Logical and Physical Operators Reference — https://learn.microsoft.com/en-us/sql/relational-databases/showplan-logical-and-physical-operators-reference
- Microsoft Learn — SET STATISTICS TIME (Transact-SQL) — https://learn.microsoft.com/en-us/sql/t-sql/statements/set-statistics-time-transact-sql
'๐ Tech Stack > Database & Infra' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Kubernetes ์ ๋ฌธ (1) | 2026.06.30 |
|---|---|
| GitHub Actions๋ก CI/CD ํ์ดํ๋ผ์ธ ๋ง๋ค๊ธฐ (1) | 2026.06.29 |
| Docker ์ ๋ฌธ (1) | 2026.06.28 |
| ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ๊ธฐ ์ ์ ๋ณด๋ ๊ฒ๋ค (0) | 2026.05.20 |
