Redis κΈ°λ°μ μΊμ± λ° λκΈ°μ΄ κ΄λ¦¬λ₯Ό ν΅ν μ½μνΈ μμ½ μλΉμ€ μ±λ₯ κ°μ
λ€μ΄κ°λ©°
μ΄λ² μκ°μλ μ½μνΈ μμ½ μλΉμ€μ μ±λ₯μ κ°μ νκΈ° μν΄ νμ¬ μλ리μ€μ μ‘°ν API μ€ μΊμ±μ μ μ©ν λΆλΆμ λν΄ κ³ λ―Όν΄ λ³΄κ³ , κΈ°μ‘΄ RDBμμ μλλκ³ μλ λκΈ°μ΄ λ‘μ§μ Redisλ‘ μ΄κ΄νλ κ³Όμ μ λν΄ μ΄ν΄λ³΄κ² μ΅λλ€. λ μΊμ± μ ν ν μ€νΈ κ²°κ³Ό λΉκ΅λ₯Ό ν΅ν΄ μΌλ§λ μ±λ₯μ΄ κ°μ λλμ§λ 체ν¬ν΄ 보λλ‘ νκ² μ΅λλ€.
μΊμ μ μ© κΈ°μ€
μ°μ μΊμλ₯Ό μ μ©ν λ κ³ λ €ν΄μΌ ν κΈ°μ€μ μλμ κ°μ΅λλ€.
1. μ‘°ν λΉμ©μ΄ λμμ§
λ°μ΄ν°λ² μ΄μ€μμ λ°μ΄ν°λ₯Ό μ‘°ννλ λΉμ©μ΄ ν° κ²½μ° μΊμ±μ ν΅ν΄ μ±λ₯μ κ°μ ν μ μμ΅λλ€.
2. μΌλ§λ μμ£Ό μ‘°νλλμ§
λ°λ³΅μ μΈ μμ²μ΄ λ§μ κ²½μ° μΊμλ₯Ό νμ©νμ¬ ν¨μ¨μ±μ λμΌ μ μμ΅λλ€.
3. λ°μ΄ν° μ ν©μ±μ μ΄μκ° μλμ§
μΊμλ₯Ό μ¬μ©ν κ²½μ°, μΊμλ λ°μ΄ν°μ μλ³Έ λ°μ΄ν° κ°μ μ ν©μ±μ μ μ§ν μ μλμ§ νμΈν΄μΌ ν©λλ€. μΊμλ₯Ό μ μ ν κ°±μ ν μ μλ€λ©΄ μΊμ±μ μ μ©νλ κ²μ΄ μ’μ΅λλ€.
μΊμ μ μ©μ κ³ λ €ν λλ μΊμ ννΈμ¨
μ΄ κ°μ₯ μ€μν μμμ
λλ€. λμ ννΈμ¨μ μ μ§νλ κ²μ΄ μΊμ± ν¨κ³Όλ₯Ό κ·Ήλννλ λ° νμν©λλ€. μ΄λ₯Ό ν΅ν΄ λ°μ΄ν°λ² μ΄μ€ λΆνλ₯Ό μ€μ΄κ³ , μμ€ν
μ μλ΅ μλλ₯Ό ν¬κ² κ°μ ν μκ° μμ΅λλ€.
μ‘°ν API λ³ λΆμ
νμ¬ μ½μνΈ μμ½ μλΉμ€μλ λ€μν μ‘°ν APIκ° μ‘΄μ¬ν©λλ€. κ° APIμ λν λΆμκ³Ό μΊμ μ μ© μ¬λΆλ₯Ό νλ¨ν΄λ³΄κ² μ΅λλ€.
1. μ½μνΈ μ 보 λ° λ μ§ λͺ©λ‘ μ‘°ν API β
νΉμ μ½μνΈμ μ 보μ μμ½ κ°λ₯ν λ μ§λ₯Ό μ‘°ννλ APIμ λλ€.
- μ‘°ν λΉμ© : λ°μ΄ν° μμ΄ ν¬κ³ 볡μ‘νκΈ° λλ¬Έμ μ‘°ν λΉμ©μ΄ ν½λλ€.
- μ‘°ν λΉλ : μ¬μ©μλ€μ΄ μ½μνΈλ₯Ό κ²μν λ μμ£Ό μμ²λλ APIμ λλ€.
- λ°μ΄ν° μ ν©μ± : λ μ§ μ λ³΄κ° μμ£Ό λ³κ²½λμ§ μμ μΊμ± μ μ©μ΄ μ λΉν©λλ€. λ€λ§, λ μ§λ³ μμ½ κ°λ₯ μ¬λΆλ μ€μκ°μΌλ‘ λ³κ²½λλ―λ‘ μμ½ κ°λ₯ μ’μ μλ₯Ό λκΈ°ννλ μ€μΌμ€λ§ μ£ΌκΈ°μ λ§μΆ° μ ν©μ±μ λ§μΆ μ μμ΅λλ€.
2. λκΈ°μ΄ μν μ‘°ν API β
μ¬μ©μκ° λκΈ°μ΄μμ λͺ λ²μ§Έμ μμΉν΄ μλμ§λ₯Ό νμΈνλ APIμ λλ€.
- μ‘°ν λΉμ© : λκΈ°μ΄ μμ μ 보 μ‘°ν μ체λ κ°λ²Όμ°λ, μ€μκ° λ°μ΄ν°κ° νμν©λλ€.
- μ‘°ν λΉλ : λκΈ° μ€μΈ μ¬μ©μκ° μμ£Ό μ‘°ννμ§λ§, νμ μ΅μ μ λ³΄κ° νμν©λλ€.
- λ°μ΄ν° μ ν©μ± : μ€μκ° μ λ³΄κ° νμνμ¬ μΊμ±μ μ μ©ν κ²½μ° μ ν©μ± λ¬Έμ κ° λ°μν μ μμ΅λλ€.
3. μ’μ μν μ‘°ν API β
νΉμ μ½μνΈ λ μ§μ μ’μ μ 보λ₯Ό μ‘°ννλ APIμ λλ€.
- μ‘°ν λΉμ© : μ’μ μνλ 볡μ‘ν μ 보μ΄λ―λ‘ μ‘°ν λΉμ©μ΄ λΉκ΅μ ν½λλ€.
- μ‘°ν λΉλ : μ’μ μνλ μμ½μ΄ μ§νλ λ μμ£Ό μ‘°νλ©λλ€.
- λ°μ΄ν° μ ν©μ± : μμ½μ΄ μμ£Ό λ³κ²½λκΈ° λλ¬Έμ μ ν©μ± μ μ§κ° μ΄λ €μΈ μ μμ΅λλ€.
4. μμ‘ μ‘°ν API β
νΉμ μ¬μ©μμ νμ¬ μμ‘μ μ‘°ννλ APIμ λλ€.
- μ‘°ν λΉμ© : μμ‘ μ‘°ν μ체λ κ°λ¨νμ¬ λΉμ©μ΄ ν¬μ§ μμ΅λλ€.
- μ‘°ν λΉλ : μ¬μ©μκ° μΆ©μ λλ κ²°μ μ νλ‘ μμ μ μμ‘μ μμ£Ό νμΈν μ μμ΅λλ€.
- λ°μ΄ν° μ ν©μ± : μμ‘ μ 보λ μ€μκ°μ±μ΄ μꡬλ μ μμ§λ§, μΆ©μ /κ²°μ μ μΊμλ₯Ό μ μ ν κ°±μ νλ€λ©΄ μΊμ±μ μ μ©ν μλ μμ΅λλ€.
μ΄ API μ€, μ½μνΈ μ 보 λ° λ μ§ λͺ©λ‘ μ‘°νλ λ°μ΄ν° μμ΄ ν¬κ³ κ°±μ μ£ΌκΈ°κ° λΉκ΅μ λ리며 λμΌν μμ²μ΄ λ°λ³΅λ κ°λ₯μ±μ΄ λκΈ° λλ¬Έμ μΊμ± μ μ©μ νμμ±μ΄ ν¬λ€κ³ μκ°λ©λλ€. λ°λ©΄ λκΈ°μ΄ μν μ‘°ν, μ’μ μν μ‘°νλ μ€μκ°μ±μ μꡬνκ³ μμ‘ μ‘°νλ λΉμ©μ΄ λΉκ΅μ ν¬μ§ μκΈ° λλ¬Έμ μΊμ μ μ© λμμ λ£μ§ μμμ΅λλ€.
λ€λ§, λκΈ°μ΄ μν μ‘°νλ Redisλ‘ λκΈ°μ΄ κ΄λ¦¬λ₯Ό νκΈ° λλ¬Έμ μΊμ±μ΄ μλμ΄λ λΉ λ₯Έ μ‘°ν λ°©μμΌλ‘ κ°μ ν μ μμ΅λλ€.
μΊμ μ ν μ±λ₯ λΉκ΅
μΊμ μ μ© μ νμ μ±λ₯μ λΉκ΅νκΈ° μν΄ Grafanaμ k6λ₯Ό νμ©νμ¬ λΆν ν μ€νΈλ₯Ό μ§ννμ΅λλ€.
ν μ€νΈ κ²°κ³Ό, μΊμ μ μ© μ μλ νκ· 1.56, μ΅λ 10μ νμ± μ»€λ₯μ μ΄ μμμ§λ§, μΊμ μ μ© νμλ νμ± μ»€λ₯μ μ΄ 0μΌλ‘ μ€μ΄λ€μ΄ λ°μ΄ν°λ² μ΄μ€μ λν μ§μ μ μΈ μμ²μ΄ κ±°μ μμμ΅λλ€. λν, λμ μμ²μ΄ λ§μ λ ν μ¬μ΄μ¦μ νκ³μ λλ¬ν΄ λκΈ° 컀λ₯μ μ΄ λ°μνλ κ²κ³Ό λΉκ΅ν΄, μΊμ μ μ© νμλ λκΈ° 컀λ₯μ μ΄ μμμ΅λλ€.
μΊμ μ μ© ν, λμΌ μκ° λ΄ μμ² μ²λ¦¬λμ΄ μν μ¦κ°νμΌλ©°, μλ²μ λ¨κ±΄ μμ² μ²λ¦¬ μκ°λ μ§§μμ§ κ²μ νμΈνμ΅λλ€.
μλλ μ£Όμ κ²°κ³Όλ₯Ό μμ½ν νμ Grafana κ²°κ³Ό μΊ‘μ³μ λλ€.
νλͺ© | μΊμ μ μ© μ | μΊμ μ μ© ν |
HikariCP νμ± μ»€λ₯μ (νκ· ) | 1.56 | 0.0 |
HikariCP νμ± μ»€λ₯μ (μ΅λ) | 10.0 | 0.0 |
HikariCP λκΈ° 컀λ₯μ (νκ· ) | 0.222 | 0.0 |
HikariCP λκΈ° 컀λ₯μ (μ΅λ) | 2.0 | 0.0 |
API μμ² μ (νκ· ) | 183.0 | 198.0 |
API μμ² μ (μ΅λ) | 453.0 | 486.0 |
HTTP μμ² μ§μ μκ° (νκ· ) | 14.1 | 13.08 |
HTTP μμ² μ§μ μκ° (μ΅λ) | 264.95 | 182.45 |
ν μ€νΈ κ²°κ³Ό ( μΊμ μ μ© μ )
ν μ€νΈ κ²°κ³Ό ( μΊμ μ μ© ν )
μ΄λ₯Ό ν΅ν΄ μΊμ±μ΄ λ°μ΄ν°λ² μ΄μ€ λΆνλ₯Ό μ€μ΄κ³ μλ΅ μκ°μ λ¨μΆμν€λ λ° κΈ°μ¬νλ κ²μ νμΈν μ μμμ΅λλ€.
λκΈ°μ΄ λ‘μ§μ RDBμμ Redisλ‘ μ΄κ΄
κΈ°μ‘΄μ λκΈ°μ΄ λ‘μ§μ RDBλ₯Ό μ¬μ©νμ¬ κ΄λ¦¬νκ³ μμμ΅λλ€. νμ§λ§ μ΄ λ°©μμ μ€μκ° μ±λ₯μ μꡬνλ λκΈ°μ΄ μμ€ν μ μ ν©νμ§ μμμΌλ©°, λμ©λ νΈλν½ μ²λ¦¬ μ λ°μ΄ν°λ² μ΄μ€μ λμ λΆνλ₯Ό μ λ°ν μ μμ΅λλ€. μ΄μ λ°λΌ λκΈ°μ΄ λ‘μ§μ Redisλ‘ μ΄κ΄νμμ΅λλ€.
Redisλ‘ μ΄κ΄νλ©΄μ ActiveToken(μμ½ κ°λ₯ νμ± ν ν°)κ³Ό WaitingToken(λκΈ° ν ν°)μ λΆλ¦¬νμ¬ κ΄λ¦¬νλ λ°©ν₯μ μ€μ νμμ΅λλ€. κ° λκΈ°/νμ± ν ν°μ μ½μνΈ μ€μΌμ€ IDλ₯Ό Keyλ‘ κ°μ§λ ZSet
(Sorted Set)μΌλ‘ κ΄λ¦¬νμμ΅λλ€.
μ΄λ₯Ό ν΅ν΄, WaitingTokenμμλ ZRANK
λͺ
λ Ήμ΄λ₯Ό μ¬μ©νμ¬ O(log(N))
μ μκ°λ³΅μ‘λλ‘ μμλ₯Ό κ°μ Έμ¬ μ μμ΅λλ€. λν ActiveTokenμμλ Scoreμ λ§λ£ μκ°μ κΈ°λ‘νμ¬ ZREMRANGEBYSCORE
λͺ
λ Ήμ΄λ‘ λ§λ£λ ν ν°μ λΉ λ₯΄κ² μμ ν μ μμ΅λλ€.
λ κ° ν ν°μ λ³λμ Hash
κ΅¬μ‘°λ‘ μ μ₯νμ¬, ν ν°μ keyλ‘ μ¬μ©νκ³ valueμλ μ€μΌμ€ ID, μνκ°, λ§λ£μΌμλ₯Ό μ μ₯νλλ‘ κ΅¬μ±νμ΅λλ€. μ΄λ₯Ό ν΅ν΄ νΉμ ν ν°μ λν μ‘°ν μ λ°μ΄ν° μ κ·Ό μλλ₯Ό ν¬κ² ν₯μμν¬ μ μμμ΅λλ€.
μ½λ λ³κ²½μ μΌλ‘λ κΈ°μ‘΄ JPAλ₯Ό μ¬μ©νλ λ‘μ§μ RedisTemplateλ₯Ό μ¬μ©ν λ‘μ§μΌλ‘ λ³κ²½νμμΌλ©°, λ§λ£ μκ° κ΄λ¦¬μ μν λ³κ²½ μμ Redisλ‘ μ²λ¦¬ν μ μλλ‘ λ¦¬ν©ν°λ§ νμμ΅λλ€.
μλλ ν ν° λ°κΈ μ RedisTemplate
μ μ¬μ©νμ¬ Redisμ μ μ₯νλλ‘ λ³κ²½ν μ½λ μμμ
λλ€.
@Repository
class WaitingQueueRedisRepository(
private val redisTemplate: RedisTemplate<String, Any>,
) : WaitingQueueRepository {
companion object {
const val WAITING_TOKEN_PREFIX = "WaitingToken"
const val ACTIVE_TOKEN_PREFIX = "ActiveToken"
const val TOKEN_INFO_PREFIX = "TokenInfo"
}
override fun addWaitingQueue(waitingQueue: WaitingQueue): WaitingQueue {
// 1. WaitingToken μ μ₯
redisTemplate.opsForZSet().add(
"$WAITING_TOKEN_PREFIX:${waitingQueue.scheduleId}",
waitingQueue.token,
System.currentTimeMillis().toDouble(),
)
redisTemplate.expire("$WAITING_TOKEN_PREFIX:${waitingQueue.scheduleId}", Duration.ofHours(1))
// 2. TokenInfo μ μ₯
val key = "$TOKEN_INFO_PREFIX:${waitingQueue.token}"
val fields =
mapOf(
"scheduleId" to waitingQueue.scheduleId.toString(),
"status" to waitingQueue.status.name,
)
redisTemplate.opsForHash<String, String>().putAll(key, fields)
redisTemplate.expire(key, Duration.ofHours(1))
return waitingQueue
}
/* ... */
}
λ§μΉλ©°
μ½μνΈ μμ½ μλΉμ€μ μ±λ₯μ κ°μ νκΈ° μν΄ μ‘°ν APIμ μΊμ±μ μ μ©νκ³ , λκΈ°μ΄ λ‘μ§μ Redisλ‘ μ΄κ΄νλ κ³Όμ μ μ λ¦¬ν΄ λ³΄μμ΅λλ€. μΊμ±μ ν΅ν΄ λ°λ³΅μ μΈ μ‘°ν μμ²μ μ±λ₯μ κ°μ νκ³ , Redisλ₯Ό νμ©νμ¬ λκΈ°μ΄ λ‘μ§μ μ€μκ° μ±λ₯μ ν₯μμν¬ μ μμμ΅λλ€. μ΄λ² κ³Όμ μ ν΅ν΄ μΊμ±μ μ μ©νλ κΈ°μ€κ³Ό Redisλ₯Ό νμ©ν μ±λ₯ μ΅μ ν κΈ°λ²μ ν λλ‘ μΆνμ μ€λ¬΄μ μ μ©ν΄ λ³Ό μ μμ κ² κ°μ΅λλ€.