JiwonDev

์•ˆ์ • ํ•ด์‹œ(Consistent Hashing)

by JiwonDev

 

 

 

๐Ÿ€ ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•ด ์š”์ฒญ์„ ๋ถ„์‚ฐํ•˜๋Š” ๋ฐฉ๋ฒ•

N๊ฐœ์˜ ์บ์‹œ ์„œ๋ฒ„๊ฐ€ ์žˆ์„ ๋•Œ ๋ถ€ํ•˜๋ฅผ ๋‚˜๋ˆ„๋Š” ๋ณดํŽธ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

MD5 (2^128): ์†๋„๊ฐ€ ๋ณด์•ˆ๋ณด๋‹ค ์ค‘์š”ํ•œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜. SHA-256 ์— ๋น„ํ•ด ํ•ด์‹œ ๊ณต๊ฐ„์€ ์ž‘๋‹ค.
SHA-256 (2^256): ๋” ๊ธด ํ•ด์‹œ ํฌ๊ธฐ์™€ ๋” ๊ฐ•๋ ฅํ•œ ์•”ํ˜ธํ™” ์†์„ฑ. ์†๋„๋Š” MD5 ์— ๋น„ํ•ด ๋Š๋ฆฌ๋‹ค. ๋งค์šฐ ํฐ ํ•ด์‹œ ๊ณต๊ฐ„์„ ๊ฐ–๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋Œ์ด ๊ฑฐ์˜ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

serverIndex = hash(key) % N (์„œ๋ฒ„๊ฐœ์ˆ˜)

 

์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์€ ๋ชจ๋“  ์ƒํ™ฉ์—์„œ ์ž˜ ๋™์ž‘ํ•  ๊ฒƒ ๊ฐ™์ง€๋งŒ ์„œ๋ฒ„ ๊ฐœ์ˆ˜ (server pool)์ด ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•œ์ชฝ์— ๋ชฐ๋ฆฌ๊ฒŒ๋˜์—ˆ์„ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ํ‚ค๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ์žฌ๋ฐฐ์น˜ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋ถ€ํ•˜๊ฐ€ ๋ชฐ๋ฆฌ๊ฒŒ ๋˜๊ณ  ๋Œ€๊ทœ๋ชจ์˜ ์บ์‹œ ๋ฏธ์Šค(cache miss)๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์ฒ˜์Œ์—” ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ๊ฒ ์ง€๋งŒ, ์ง€๊ธˆ์€ ํ•ด์‹œํ‚ค๋ฅผ ์žฌ๋ฐฐ์น˜ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

 

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๋ ค๋ฉด ์„œ๋ฒ„๊ฐ€ ์ถ”๊ฐ€/์‚ญ์ œ ๋˜๋”๋ผ๋„ key ๋งคํ•‘ ๋ณ€๊ฒฝ์€ ์ตœ์†Œํ™” ๋˜์–ด์•ผ ํ•œ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ "์•ˆ์ • ํ•ด์‹œ" ์ด๋‹ค.

์•ˆ์ •ํ•ด์‹œ(Consistent Hashing)
MIT์—์„œ ์ฒ˜์Œ ์ œ์•ˆ ๋˜์—ˆ์œผ๋ฉฐ ํ•ด์‹œ ํ…Œ์ด๋ธ”์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ, ํ‰๊ท ์ ์œผ๋กœ ์˜ค์ง  k(ํ‚ค์˜๊ฐœ์ˆ˜) / n(slot์˜ ๊ฐœ์ˆ˜)  ๊ฐœ์˜ ํ‚ค๋งŒ ์žฌ๋ฐฐ์น˜ํ•˜๋Š” ๊ธฐ์ˆ ์ด๋‹ค. ์•ˆ์ •ํ•ด์‹œ๋กœ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š” ํ•ด์‹œํ•จ์ˆ˜๋“ค์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๋Š” ์Šฌ๋กฏ์˜ ์ˆ˜๊ฐ€ ์ค„์–ด๊ฑฐ๋‚˜ ๋Š˜์–ด๋‚˜๋ฉด ๋งŽ์€ ํ‚ค๊ฐ€ ์žฌ๋ฐฐ์น˜ ๋˜์•ผ ํ•œ๋‹ค. 

 

 

 

๐Ÿ€ ์•ˆ์ •ํ•ด์‹œ๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ• ๊นŒ

์˜ˆ๋ฅผ ๋“ค์–ด ํ•ด์‹œ ํ•จ์ˆ˜๋กœ SHA-1์„ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ํ•ด์‹œ ๊ณต๊ฐ„์€ 0 <= x < 2^160 ๊นŒ์ง€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์‹œ์ž‘๊ณผ ๋์„ ๋ถ™์ด๊ฒŒ๋˜๋ฉด ์›ํ˜• ๋ฆฌ์ŠคํŠธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

class ConsistentHash<T>(
private val hashFunction: (String) -> Long, // ์‚ฌ์šฉํ•  ํ•ด์‹œ ํ•จ์ˆ˜
private val circle: SortedMap<Long, T> = TreeMap() // ํ•ด์‹œ ๊ฐ’์„ ํ‚ค๋กœ ํ•˜๊ณ  ๋…ธ๋“œ๋ฅผ ๊ฐ’์œผ๋กœ ํ•˜๋Š” ์ •๋ ฌ๋œ ๋งต
){
/* ... */
}
// ํ•ด์‹œ ํ•จ์ˆ˜์˜ ์˜ˆ์ œ ๊ตฌํ˜„์ž…๋‹ˆ๋‹ค. Java์˜ MessageDigest๋ฅผ ์‚ฌ์šฉํ•ด ๋ฌธ์ž์—ด์˜ SHA-1 ํ•ด์‹œ ๊ฐ’์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
fun hashFunction(key: String): Long {
val md = MessageDigest.getInstance("SHA-1")
return md.digest(key.toByteArray())
.fold(0L, { acc, byte -> (acc shl 8) + (byte and 0xff) })
}
fun main() {
val consistentHash = ConsistentHash(::hashFunction)
}

 

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•œ ์›ํ˜• ๋ฆฌ์ŠคํŠธ์—์„œ ์„œ๋ฒ„(IP๋‚˜ ์„œ๋ฒ„ ์ด๋ฆ„)์„ ์ €์žฅํ•˜๊ณ  ์‚ฌ์šฉํ•  hash key ๋“ค์„ ์›ํ•˜๋Š” ์–ด๋Š ์ง€์ ์—๋‚˜ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌ์„ฑํ•œ๋‹ค.

  • ์ด ๋•Œ ๊ธฐ์กด์˜ ํ•ด์‹œ๊ฐ€ ์•„๋‹Œ ๊ท ๋“ฑ ๋ถ„ํฌ (uniform distribution) ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์›ํ˜•์— ๊ณจ๊ณ ๋ฃจ ๋ถ„ํฌ์‹œํ‚จ๋‹ค.

 

 

 

์ด๋Ÿฐ ์‹์œผ๋กœ ์›ํ˜•์œผ๋กœ ๊ตฌ์„ฑํ•˜๊ฒŒ๋˜๋ฉด ์ถ”๊ฐ€/์‚ญ์ œ์—์„œ ๊ธฐ์กด์˜ ํ‚ค ๋งคํ•‘ ๋ณ€๊ฒฝ์ด ์ตœ์†Œํ™” ๋˜๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์„œ๋ฒ„ ์กฐํšŒ
    ํ•ด๋‹น hash key๋กœ๋ถ€ํ„ฐ ์‹œ๊ณ„๋ฐฉํ–ฅ์œผ๋กœ ์„œ๋ฒ„๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ ๋งŒ๋‚œ ์ฒซ๋ฒˆ์งธ ์„œ๋ฒ„๋ฅผ ์กฐํšŒํ•œ๋‹ค.
  • ์„œ๋ฒ„ ์ถ”๊ฐ€
    ์„œ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•˜๋”๋ผ๋„ ์‹œ๊ณ„๋ฐฉํ–ฅ์œผ๋กœ hash key๋ฅผ ์‚ฌ์šฉํ–ˆ๋˜ ๊ธฐ์กด 1๊ฐœ์˜ ์„œ๋ฒ„๋งŒ ํ‚ค๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค. ๋‹ค๋ฅธ ํ‚ค๋Š” ์žฌ๋ฐฐ์น˜ ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ์„œ๋ฒ„ ์‚ญ์ œ
    ์„œ๋ฒ„ ์‚ญ์ œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ธฐ์กด ์กฐํšŒ ๊ทœ์น™์— ๋”ฐ๋ผ ๊ฐ€์šด๋ฐ ์ผ๋ถ€๋งŒ ํ‚ค๋งŒ ์žฌ๋ฐฐ์น˜ ๋˜๊ฒŒ ๋œ๋‹ค. 
๊ฐ ์„œ๋ฒ„(๋…ธ๋“œ) ์‚ฌ์ด์— ์žˆ๋Š” key๋งŒ ์žฌ๋ฐฐ์น˜ํ•˜๋ฉด ๋œ๋‹ค. ์„œ๋ฒ„๊ฐ€ ์ถ”๊ฐ€/์‚ญ์ œ ๋˜๋”๋ผ๋„ key ์žฌ๋ฐฐ์น˜๊ฐ€ ์ตœ์†Œํ™” ๋œ๋‹ค.

 

class ConsistentHash<T>(
private val hashFunction: (String) -> Long, // ํ•ด์‹œ ํ•จ์ˆ˜
private val circle: SortedMap<Long, T> = TreeMap() // ํ•ด์‹œ ๊ฐ’์„ ํ‚ค๋กœ ํ•˜๊ณ  ๋…ธ๋“œ๋ฅผ ๊ฐ’์œผ๋กœ ํ•˜๋Š” ์ •๋ ฌ๋œ ๋งต
) {
fun addNode(node: T) {
// ๋…ธ๋“œ๋ฅผ ํ•ด์‹œ๋ง์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
val hash = hashFunction(node.toString())
circle[hash] = node
}
fun removeNode(node: T) {
// ๋…ธ๋“œ๋ฅผ ํ•ด์‹œ๋ง์—์„œ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
val hash = hashFunction(node.toString())
circle.remove(hash)
}
fun getNode(key: String): T? {
// ์ฃผ์–ด์ง„ ํ‚ค์— ๋Œ€ํ•œ ๋…ธ๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
if (circle.isEmpty()) return null
val hash = hashFunction(key)
// ์ฃผ์–ด์ง„ ํ•ด์‹œ ๊ฐ’์„ ์กฐํšŒํ•˜์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ, ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๋‹ค์Œ ํ‚ค๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
if (!circle.containsKey(hash)) {
// key ๋ณด๋‹ค ์ˆœ์„œ๊ฐ€ ๊ฐ™๊ฑฐ๋‚˜ ํฐ ์„œ๋ธŒ ๋งต ์ƒ์„ฑ(tailMap)
val tailMap = circle.tailMap(hash)
// ๋‹ค์Œ ์ˆœ์„œ๊ฐ€ ์—†๋‹ค๋ฉด ํ•œ ๋ฐ”ํ€ด ๋Œ์•„ ์ฒซ๋ฒˆ์งธ ์ˆœ์„œ(circle.firstKey()) ์‚ฌ์šฉ
hash = tailMap.firstKey() ?: circle.firstKey()
}
return circle[hash]
}
}

 

 

๐Ÿ€ ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•œ ์•ˆ์ •ํ•ด์‹œ๋Š” ๋‹จ์ ์ด ์—†์„๊นŒ?

  • ์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๊ฐ ์„œ๋ฒ„ ์‚ฌ์ด์˜ ๋นˆ ๊ณต๊ฐ„(partition)์„ ํ•ญ์ƒ ๊ท ๋“ฑํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋Š”๊ฒŒ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ๊ท ๋“ฑ ๋ถ„ํฌ ํ•ด์‹œํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ํ‚ค ๋ถ„ํฌ ์ž์ฒด๊ฐ€ ํ•œ์ชฝ ๋นˆ ๊ณต๊ฐ„(partition)์— ๋ชฐ๋ฆด ์ˆ˜๋„ ์žˆ๋‹ค.
๊ทธ๋ƒฅ ํ•ด์‹œ๋ฅผ ์“ฐ๋Š” ๊ฒƒ ๋ณด๋‹จ ๋‚ซ๊ฒ ์ง€๋งŒ, ๋ฌธ์ œ๊ฐ€ ์™„๋ฒฝํ•˜๊ฒŒ ํ•ด๊ฒฐ๋œ ๊ฑด ์•„๋‹ˆ๋‹ค.

 

 

๐Ÿ”‘ ํ•ด๊ฒฐ์ฑ…: ๊ฐ€์ƒ ๋…ธ๋“œ(virtual node) ์‚ฌ์šฉ

์‹ค์ œ ์„œ๋ฒ„(๋…ธ๋“œ)๋ฅผ 1:1๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ๊ฐ€์ƒ์˜ ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ ์‚ฌ์šฉํ•œ๋‹ค. ์„œ๋ฒ„ 1๋Œ€๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ฐ€์ƒ ๋…ธ๋“œ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค.

  • ๊ฐ€์ƒ ๋…ธ๋“œ์˜ ๊ฐœ์ˆ˜๋ฅผ ๋Š˜๋ฆผ์œผ๋กœ์„œ ํ‚ค์˜ ๋ถ„ํฌ๋ฅผ ๊ท ๋“ฑํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ๋…ธ๋“œ์˜ ๊ฐœ์ˆ˜๊ฐ€ ๋งŽ์„ ์ˆ˜๋ก ํ‘œ์ค€ํŽธ์ฐจ๊ฐ€ ์ ์–ด์ ธ ๋” ๊ท ๋“ฑํ•ด์ง€๊ฒ ์ง€๋งŒ ๊ทธ๋งŒํผ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๊ณ  ์ถ”๊ฐ€/์‚ญ์ œ์‹œ ๊ฐ€์ƒ๋…ธ๋“œ๋ฅผ ๋งŽ์ด ์ •๋ฆฌํ•ด์•ผํ•œ๋‹ค. ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„๋ฅผ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค.
  • ์ฐธ๊ณ ๋กœ ๊ฐ€์ƒ ๋…ธ๋“œ์˜ ์ˆ˜๋Š” ๋ฌดํ•œ์ • ๋Š˜๋ฆด ์ˆ˜ ์—†๋‹ค. ํ•„์š”์— ๋”ฐ๋ผ ์ „์ฒด ๋…ธ๋“œ ์ˆ˜๋Š” ์ค„์ด๊ณ  ์„ฑ๋Šฅ์ด ์ข‹์€ ์„œ๋ฒ„๊ฐ€ ๋งŽ์€ ๋…ธ๋“œ ์ˆ˜๋ฅผ ๊ฐ€์ ธ๊ฐ€๊ฒŒ๋” ๊ตฌํ˜„ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.
// ๊ฐ€์ƒ ๋…ธ๋“œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํด๋ž˜์Šค
class VirtualNode<T>(
private val physicalNode: T,
private val replicaIndex: Int
) {
fun getPhysicalNode(): T = physicalNode // ๋ฌผ๋ฆฌ ๋…ธ๋“œ (์‹ค์ œ ์„œ๋ฒ„)
fun getKey(): String = "$physicalNode#$replicaIndex" // ๊ฐ€์ƒ ๋…ธ๋“œ์˜ ๊ณ ์œ  ํ‚ค(ํ•ด์‹œ์— ์‚ฌ์šฉ)
fun isVirtualOf(node: T): Boolean = this.physicalNode == node
override fun toString(): String = getKey()
}
class ConsistentHash<T>(
private val hashFunction: (String) -> Long,
private val numberOfReplicas: Int,
private val circle: SortedMap<Long, VirtualNode<T>> = TreeMap()
) {
fun addNode(node: T) {
for (i in 0 until numberOfReplicas) {
val virtualNode = VirtualNode(node, i)
circle[hashFunction(virtualNode.getKey())] = virtualNode
}
}
fun removeNode(node: T) {
circle.entries.removeIf { it.value.isVirtualOf(node) }
}
fun getNode(key: String): T? {
if (circle.isEmpty()) return null
var hash = hashFunction(key)
if (!circle.containsKey(hash)) {
val tailMap = circle.tailMap(hash)
hash = tailMap.firstKey() ?: circle.firstKey()
}
return circle[hash]?.getPhysicalNode()
}
}

 

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•จ์œผ๋กœ์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์ ์„ ์ฑ™๊ธธ ์ˆ˜ ์žˆ๋‹ค.

  • ์„œ๋ฒ„ ์ถ”๊ฐ€/์‚ญ์ œ์—๋„ ํ‚ค ๋งคํ•‘ ๋ณ€๊ฒฝ์ด ์ตœ์†Œํ™”๋œ๋‹ค. cache miss ๋น„์œจ์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์„œ๋ฒ„ ์ถ”๊ฐ€/์‚ญ์ œ๋ฅผ ๊ต‰์žฅํžˆ ๋งŽ์ด ํ•˜๋”๋ผ๋„ ํ•œ์ชฝ์œผ๋กœ ๋ชฐ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ๊ท ๋“ฑํ•˜๊ฒŒ ๋ถ„ํฌ๋œ๋‹ค.
  • ์œ„ ์žฅ์ ์œผ๋กœ ์ˆ˜ํ‰์  ๊ทœ๋ชจ ํ™•์žฅ์ด ์‰ฌ์›Œ์ง€๊ณ  hot spot key (ํ•œ์ชฝ์œผ๋กœ ํ‚ค๊ฐ€ ๋ชฐ๋ฆฌ๋Š” ํ˜„์ƒ)์„ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

๐Ÿ€ ์ˆ˜์น˜์ ์œผ๋กœ ์•ˆ์ •ํ•ด์‹œ๋Š” ์–ผ๋งˆ๋‚˜ ์ข‹์•„์ง€๋Š”๊ฑธ๊นŒ?

์‹คํ—˜ ์ถœ์ฒ˜: https://songkg7.github.io/posts/Consistent-Hashing/


๋ผ์šฐํŒ…์„ ๋งŒ๋“ ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. ์—ฌ๊ธฐ์—์„œ key ๋Š” request url, ip ๋“ฑ์ด ๋  ๊ฒƒ์ด๊ณ  ํ‚ค์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์„œ๋ฒ„๋ฅผ ์ฐพ์•„์ฃผ์–ด์•ผํ•œ๋‹ค.

  • ์ด ๋•Œ key(์š”์ฒญ)์— ๋”ฐ๋ผ ์–ด๋””๋กœ ๋ผ์šฐํŒ… ๋˜์—ˆ๋Š”์ง€ ์บ์‹ฑํ•ด๋‘”๋‹ค. ์ดํ›„ ๋™์ผ ์š”์ฒญ์€ ๋ผ์šฐํŒ… ๊ฒฐ์ •๊ณผ์ •์„ ์ƒ๋žตํ•˜๊ณ  ๋ฐ”๋กœ ์กฐํšŒํ•œ๋‹ค.
  • ์บ์‹œ๊ฐ€ ์—†๊ฑฐ๋‚˜ ํ•ด๋‹น cache ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ์ •๋ณด๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ (๋…ธ๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ) ๋‹ค์‹œ ๋ผ์šฐํŒ… ๊ฒฐ์ •๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
ChatGPT์˜ ์นœ์ ˆํ•œ ์„ค๋ช…

 

 

 

# ๊ทธ๋ƒฅ ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ–ˆ์„ ๋•Œ

๊ทธ๋ƒฅ ํ•ด์‹œ ( https://songkg7.github.io/posts/Consistent-Hashing/ )

 

 

# ์•ˆ์ •ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ

์•ˆ์ •ํ•ด์‹œ(๋…ธ๋“œ1:1)

 

 

# ์•ˆ์ •ํ•ด์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ฐ€์ƒ ๋…ธ๋“œ ์ˆ˜๋ฅผ ๋Š˜๋ ค ํ‘œ์ค€ํŽธ์ฐจ๋ฅผ ์ค„์˜€์„ ๋•Œ

  • ๊ฐ€์ƒ ๋…ธ๋“œ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ฉด ํ™•์‹คํžˆ ๋” ๊ณจ๊ณ ๋ฃจ ๋ถ„์‚ฐ๋œ๋‹ค. ๋‹ค๋งŒ ๊ฐ€์ƒ ๋…ธ๋“œ ๊ฐœ์ˆ˜๊ฐ€ ์–ด๋Š์ •๋„ ์ฐจ๊ฒŒ๋˜๋ฉด ์ดํ›„ ์ˆ˜์‹ญ๋งŒ๊ฐœ๋ฅผ ๋Š˜๋ ค๋„ ๊ฒฐ๊ณผ๋Š” ํฐ ๋ณ€ํ™”๊ฐ€ ์—†๋‹ค๋Š”๊ฑธ ์ธ์ง€ํ•˜์ž. ์ด๊ฒƒ๋„ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„๋ฅผ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค. 
์•ˆ์ •ํ•ด์‹œ( ๋…ธ๋“œ1:1 ๋…ธ๋“œ1:10 ๋…ธ๋“œ1:100 )

 

 

 

๐Ÿ€ ์ด๋Ÿฌํ•œ ๊ธฐ์ˆ ๋“ค์€ ์‹ค์ œ๋กœ ์–ด๋””์— ๋งŽ์ด ์“ฐ์ผ๊นŒ?

 

Choosing the Right DynamoDB Partition Key | Amazon Web Services

September 2022: This post was reviewed and updated for accuracy. This blog post covers important considerations and strategies for choosing the right partition key for designing a schema that uses Amazon DynamoDB. Choosing the right partition key is an imp

aws.amazon.com

 

 

 

์ด๋ ‡๊ฒŒ ๋Œ€๊ทœ๋ชจ ์ผ€์ด์Šค๊ฐ€ ์•„๋‹ˆ๋”๋ผ๋„ ๋‹น์žฅ ์‚ฌ์šฉ์ž์ˆ˜๊ฐ€ ๋งŽ์•„์ ธ DB ์Šค์ผ€์ผ์—…์„ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค๋ฉด ํŒŒํ‹ฐ์…”๋‹์„ํ• ์ง€, ์ƒค๋”ฉ์„ ํ• ์ง€ ๊ณ ๋ฏผํ•˜๊ณ  ํ•œ๋‹ค๋ฉด ์–ด๋–ค key๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๋‚˜๋ˆ ์•ผํ• ์ง€๋ฅผ ๊ณ ๋ฏผํ•ด๋ณด์•„์•ผํ•œ๋‹ค. MiRO ์—”์ง€๋‹ˆ์–ด๋ง - ๋ฐ์ดํ„ฐ ์ƒค๋”ฉ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ์ ์ ˆํ•œ ํ•ด์‹œํ•จ์ˆ˜ ์„ ํƒ

๋‹น์—ฐํ•œ ๋ง์ด์ง€๋งŒ Hash๊ฐ€ ์ •๋‹ต์ด ์•„๋‹ ์ˆ˜๋„ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋“ค์„ ์•Œ๊ณ  ์žˆ๋Š”๊ฒŒ ๋ถ„๋ช… ์„ ํƒ์— ๋„์›€์ด ๋  ๊ฒƒ์ด๋‹ค.

 

๋ธ”๋กœ๊ทธ์˜ ํ”„๋กœํ•„ ์‚ฌ์ง„

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ