λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
  • μž₯원읡 κΈ°μˆ λΈ”λ‘œκ·Έ
πŸ”¬web application/- Database

μ˜€ν”„μ…‹ νŽ˜μ΄μ§•, λ‹¨κ³„λ³„λ‘œ μ΅œμ ν™”ν•˜κΈ°

by Wonit 2023. 11. 19.

 

이 글은 offset based pagination 을 빌미둜 λ°μ΄ν„°λ² μ΄μŠ€μ˜ λ‚΄λΆ€ λ™μž‘κ³Ό μ—¬λŸ¬ κ°œλ…λ“€μ„ μ•Œμ•„λ³΄λŠ” κΈ€ μž…λ‹ˆλ‹€.

 

2개의 κΈ€λ‘œ κ΅¬μ„±λ˜μ–΄ 있고, 각각의 κΈ€μ—μ„œ 얻을 수 μžˆλŠ” insight κ°€ λ‹€λ₯΄λ‹ˆ ν•¨κ»˜ 읽으면 λ”μš± μœ μ΅ν•©λ‹ˆλ‹€.

 

  1. μ˜€ν”„μ…‹ νŽ˜μ΄μ§•μ΄ 느린 μ§„μ§œ 이유
  2. μ˜€ν”„μ…‹ νŽ˜μ΄μ§•, λ‹¨κ³„λ³„λ‘œ μ΅œμ ν™”ν•˜κΈ° <- ν˜„μž¬ κΈ€

 

μ§€λ‚œ μ‹œκ°„, μš°λ¦¬λŠ” μ˜€ν”„μ…‹ νŽ˜μ΄μ§•μ΄ 느린 μ§„μ§œ 이유 에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜λ‹€.

 

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 μž‘μ—… μˆ˜ν–‰ν•˜μ—¬ κ²°κ³Ό ν…Œμ΄λΈ” 생성

 

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 기반 νŽ˜μ΄μ§•μœΌλ‘œ κ°€λŠ” 것도 쒋은 선택이닀.

 

μ΄λ ‡κ²Œ μ•žμ„  κΈ€κ³Ό 이번 글을 ν†΅ν•΄μ„œ λ°μ΄ν„°λ² μ΄μŠ€μ˜ μ—¬λŸ¬κ°€μ§€ ν‚€μ›Œλ“œμ™€ κΈ°μˆ μ— λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜λ‹€.

 

λ‚΄μš©μ˜ λ§₯락을 지킀기 μœ„ν•΄μ„œ μ—¬λŸ¬ ν‚€μ›Œλ“œλ“€μ— λŒ€ν•΄ μ œν•œμ μœΌλ‘œ μ„€λͺ…을 ν–ˆκΈ° λ•Œλ¬Έμ— κ΄€λ ¨ν•΄μ„œλŠ” 더 깊게 ν•™μŠ΅ν•˜λŠ” 것을 μΆ”μ²œν•˜λ©° 글을 λ§ˆλ¬΄λ¦¬ν•œλ‹€.

λŒ“κΈ€