1. ๋ฌธ์ ์ํฉ
๋งค์ฅ์์ ๋งค์ ๊ณ ๊ฐ์ ์๋งค์ถ๊ธ์ก์ ๊ธฐ์ค์ผ๋ก, ํด๋น ๊ณ ๊ฐ์ด ์ด๋ ๊ตฌ๊ฐ(Decile, 10๋ฑ๋ถ)์ ์ํ๋์ง๋ฅผ ์ง๊ณ ํด์ผํ๋ ์ํฉ์ ๊ฐ์
์๋ฅผ ๋ค์ด, ์ ์ต๋ ๋งค์ถ๊ธ์ก์ด 20,000์์ด๋ผ๋ฉด ์ด๋ฅผ 10๊ฐ ๊ตฌ๊ฐ์ผ๋ก ๋๋๊ฒ ๋๋ค.
- ๊ตฌ๊ฐ 1: 1 ~ 2,000์
- ๊ตฌ๊ฐ 2: 2,001 ~ 4,000์
- ๊ตฌ๊ฐ 3: 4,001 ~ 6,000์
- …
- ๊ตฌ๊ฐ 10: 18,001 ~ 20,000์
๊ฒฐ๊ตญ ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒฐ๊ณผ๋ ๊ฐ๋จํ๋ค.
- ๊ฐ ๊ตฌ๊ฐ๋ณ ๊ณ ๊ฐ ์
- ๊ฐ ๊ตฌ๊ฐ๋ณ ๊ณ ๊ฐ ์๊ฐ ์ ์ฒด์์ ์ฐจ์งํ๋ ๋น์จ(%)
2. ์ด๊ธฐ ์ ๊ทผ (Oracle ์ ์ฉ ๋ฌธ๋ฒ)
์ฒ์์๋ Oracle ์ ์ฉ ๊ธฐ๋ฅ์ ์๊ฒ๋์ด ์ด๋ฅผ ํ์ฉํด ๊ฐ๋ฐํ๊ณ ์ ํ๋ค.
- CONNECT BY LEVEL : 10๊ฐ ๊ตฌ๊ฐ ์์ฑ
- WIDTH_BUCKET : ๊ฐ ๊ณ ๊ฐ์ ๋งค์ถ๊ธ์ก์ ์๋์ผ๋ก ๊ตฌ๊ฐ ๋ฒํธ์ ๋งคํ
๋น ๋ฅด๊ฒ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์์ง๋ง, ๋จ์ ๋ ๋ช ํํ๋ค.
- Oracle ์ข ์์ฑ → ๋ค๋ฅธ DBMS(PostgreSQL, MySQL ๋ฑ)๋ก ์ด์ ๋ถ๊ฐ
- ์ธ๋ฑ์ค ํ์ฉ ํ๊ณ → WIDTH_BUCKET์ ํจ์ ๊ธฐ๋ฐ์ด๋ผ RANGE SCAN ๋ถ๊ฐ (CPU ์ฐ์ฐ ์ฆ๊ฐ)
- ์ฑ๋ฅ ์ ํ → 100๋ง ๊ฑด ์ด์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ CPU์์ ํจ์ ๊ณ์ฐ์ ํ๊ฒ ๋์๋ค
3. ๊ฐ์ ์ ์ฟผ๋ฆฌ (Oracle ์ข ์ ๋ฌธ๋ฒ)
WITH ์ต๋๋งค์ถ_CTE AS (
SELECT MAX(์๋งค์ถ๊ธ์ก) ์ต๋๋งค์ถ
FROM ๋งค์ถ์กฐ๊ฑด_T
WHERE ๋งค์ฅ์ฝ๋ = :๋งค์ฅ์ฝ๋
AND :๊ธฐ์ค๋
์ BETWEEN SUBSTR(์์์ผ์,0,6) AND SUBSTR(์ข
๋ฃ์ผ์,0,6)
),
๊ตฌ๊ฐ๊ธฐ์ค_CTE AS (
SELECT CASE WHEN LEVEL = 1
THEN 1
ELSE (LEVEL-1) * (์ต๋๋งค์ถ/10)
END AS ์ต์๊ฐ,
CASE WHEN LEVEL = 11
THEN NULL
ELSE LEVEL * (์ต๋๋งค์ถ/10) - 1
END AS ์ต๋๊ฐ
FROM ์ต๋๋งค์ถ_CTE
CONNECT BY LEVEL <= 11
),
๋ฒํท์ง๊ณ_CTE AS (
SELECT WIDTH_BUCKET(์๋งค์ถ๊ธ์ก, 1, V.์ต๋๋งค์ถ, 10) AS ๋ฒํท๋ฒํธ,
COUNT(*) AS ๊ณ ๊ฐ์
FROM ์๋ณ๋งค์ถ์ง๊ณ_T A, ์ต๋๋งค์ถ_CTE V
WHERE A.๋งค์ฅ์ฝ๋ = :๋งค์ฅ์ฝ๋
AND A.๊ธฐ์ค๋
์ = :๊ธฐ์ค๋
์
GROUP BY WIDTH_BUCKET(์๋งค์ถ๊ธ์ก, 1, V.์ต๋๋งค์ถ, 10)
)
SELECT B.์ต์๊ฐ, B.์ต๋๊ฐ,
NVL(D.๊ณ ๊ฐ์,0) AS ๊ณ ๊ฐ์
FROM ๊ตฌ๊ฐ๊ธฐ์ค_CTE B, ๋ฒํท์ง๊ณ_CTE D
WHERE B.๊ตฌ๊ฐ์๋ฒ = D.๋ฒํท๋ฒํธ(+)
ORDER BY ์ต์๊ฐ;
3-1. ๊ฐ์ ํ ์ฟผ๋ฆฌ (ANSI SQL ๊ธฐ๋ฐ)
oracle์์๋ Recursive ๊ด๋ จํด์ ์ฝ๋๊ฐ ์์ ์ด ํ์ํ๋ค. (์ด์์ฑ 100%๋ ์๋ ์ ์ ์ดํดํด์ฃผ์ธ์..)
WITH RECURSIVE
๊ตฌ๊ฐํญ๊ณ์ฐ_CTE AS (
SELECT CEIL(:์ต๋๋งค์ถ/10.0) AS ๊ตฌ๊ฐํญ,
:์ต๋๋งค์ถ AS ์ต๋๋งค์ถ
),
๊ตฌ๊ฐ๊ธฐ์ค_CTE AS (
SELECT 1 AS ๊ตฌ๊ฐ๋ฒํธ, 1 AS ์ต์๊ฐ, ๊ตฌ๊ฐํญ AS ์ต๋๊ฐ, ๊ตฌ๊ฐํญ, ์ต๋๋งค์ถ
FROM ๊ตฌ๊ฐํญ๊ณ์ฐ_CTE
UNION ALL
SELECT ๊ตฌ๊ฐ๋ฒํธ+1,
์ต๋๊ฐ+1,
CASE WHEN ๊ตฌ๊ฐ๋ฒํธ+1=10 THEN ์ต๋๋งค์ถ ELSE ์ต๋๊ฐ+๊ตฌ๊ฐํญ END,
๊ตฌ๊ฐํญ, ์ต๋๋งค์ถ
FROM ๊ตฌ๊ฐ๊ธฐ์ค_CTE
WHERE ๊ตฌ๊ฐ๋ฒํธ < 10
),
๊ตฌ๊ฐ๋ณ์ง๊ณ_CTE AS (
SELECT B.๊ตฌ๊ฐ๋ฒํธ, B.์ต์๊ฐ, B.์ต๋๊ฐ,
COUNT(C.๊ณ ๊ฐID) AS ๊ณ ๊ฐ์
FROM ๊ตฌ๊ฐ๊ธฐ์ค_CTE B
LEFT JOIN ์๋ณ๋งค์ถ์ง๊ณ_T C
ON C.์๋งค์ถ๊ธ์ก BETWEEN B.์ต์๊ฐ AND B.์ต๋๊ฐ
AND C.๋งค์ฅ์ฝ๋ = :๋งค์ฅ์ฝ๋
AND C.๊ธฐ์ค๋
์ = :๊ธฐ์ค๋
์
GROUP BY B.๊ตฌ๊ฐ๋ฒํธ, B.์ต์๊ฐ, B.์ต๋๊ฐ
)
SELECT ๊ตฌ๊ฐ๋ฒํธ, ์ต์๊ฐ, ์ต๋๊ฐ, ๊ณ ๊ฐ์,
ROUND(๊ณ ๊ฐ์*100.0/NULLIF(SUM(๊ณ ๊ฐ์) OVER(),0),2) AS ๋น์จํผ์ผํธ
FROM ๊ตฌ๊ฐ๋ณ์ง๊ณ_CTE
ORDER BY ๊ตฌ๊ฐ๋ฒํธ;
4. ๊ฐ์ ํฌ์ธํธ
- ANSI SQL ๋ฌธ๋ฒ
WITH RECURSIVE๋ฅผ ์ฌ์ฉํด Oracle ์ ์ฉ ๋ฌธ๋ฒ์ ์ ๊ฑฐํ๊ฒ ๋์๋ค.
์ด๋ก์จ PostgreSQL, MySQL ๋ฑ์์๋ ๋์ผํ๊ฒ ๋์ํ ์ ์๊ฒ ๋์๋ค. - ๊ตฌ๊ฐ๋ณ ์กฐ์ธ ๊ตฌ์กฐ
๊ตฌ๊ฐ(10ํ)์ ๊ธฐ์ค์ผ๋ก ๋งค์ถ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ธํ๊ฒ ๋ง๋ค์๋ค.
๋๋ถ์ ๊ฐ ๊ตฌ๊ฐ๋ง๋ค INDEX RANGE SCAN์ด ๊ฐ๋ฅํด์ก๋ค. - ์ฑ๋ฅ ์์ธก ๊ฐ๋ฅ์ฑ
ํญ์ 10ํ RANGE SCAN๋ง ์ํํ๊ฒ ๋๋, ๋ฐ์ดํฐ๋์ด ์ปค์ ธ๋ ์คํ ํจํด์ด ์์ ์ ์ด๊ฒ ๋์๋ค.'
5. ์ธ๋ฑ์ค ์ค๊ณ ๊ณผ์
5-1. ์ด๊ธฐ ์ธ๋ฑ์ค
CREATE INDEX IDX_๋งค์ถ_CLUB_YM_CUST ON ์๋ณ๋งค์ถ์ง๊ณ_T (๋งค์ฅ์ฝ๋, ๊ธฐ์ค๋
์, ๊ณ ๊ฐID);
- ๊ณ ๊ฐ ๋จ๊ฑด ์กฐํ ์ ๋ฆฌ
- ๊ตฌ๊ฐ ์กฐ๊ฑด์๋ ์๋งค์ถ๊ธ์ก์ด ์์ด ํ ์ด๋ธ ๋๋ค ์ก์ธ์ค๋ฅผ ํผํ ์ ์์
5-2. ๊ฐ์ ์ธ๋ฑ์ค
CREATE INDEX IDX_๋งค์ถ_YM_CLUB_AMT_CUST ON ์๋ณ๋งค์ถ์ง๊ณ_T (๊ธฐ์ค๋
์, ๋งค์ฅ์ฝ๋, ์๋งค์ถ๊ธ์ก, ๊ณ ๊ฐID);
- ์๋งค์ถ๊ธ์ก์ ํฌํจํด ๊ตฌ๊ฐ Range Scan์ด ๊ฐ๋ฅ
- ํ ์ด๋ธ ์ก์ธ์ค ์์ด ์ธ๋ฑ์ค๋ง์ผ๋ก ์ฒ๋ฆฌ(index-only)๊ฐ ๊ฐ๋ฅ
๋ค๋ง ๊ณ ๊ฐ ๋จ๊ฑด ์กฐํ์๋ ๊ธฐ์กด ์ธ๋ฑ์ค๊ฐ ๋ ์ ํฉํ๊ธฐ์,
๋ ์ธ๋ฑ์ค๋ฅผ ์ด์ํํด ์ํฉ๋ณ๋ก ์ ํํ๊ฒ ๋๋ ๊ฒ์ด ๋ ํฉ๋ฆฌ์ ์ผ ์ ์๋ค.
6. SQL ์คํ๊ณํ ๋น๊ต
6-1. ๊ฐ์ ์ (Oracle ์ ์ฉ / WIDTH_BUCKET)
- COST: ์ฝ 1450
- TIME: 3.5 ~ 4์ด
- ํน์ง: ์ธ๋ฑ์ค๋ก ์กฐ๊ฑด ์ผ๋ถ๋ง ๊ฑธ๋ฌ๋ด๊ณ , ๋๋จธ์ง๋ ํ ์ด๋ธ ๋๋ค ์ก์ธ์ค์ ํจ์ ๊ณ์ฐ
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost | Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 11 | 1450 | 3.8s |
| 1 | SORT ORDER BY | | 11 | 3 | 0.0s |
| 2 | NESTED LOOPS OUTER | | 11 | 1447 | 3.8s |
| 3 | VIEW | (B: ๊ตฌ๊ฐ๊ธฐ์ค_CTE) | 11 | 2 | 0.0s |
| 4 | CONNECT BY WITH FILTERING | | 11 | 2 | 0.0s |
| 5 | VIEW | (D: ๋ฒํท์ง๊ณ_CTE) | 10 | 1445 | 3.8s |
| 6 | HASH GROUP BY | | 10 | 1445 | 3.8s |
| 7 | TABLE ACCESS BY INDEX ROWID | ์๋ณ๋งค์ถ์ง๊ณ_T | 1,000K | 1400 | 3.6s |
| 8 | INDEX RANGE SCAN | IDX_๋งค์ถ_CLUB_YM_CUST | 1,000K | 320 | 0.9s |
------------------------------------------------------------------------------------------------------------
Predicate Information:
8 - access("๊ธฐ์ค๋
์" = :๊ธฐ์ค๋
์ AND "๋งค์ฅ์ฝ๋" = :๋งค์ฅ์ฝ๋)
7 - filter(WIDTH_BUCKET("์๋งค์ถ๊ธ์ก", 1, :์ต๋๋งค์ถ, 10) IS NOT NULL)
6-2. ๊ฐ์ ํ (ANSI SQL / RECURSIVE, JOIN, RANGE SCAN, INDEX_ONLY)
- COST: ์ฝ 150
- TIME: 0.4์ด
- ํน์ง: ๊ตฌ๊ฐ๋ณ 10ํ INDEX RANGE SCAN๋ง ์ํ, ํ ์ด๋ธ ์ก์ธ์ค ๋ฐ์X
Plan hash value: 7xxx4321
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost | Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 150 | 0.4s |
| 1 | SORT ORDER BY | | 10 | 5 | 0.0s |
| 2 | VIEW | (D: ๊ตฌ๊ฐ๋ณ์ง๊ณ_CTE) | 10 | 145 | 0.4s |
| 3 | HASH GROUP BY | | 10 | 145 | 0.4s |
| 4 | NESTED LOOPS OUTER | | 1M | 140 | 0.4s |
| 5 | RECURSIVE WITH PUMP | (B: ๊ตฌ๊ฐ๊ธฐ์ค_CTE, 10ํ) | 10 | 1 | 0.0s |
| 6 | PARTITION RANGE SINGLE | | 100K | 14 | 0.05s |
| 7 | INDEX RANGE SCAN | IDX_๋งค์ถ_YM_CLUB_AMT_CUST | 100K | 14 | 0.05s |
------------------------------------------------------------------------------------------------------------
Predicate Information:
7 - access("C"."๊ธฐ์ค๋
์"=:๊ธฐ์ค๋
์ AND "C"."๋งค์ฅ์ฝ๋"=:๋งค์ฅ์ฝ๋
AND "C"."์๋งค์ถ๊ธ์ก" BETWEEN "B"."์ต์๊ฐ" AND "B"."์ต๋๊ฐ")
6-3. ์ฑ๋ฅ ๋น๊ต ์์ฝ
| ๊ฐ์ ์ | (๋งค์ฅ์ฝ๋, ๊ธฐ์ค๋ ์, ๊ณ ๊ฐID) | RANGE SCAN ํ ROWID ๋๋ค ์ก์ธ์ค + Width Bucket ํจ์๊ณ์ฐ | cost : 1450 | 3.5~4s |
| ๊ฐ์ ํ | (๊ธฐ์ค๋ ์, ๋งค์ฅ์ฝ๋, ์๋งค์ถ๊ธ์ก, ๊ณ ๊ฐID) | ๊ตฌ๊ฐ๋ณ INDEX RANGE SCAN (index-only) | cost : 150 | 0.4s |
7. ๊ฒฐ๋ก
- Oracle ์ข
์์ฑ ์ ๊ฑฐ
- ์ ์ฉ ํจ์(WIDTH_BUCKET, CONNECT BY) ๋์ ANSI SQL(WITH RECURSIVE)๋ฅผ ์ฌ์ฉํ๋ฉด์ WIDTH_BUCKET์์์ ๋ถํ์ํ CPU ์ฐ์ฐ์ ์ค์ผ ์ ์์๋ค.
- ๊ฐ DB์ ์ข ์์ ์ธ ํจ์์ ์ฌ์ฉ์ ์ง์ํด์ผ ํ๋ ์ผ์ด์ค๋ฅผ ์๊ฒ ๋์๋ค.
- ์ธ๋ฑ์ค ์ต์ ํ
- ๋ฒ์ ์กฐ๊ฑด ์ปฌ๋ผ(์๋งค์ถ๊ธ์ก)์ ์ธ๋ฑ์ค์ ํฌํจ์ํด์ผ๋ก์จ ํ ์ด๋ธ ์์ธ์ค๊ฐ ํ์ํ์ง ์๊ฒ ๋์๊ณ , ๊ตฌ๊ฐ๋ณ ์ง๊ณ ์ฟผ๋ฆฌ์์์ ํจ์จ์ด ์ฆ๊ฐํ์๋ค.
- ์ฑ๋ฅ ๊ฐ์
- 3~4์ด -> 0.4์ด, ์ฝ 10๋ฐฐ ๊ฐ์
'๐ Backend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ์ ์ฐฉ์ ์์คํ ์์ ๋ฐ์ํ๋ Race Condition์ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ (0) | 2025.01.06 |
|---|---|
| JNDI๋ฅผ ์ด์ฉํ์ฌ Datasource๋ฅผ ์ค์ ํ ์ด์ (0) | 2024.04.03 |
| OncePerRequestFilter ์ฌ์ฉํ๋ ์ด์ (0) | 2024.04.03 |
| Thread Pool์ ๋ํด์(with Tomcat, Spring Boot) (0) | 2023.09.12 |
| XSS์ CSRF์ ํน์ง (1) | 2023.09.11 |