μ΄ κΈμ offset based pagination μ λΉλ―Έλ‘ λ°μ΄ν°λ² μ΄μ€μ λ΄λΆ λμκ³Ό μ¬λ¬ κ°λ λ€μ μμ보λ κΈ μ λλ€.
2κ°μ κΈλ‘ ꡬμ±λμ΄ μκ³ , κ°κ°μ κΈμμ μ»μ μ μλ insight κ° λ€λ₯΄λ ν¨κ» μ½μΌλ©΄ λμ± μ μ΅ν©λλ€.
- μ€νμ νμ΄μ§μ΄ λλ¦° μ§μ§ μ΄μ
- μ€νμ νμ΄μ§, λ¨κ³λ³λ‘ μ΅μ ννκΈ° <- νμ¬ κΈ
μ§λ μκ°, μ°λ¦¬λ μ€νμ νμ΄μ§μ΄ λλ¦° μ§μ§ μ΄μ μ λν΄μ μμ보μλ€.
offset based pagination μ λ³΄κ³ λκ΅°κ°λ μ΄λ° μ΄μΌκΈ°λ₯Ό νλ€.
"κΌ offset based paging μ ν΄μΌνλμ? cursor based paging μ νλ©΄ μλΌμ?"
λ§λ λ§μ΄λ€. νμ΄μ€λΆμμλ κ°λ₯ν νμ cursor based pagination μ΄ μ¬μ©λμ΄μΌ νλ€κ³ κ°μ‘°νλ€.
νμ§λ§ μ΄λ ν λΉμ¦λμ€ μꡬμ¬νμμλ λ€λ₯Ό μ μλ€.
offset based paging μ μ¬μ©νμ§ μλκ²μ΄ μ’μΌλ νμ κ·Έλ΄μ μλ€.
μ΄λ€ μν©μμλ no offset μ μ¬μ©ν μ μμ μ μλ€
- λΉμ¦λμ€ μꡬμ¬νμ΄ no offset μ μ¬μ©ν μ μλ κ²½μ°
- WHERE μ‘°κ±΄μ΄ μ€λ³΅λμ΄ cursor λ₯Ό μ¬μ©ν μ μλ κ²½μ°
κ·Έλμ μ°λ¦¬λ offset based pagaing μ μ μ°λ λ²λ μμμΌνλλ°, μ΄μ λν΄μ μ¬λ¬ λ°μ΄ν°λ² μ΄μ€μ κ°λ λ€κ³Ό ν¨κ» μ΄ν΄ν΄λ³΄λλ‘ νμ
μ΄λ² κΈμ ν΅ν΄ μ¬λ¬λΆλ€μ μλ ν€μλμ λν΄μ μμκ° μ μμ κ²μ΄λ€.
- deferred join
- composite index, concaternate index
- using filesort vs using index sort
- covering index
TL;DR
μ΄λ² κΈμ ν΅μ¬μ μμ½νλ©΄ λ€μκ³Ό κ°λ€.
- offset based pagination μ΅μ ν
- deferred join: buffering λ°©μμ μ΄μ© μ μλ λ°μ΄ν° λ²λ €μ§(discard)μ μ΅μννμ
- covering index: λΆνμν Disk I/O λ₯Ό μ€μ΄μ
- composite index: filesort λ₯Ό index sort λ‘ λ°κΎΈμ
offset based pagination μ΅μ ν νκΈ°
μ΅μ νμ μμ, μ§λ μκ° offset based pagination μ΄ λλ¦° μ΄μ μ λν΄μ λ€μ ν λ² μκΈ°ν΄λ³΄μ
- λλ¦° μ΄μ 1. ORDER BY ꡬ문μ μν΄ streaming λ°©μμμ buffering λ°©μμΌλ‘ λ³κ²½λ¨
- κ·Έμ λ°λΌ LIMIT μΏΌλ¦¬κ° μ‘΄μ¬νλλΌλ μ 체 λ°μ΄ν°λ₯Ό μ‘°ννκ² λ¨
- λλ¦° μ΄μ 2. μ λ ¬μ μν΄ νμν λ°μ΄ν°λ€μ κ°μ Έμ€κΈ° μν΄ offset λ§νΌ Disk I/O κ° λ°μ
- νΉμ μΌμ΄μ€μμ μμ I/O λ index λ₯Ό μ¬μ©ν μ μκΈ° λλ¬Έμ λΉμΌ Random I/O μ λ°
- λλ¦° μ΄μ 3. filesort κ° μ¬μ©λ¨
- μ λ ¬μ μν sort buffer ν λΉ
- λ°μ΄ν°λ€μ νΉμ λ¨μλ‘ λλκ³ μ λ ¬μ μν ν κ²°κ³Όλ₯Ό λμ€ν¬μ μμ μ μ₯
- μ λ ¬λ μμ νμΌλ€μ λ³ν©(mutiple merge) νμ¬ κ²°κ³Ό ν μ΄λΈ μμ±
μ μ΄μ λ€ μ€ 1λ²μ ν΄κ²°ν μ μλ κ²½μ°κ° λλΆλΆμΌ κ²μ΄λ€.
κ·Έ μλμ 2κ°μ§ λλ¦° μ΄μ λ₯Ό νλμ© μ κ±°νλ©΄μ μ±λ₯μ λμ΄μ¬λ €λ³΄μ
μ΅μ ν 1λ¨κ³. deferred join μ μ΄μ©νλΌ
첫λ²μ§Έλ‘ μ μ©ν μ΅μ νλ λ°λ‘ deferred join μ΄λΌλ κΈ°λ²μ΄λ€
deferred λΌλ λ»μ postponed, delay μΌλ‘ μ§μ°μν€λ€λ μλ―Έλ₯Ό κ°μ§κ³ μλ€.
deferred join μ μΌμ’ μ lazy loading μ μννλ κ²μ΄λ€.
ν΅μ¬μ OFFSET ꡬ문μΌλ‘ μΈν΄ κ·Έ λ§νΌ Disk μ I/O κ° λ°μνλ κ²μ λ§μ μ μμ§λ§, λμ λ²λ €μ§λ λ°μ΄ν°λ₯Ό μ€μ΄λ κ²μ΄λ€.
λ²λ €μ§λ λ°μ΄ν°λ₯Ό μ€μ¬μ λ©λͺ¨λ¦¬μμ (buffer pool) μ²λ¦¬λλ λ°μ΄ν°μ μ¬μ΄μ¦λ₯Ό μ€μ΄λ κ²μ΄λ€
μ΄λ»κ² ν κΉ?
- μ λ ¬μ μνν΄μΌ νλ OFFSET ꡬ문μ sub query λ‘ λ§λ λ€.
- μμ κ²°κ³Όλ‘ μ‘°νλ id λ₯Ό μλ ν μ΄λΈκ³Ό join νλ€.
μ΄ κ³Όμ μ κ±°μΉλ©΄, λ€λμ λ°μ΄ν°λ₯Ό μ‘°ννλ OFFSET 쿼리μμ id λ§ μ‘°ννκ² λκ³ , μλ ν μ΄λΈκ³Ό join μ νμ¬ μ 체 κ²°κ³Όλ₯Ό μ»μ μ μκ² λλ€.
deferred join - ꡬν λ°©λ²
SELECT *
FROM ν
μ΄λΈλͺ
ORDER BY μ λ ¬_쑰건
LIMIT μ€νμ
_κ°, νμ΄μ§_μ¬μ΄μ¦;
SELECT *
FROM ν
μ΄λΈλͺ
JOIN (/*μμ μ€νμ
쿼리 λ¨, id λ§ μ‘°ννλ€*/) AS temp USING (id)
ORDER BY μ λ ¬_쑰건;
첫λ²μ§Έ 쿼리μμ SELECT * FROM..
μ΄ μλλΌ SELECT id FROM..
μΌλ‘ μμ ν 쿼리λ₯Ό λλ²μ§Έ 쿼리μ JOIN μ sub query λ‘ λ£μΌλ©΄ λλ€.
κ·ΈλΌ OFFSET LIMIT
μ΄ μ μ©λ λ id λ§ μ‘°νν κ²μ΄κ³ , OFFSET μ΄ν λ²λ €μ§λ λ°μ΄ν°λ€λ id λ§ λ²λ €μ§κ² λλ€.
λ§μ§λ§μΌλ‘ μ€μ client μκ² λ°νν΄μΌνλ λ°μ΄ν°λ€μ JOIN μΈλΆ 쿼리μμ μ€ννλ κ²μ΄λ€
μμλ₯Ό ν΅ν΄ μμ보μ
deferred join - μμ
μλμ 쿼리λ 170λ§κ±΄μ λ°μ΄ν°μ μ μ©λ μΌλ°μ μΈ OFFSET 쿼리μ΄λ€.
SELECT *
FROM simple_todos AS todos
ORDER BY todos.createdAt,
todos.title
LIMIT 1700000, 10;
μ΄ μΏΌλ¦¬μμ SELECT λ€μ λμ€λ *
λλ¬Έμ OFFSET λ§νΌ κ°μ Έμ μ λ ¬μ νλλ°, λ©λͺ¨λ¦¬ 곡κ°μ λ§μ΄ μ‘μλ¨Ήκ² λλ€.
νμ§λ§ μ¬κΈ°μ deferred join μ μ¬μ©νλ€λ©΄
SELECT *
FROM simple_todos AS todos
JOIN ( -- deferred join μμ
SELECT id -- id λ§ μ‘°ν
FROM simple_todos
ORDER BY todos.createdAt, todos.title
LIMIT 1700000, 10
) AS temp USING (id) -- sub query μ κ²°κ³Όλ₯Ό driving table id λ‘ join
ORDER BY todos.createdAt, todos.title; -- μ¬μ λ ¬
κ³Ό κ°μ΄λ μ μλ€
deferred join - μ±λ₯ λΉκ΅
deferred join μ μ¬μ©ν 쿼리μ κ·Έλ μ§ μμ 쿼리μ κ²°κ³Ό μ½ 1.6λ°°μ μ±λ₯ μ°¨μ΄λ₯Ό 보μλ€.
μ μλ―Έν μ±λ₯μ μ°¨μ΄λ νμΈνμ§λ§ μμ§ λΆμ‘±νλ€.
μ΄ μΏΌλ¦¬λ 170λ§ κ±΄μ λ°μ΄ν°μΌ λ μλ―Έκ° μμ§ 10λ§κ±΄μ μ λμ λ°μ΄ν°μμλ ν° μ°¨μ΄κ° μμ κ²μ΄λ€.
κ·Έλ₯ 볡μ‘ν 쿼리λ‘λ§ λ³΄μΌ λΏμ΄λ€.
λ λμ μμ€μ μ΅μ νλ₯Ό ν΄λ³΄μ
μ΅μ ν 2λ¨κ³. Random Disk I/O λ₯Ό μ€μ¬λ³΄μ
μ¬μ€ μμ λ¬Έμ μ€ κ°μ₯ ν° λ¬Έμ , μ¦ κ°μ₯ λλ¦° μμ μ λΉμ°ν λ°μ΄ν° λ μ½λλ₯Ό μ‘°νν λ λ°μνλ Disk I/O (Random I/O) μΌ κ²μ΄λ€.
μ΄λ»κ² νλ©΄ Disk I/O λ₯Ό μ€μΌ μ μμκΉ?
λ΅μ κ°λ¨νλ€. λ°μ΄ν° λ μ½λλ₯Ό μν Disk I/O λ₯Ό νμ§ μλλ‘ μ λνλ©΄ λλ€
DB μ μ€ν μμ§μ΄ λ°μ΄ν° λ μ½λλ₯Ό μ‘°ννκΈ° μν΄ Disk I/O λ₯Ό νκΈ° μ μ νμν λ°μ΄ν°λ₯Ό λͺ¨λ μ₯μ΄μ£Όλ©΄ λλ€.
- ORDER BY 쑰건μ
createdAt
μ΄ μμ΄μ μ΄μ© μ μμ΄ Disk μ μ κ·Όν΄μΌνλ€?- κ·ΈλΌ index λ₯Ό κ±Έμ΄λ²λ¦¬μ.
- index λ₯Ό 미리 λ§λ€μ΄λμκΈ° λλ¬Έμ λ μ½λ μ 체λ₯Ό μ‘°ννλ Disk I/O κ° μ¬λΌμ§ κ²μ΄λ€
SELECT * FROM..
λλ¬Έμ μ 체 λ°μ΄ν° μ‘°νλ₯Ό μν΄ Disk μ μ κ·Όν΄μΌνλ€?- κ·ΈλΌ
SELECT id FROM..
μΌλ‘ pk(id) λ§ μ‘°ννλλ‘ νμ - pk(id) λ μλμΌλ‘ μμ±λλ index μ΄κΈ° λλ¬Έμ index μ‘°νμ λͺ¨λ μ‘°κ±΄μ΄ μΆ©μ‘±λλ€.
- λ°μ΄ν° λ μ½λ μ κ·Όμ μν Disk IO κ° μμ΄μ§ κ²μ΄λ€
- κ·ΈλΌ
μ¬μ€ μμμ μ€λͺ ν λ΄μ©μ΄ λ°λ‘ Covering Index μ΄λ€.
Disk I/O μ€μ΄κΈ° - covering index
covering index λ 쿼리 κ²°κ³Όλ₯Ό μ»κΈ° μν΄ νμν λͺ¨λ μ 보λ₯Ό ν¬ν¨νλ index λ₯Ό μλ―Ένλ€.
SELECT username
FROM users
WHERE username = 'μ₯μμ΅'
ALTER TABLE users
ADD INDEX idx_name (username);
μμ κ°μ΄ index λ₯Ό κ±Έκ³ index μ μ€μ λ column name μΌλ‘ WHERE 쑰건과 SELECT κ²°κ³Όλ₯Ό ꡬμ±νλ€λ©΄ Table Scan μ΄ μλλΌ Index Scan μΌλ‘ λμνκ² λ κ²μ΄λ€
κ·ΈλΌ κ²°κ΅ νΉμ λ°μ΄ν° μ‘°νλ₯Ό μν΄ λ°μ΄ν° λ μ½λλ₯Ό μ§μ μ‘°ννλ Disk I/O κ° μ¬λΌμ§ κ²μ΄λ€.
컀λ²λ§ μΈλ±μ€λ₯Ό μ μ©νμ¬ μ±λ₯μ λΉκ΅νκΈ° μ μ μλ μ΅μ νλ₯Ό λ¨Όμ νμΈν΄λ³΄μ.
μ΅μ ν 3 λ¨κ³. filesort λ₯Ό μμ μ
filesort λ μμ 볡μ‘νκ³ μκ°μ΄ μ€λ 걸리λ μμ μ€ νλλ‘ κΌ½νλ€.
filesort λ₯Ό μμ λ©΄ μ±λ₯ ν₯μμ΄ μλͺ νλ€!!
filesort λ₯Ό μμ κΈ° - μ¬μ μ§μ
filesort λ₯Ό μμ κΈ° μν΄μλ μ°μ κ·Έκ² λ¬΄μμΈμ§, κ·Έλ¦¬κ³ λμμ 무μμΈμ§ μμμΌ νλ€.
filesort μ index sort μ λν μ€λͺ μ μ§λ μ€νμ νμ΄μ§μ΄ λλ¦° μ§μ§ μ΄μ μμ μΈκΈνμμ§λ§ κ°λ΅ν μ 리νμλ©΄
- index sort
- νμ μ λ ¬λ μνλ‘ μ μ₯νλ index μ νΉμ±μ μ΄μ©νλ κ²
ORDER BY
ꡬ문μ index λ‘ μ§μ λ column μ΄ μλ κ²½μ° λ―Έλ¦¬ μ λ ¬λ μΈλ±μ€μ νΉμ±μ μ΄μ©ν΄μ μ λ ¬λ κ²°κ³Όλ₯Ό λ°ν- μ λ ¬μ μν μμ file μμ± λ° multiple merge λ₯Ό μν μΆκ°μ μΈ μμ μμ
- μ λ ¬ μμ μ€ κ°μ₯ λΉ λ₯Έ μλ 보μ₯
- filesort
- filesort λ μ λ ¬μ μν΄ λμ€ν¬λ μμ νμΌμ μ¬μ©νλ λ°©λ²
- μ£Όλ‘ index sort λ₯Ό μ¬μ©ν μ μλ κ²½μ°μ μ¬μ©λλ©° λ€μκ³Ό κ°μ μΌμ νλ€
- λ΄λΆμ μΌλ‘ λ€μκ³Ό κ°μ΄ μ²λ¦¬λλ―λ‘ λΉκ΅μ λλ¦Ό
- λ΄λΆμ μΌλ‘ λ©λͺ¨λ¦¬μ sort buffer λ₯Ό μμ±(ν λΉλ°μ)
- sort buffer μμ μ λ ¬ μ°μ°μ μν
- λ°μ΄ν°κ° λ§μ κ²½μ° μ 체 λ°μ΄ν°λ₯Ό λΈλ‘ λ¨μλ‘ λλ
- λΈλ‘ λ΄μ μ λ ¬ κ²°κ³Όλ₯Ό file ννλ‘ μμ μ μ₯
- μΆκ°μ μΈ Disk I/O λ°μ
- μ λ ¬λ κ²°κ³Όλ€μ λ³ν©ν κ²°κ³Ό λ°ν
- multi-merge μμ μννμ¬ κ²°κ³Ό ν μ΄λΈ μμ±
- λ΄λΆμ μΌλ‘ λ©λͺ¨λ¦¬μ sort buffer λ₯Ό μμ±(ν λΉλ°μ)
filesort λ₯Ό μμ κΈ° - λ³΅ν© μΈλ±μ€ μ€μ
filesort λ₯Ό μμ κ³ μ΅ν°λ§μ΄μ κ° index sort λ₯Ό μ¬μ©νλλ‘ μ λνκΈ° μν΄μλ ORDER BY μ λ€μ΄κ°λ μ‘°κ±΄μ΄ λͺ¨λ index λ‘ μ§μ λ column μ΄μ΄μΌ νλ€.
ORDER BY μ λ€μ΄κ°λ 쑰건μ index λ‘ νλ λ³΅ν© μΈλ±μ€λ₯Ό λ§λ€μ΄μ£Όλ©΄ λλ€
select createdAt, content
from simple_todos as todos
order by createdAt, title
LIMIT 1700000, 10;
ALTER TABLE simple_todos
ADD INDEX idx_createdAt_title (createdAt, title);
κ·ΈλΌ μ λ ¬ κ³Όμ μμ sort buffer λ temp file io λ₯Ό ν΅ν μ λ ¬μ΄ μ¬λΌμ§κ³ μ΄λ―Έ μ λ ¬λ index μμλλ‘ λ°νμ νκ²λ κ²μ΄λ€.
λ³΅ν© μΈλ±μ€ λ° covering index μ μ© ν μ±λ₯ λΉκ΅
170λ§κ±΄μ λ°μ΄ν°λ₯Ό Ordering νμμ λμ μ±λ₯ μ°¨μ΄λ₯Ό λ΄λ³΄μ
μμ order by μ ν΄λΉνλ μ λ ¬ 쑰건μ λ³΅ν© μΈλ±μ€λ₯Ό κ±ΈκΈ° μ μ΄κ³ λ€λ index λ₯Ό μΆκ°ν νλ€.
7,585ms μμ 234ms λ‘ μ½ 32λ°°μ μλ μ°¨μ΄κ° λλ€
λ§μ§λ§! μ΄ λͺ¨λ μ΅μ νλ₯Ό λ€ μ μ©ν΄λ³΄μ
μ΅μ ν μ 쿼리
μλμ 쿼리λ μ΅μ νλ₯Ό νκΈ° μ μ 쿼리λ€.
select *
from simple_todos as todos
order by createdAt, title
LIMIT 1700000, 10;
μ΅μ ν ν 쿼리
μ΅μ νλ₯Ό λ§μΉ 쿼리μ΄λ€.
SELECT *
FROM simple_todos AS todos
JOIN (SELECT id
FROM simple_todos
ORDER BY createdAt,
title
LIMIT 1700000, 10) AS temp USING (id)
ORDER BY todos.createdAt, todos.title;
ALTER TABLE simple_todos
ADD INDEX idx_createdAt_title (createdAt, title);
μ°λ¦¬λ μ΄ μ΅μ νλ₯Ό μν΄ κ½€λ λ§μ κ²μ μ μ©νμλ€.
- buffering λ°©μμ μ΄μ© μ μλ λ°μ΄ν° λ²λ €μ§(discard) λ₯Ό μν΄ deferred join μ μ μ©νλ€.
- λΆνμν Disk I/O λ₯Ό μμ κΈ° μν΄ covering index λ₯Ό μ μ©νμλ€
- filesort λ₯Ό index sort λ‘ λ°κΎΈκΈ° μν΄ μ λ ¬ 쑰건μ composite index λ₯Ό μ μ©νμλ€
κ·Έ κ²°κ³Ό κΈ°μ‘΄ λλΉ μ½ 62λ°° λΉ¨λΌμ§ κ²°κ³Όλ₯Ό μ»μ μ μμλ€
μ΄ μλλ μλ§ λ°μ΄ν° μμ΄ λ§μμ§ μλ‘, νμ΄μ§κ° κΉμ΄μ§μλ‘ λ λμ΄λ κ²μ΄λ€.
κ²°λ‘
μ΄λ κ² μ¬λ¬κ°μ§ μ₯μΉλ₯Ό ν΄λμμ§λ§ κ²°κ΅ κ²μ 쑰건, μ λ ¬ 쑰건λ§λ€ μΈλ±μ€λ₯Ό κ±°λκ²μ μ¬μ€μ λ§€μ° μ΄λ ΅λ€.
λ λ€λ₯Έ λ¬Έμ λ μ μ΅μ ν μΏΌλ¦¬κ° μ΄λ¬ν μ§μμ΄ μμ΄ λ³Έλ€λ©΄ λ§€μ° λ³΅μ‘ν μΏΌλ¦¬λ‘ λ³΄μΌ κ²μ΄λ€.
μ΄μ© μ μλ μν©μ μ μΈνκ³ λ κ°λ₯νλ€λ©΄ cursor κΈ°λ° νμ΄μ§μΌλ‘ κ°λ κ²λ μ’μ μ νμ΄λ€.
μ΄λ κ² μμ κΈκ³Ό μ΄λ² κΈμ ν΅ν΄μ λ°μ΄ν°λ² μ΄μ€μ μ¬λ¬κ°μ§ ν€μλμ κΈ°μ μ λν΄μ μμ보μλ€.
λ΄μ©μ λ§₯λ½μ μ§ν€κΈ° μν΄μ μ¬λ¬ ν€μλλ€μ λν΄ μ νμ μΌλ‘ μ€λͺ μ νκΈ° λλ¬Έμ κ΄λ ¨ν΄μλ λ κΉκ² νμ΅νλ κ²μ μΆμ²νλ©° κΈμ λ§λ¬΄λ¦¬νλ€.
'π¬web application > - Database' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
100λ§κ° 1000λ§κ°μ λλ―Έλ°μ΄ν°λ₯Ό μμ±νλ κ°μ₯ μ¬μ΄ λ°©λ² (1) | 2024.10.20 |
---|---|
μ€νμ νμ΄μ§μ΄ λλ¦° μ§μ§ μ΄μ (5) | 2023.11.19 |
[λ°μ΄ν°λ² μ΄μ€] Isolation Level, κ³ λ¦½ μμ€ (0) | 2021.04.07 |
[λ°μ΄ν°λ² μ΄μ€] Transaction, νΈλμμ μ΄λ? (0) | 2021.04.07 |
[SQLλ¬Έ κΈ°μ΄] DML λ°μ΄ν° μ‘°μμ΄ :: INSERT, UPDATE, DELETE (0) | 2020.07.07 |
λκΈ