๐Ÿš€ ํ•ด์‹œ+ํ•ด์‹œ ํŒŒํ‹ฐ์…”๋‹+์ƒค๋”ฉ

14903 ๋‹จ์–ด yugabytedbdatabasesqldistributed
YugabyteDB์—๋Š” SQL ์ˆ˜์ค€์˜ ๋ถ„ํ• , PostgreSQL ์„ ์–ธ์  ๋ถ„ํ• , ํ…Œ์ด๋ธ”/์ธ๋ฑ์Šค/ํŒŒํ‹ฐ์…˜ ์ˆ˜์ค€์˜ ๋ถ„ํ• , ์ž๋™ ํ•ด์‹œ ๋˜๋Š” ๋ฒ”์œ„ ๋ถ„ํ• ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด์ „ blog post์—์„œ ๊ทธ๊ฒƒ๋“ค์„ ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ์ƒค๋”ฉ์€ ํด๋Ÿฌ์Šคํ„ฐ ์ „์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋ฐฐํฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…”๋‹์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ๊ทธ๋ฃนํ™”ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํƒ€์ž„์Šคํƒฌํ”„์—์„œ ๋ฒ”์œ„๋ณ„๋กœ ๋ถ„ํ• ํ•˜์—ฌ ์ •๋ณด ์ˆ˜๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‹œ๊ฐ„๋Œ€์— ํ•จ๊ป˜ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ๊ตญ๊ฐ€ ๋ชฉ๋ก์—์„œ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ƒ์ฃผ๋ฅผ ์œ„ํ•ด ํŠน์ • ์ง€์—ญ์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ํ…Œ๋„ŒํŠธ ๋ชฉ๋ก์„ ๊ทธ๋ฃนํ™”ํ•˜์—ฌ ๋‹ค๋ฅธ ํ…Œ๋„ŒํŠธ์™€ ๊ฒฉ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

PostgreSQL์—๋Š” ํ•ด์‹œ ๋ถ„ํ• ๋„ ์žˆ์ง€๋งŒ ์ฃผ์š” ๋ชฉํ‘œ๊ฐ€ ๋ฐฐํฌ์ด๊ธฐ ๋•Œ๋ฌธ์— YugabyteDB์—์„œ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋” ๋งŽ์€ ํŒŒํ‹ฐ์…˜, ์ž๋™ ์žฌ์กฐ์ •, ๊ธ€๋กœ๋ฒŒ ์ธ๋ฑ์Šค ๋“ฑ ํƒœ๋ธ”๋ฆฟ์œผ๋กœ ์ƒค๋”ฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ•ด์‹œ ํŒŒํ‹ฐ์…”๋‹๊ณผ ํ•ด์‹œ ์ƒค๋”ฉ์ด ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๋ฉด ์ ˆ๋Œ€ ์•ˆ ๋œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ํ•ด์‹ฑ ๋ฐฉ๋ฒ•์ด ๋ถ„์‚ฐ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ์ž˜ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐ ํŒŒํ‹ฐ์…˜ ๋‚ด์—์„œ ํƒœ๋ธ”๋ฆฟ์œผ๋กœ์˜ ์ƒค๋”ฉ์ด ๊ท ํ˜•์„ ์ด๋ฃจ๊ธฐ๋ฅผ ์›ํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋ณด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ ์œ ํ˜•๋งŒ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” n ํŒŒํ‹ฐ์…˜์ด ์žˆ๋Š” ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๊ณ  ๋ฐฑ๋งŒ ๊ฐœ์˜ ํ–‰์œผ๋กœ ์ฑ„์šฐ๊ณ  YugabyteDB ํ•ด์‹œ ํ•จ์ˆ˜์ธ ๊ฐ๊ฐ์˜ yb_hash_code()๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์‹คํ–‰ํ•œ ์Šคํฌ๋ฆฝํŠธ์ด๋ฉฐ 1๊ฐœ์—์„œ 50๊ฐœ ํŒŒํ‹ฐ์…˜๊นŒ์ง€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

c=1000000
for n in {1..50} ; do
  echo "
  $n Partitions, $c rows:"
  {
    cat <<SQL
    drop table demo;
    create extension if not exists pgcrypto;
    create table demo (id uuid default gen_random_uuid(), val int) partition by hash(id);
SQL
    for i in $(seq 0 $(( $n -1 ))) ; do
    cat <<SQL
    create table demo$i partition of demo for values with (modulus $n , remainder $i);
SQL
    done
    cat <<SQL
    insert into demo ( val) select generate_series(1,$c);
SQL
    for i in $(seq 0 $(( $n -1 ))) ; do
    cat <<SQL
    select format('Partition %s / %s : min= %s max= %s -> %s %% rows',to_char($i,'99'),to_char($n,'99'),min(yb_hash_code(id)),max(yb_hash_code(id)),100*count(*)/$c) from demo$i;
SQL
    done
  } | psql -p 5433
done | grep Partition | tee hash-hash.log


๊ฒฐ๊ณผ:

1 Partitions, 1000000 rows:
 Partition   0 /   1 : min= 0 max= 65535 -> 100 % rows
2 Partitions, 1000000 rows:
 Partition   0 /   2 : min= 0 max= 65535 -> 49 % rows
 Partition   1 /   2 : min= 0 max= 65535 -> 50 % rows
3 Partitions, 1000000 rows:
 Partition   0 /   3 : min= 0 max= 65535 -> 33 % rows
 Partition   1 /   3 : min= 0 max= 65535 -> 33 % rows
 Partition   2 /   3 : min= 0 max= 65535 -> 33 % rows
4 Partitions, 1000000 rows:
 Partition   0 /   4 : min= 0 max= 65535 -> 25 % rows
 Partition   1 /   4 : min= 0 max= 65535 -> 24 % rows
 Partition   2 /   4 : min= 0 max= 65535 -> 24 % rows
 Partition   3 /   4 : min= 0 max= 65535 -> 24 % rows
5 Partitions, 1000000 rows:
 Partition   0 /   5 : min= 0 max= 65535 -> 19 % rows
 Partition   1 /   5 : min= 0 max= 65535 -> 20 % rows
 Partition   2 /   5 : min= 0 max= 65535 -> 20 % rows
 Partition   3 /   5 : min= 0 max= 65535 -> 19 % rows
 Partition   4 /   5 : min= 0 max= 65535 -> 20 % rows
6 Partitions, 1000000 rows:                                                                  [183/1982]
 Partition   0 /   6 : min= 0 max= 65535 -> 16 % rows
 Partition   1 /   6 : min= 0 max= 65535 -> 16 % rows
 Partition   2 /   6 : min= 0 max= 65535 -> 16 % rows
 Partition   3 /   6 : min= 0 max= 65535 -> 16 % rows
 Partition   4 /   6 : min= 0 max= 65535 -> 16 % rows
 Partition   5 /   6 : min= 0 max= 65534 -> 16 % rows
7 Partitions, 1000000 rows:
 Partition   0 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   1 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   2 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   3 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   4 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   5 /   7 : min= 0 max= 65535 -> 14 % rows
 Partition   6 /   7 : min= 0 max= 65534 -> 14 % rows
8 Partitions, 1000000 rows:
 Partition   0 /   8 : min= 2 max= 65535 -> 12 % rows
 Partition   1 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   2 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   3 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   4 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   5 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   6 /   8 : min= 0 max= 65535 -> 12 % rows
 Partition   7 /   8 : min= 0 max= 65535 -> 12 % rows
...
50 Partitions, 1000000 rows:
 Partition   0 /  50 : min= 2 max= 65534 -> 1 % rows
 Partition   1 /  50 : min= 2 max= 65531 -> 2 % rows
 Partition   2 /  50 : min= 5 max= 65527 -> 1 % rows
 Partition   3 /  50 : min= 3 max= 65534 -> 2 % rows
 Partition   4 /  50 : min= 2 max= 65532 -> 2 % rows
 Partition   5 /  50 : min= 0 max= 65530 -> 1 % rows
 Partition   6 /  50 : min= 6 max= 65534 -> 2 % rows
 Partition   7 /  50 : min= 0 max= 65526 -> 2 % rows
 Partition   8 /  50 : min= 3 max= 65535 -> 2 % rows
 Partition   9 /  50 : min= 3 max= 65535 -> 1 % rows
 Partition  10 /  50 : min= 4 max= 65534 -> 1 % rows
 Partition  11 /  50 : min= 2 max= 65535 -> 1 % rows
 Partition  12 /  50 : min= 2 max= 65533 -> 2 % rows
 Partition  13 /  50 : min= 0 max= 65535 -> 1 % rows
 Partition  14 /  50 : min= 14 max= 65529 -> 2 % rows
 Partition  15 /  50 : min= 0 max= 65530 -> 1 % rows
 Partition  16 /  50 : min= 0 max= 65535 -> 2 % rows
 Partition  17 /  50 : min= 4 max= 65528 -> 1 % rows
 Partition  18 /  50 : min= 1 max= 65533 -> 1 % rows
 Partition  19 /  50 : min= 6 max= 65532 -> 1 % rows
 Partition  20 /  50 : min= 0 max= 65533 -> 2 % rows
 Partition  21 /  50 : min= 0 max= 65535 -> 1 % rows                                         
 Partition  22 /  50 : min= 4 max= 65534 -> 2 % rows
 Partition  23 /  50 : min= 1 max= 65529 -> 2 % rows
 Partition  24 /  50 : min= 2 max= 65530 -> 2 % rows
 Partition  25 /  50 : min= 5 max= 65529 -> 2 % rows
 Partition  26 /  50 : min= 3 max= 65518 -> 1 % rows
 Partition  27 /  50 : min= 4 max= 65534 -> 1 % rows
 Partition  28 /  50 : min= 0 max= 65529 -> 1 % rows
 Partition  29 /  50 : min= 6 max= 65530 -> 2 % rows
 Partition  30 /  50 : min= 7 max= 65522 -> 2 % rows
 Partition  31 /  50 : min= 11 max= 65533 -> 2 % rows
 Partition  32 /  50 : min= 2 max= 65534 -> 1 % rows
 Partition  33 /  50 : min= 1 max= 65526 -> 2 % rows
 Partition  34 /  50 : min= 3 max= 65535 -> 2 % rows
 Partition  35 /  50 : min= 4 max= 65534 -> 2 % rows
 Partition  36 /  50 : min= 2 max= 65534 -> 2 % rows
 Partition  37 /  50 : min= 0 max= 65528 -> 1 % rows
 Partition  38 /  50 : min= 2 max= 65532 -> 2 % rows
 Partition  39 /  50 : min= 0 max= 65534 -> 2 % rows
 Partition  40 /  50 : min= 1 max= 65527 -> 2 % rows
 Partition  41 /  50 : min= 1 max= 65535 -> 1 % rows
 Partition  42 /  50 : min= 0 max= 65535 -> 2 % rows
 Partition  43 /  50 : min= 5 max= 65533 -> 2 % rows
 Partition  44 /  50 : min= 7 max= 65533 -> 1 % rows
 Partition  45 /  50 : min= 10 max= 65534 -> 2 % rows
 Partition  46 /  50 : min= 2 max= 65534 -> 2 % rows
 Partition  47 /  50 : min= 4 max= 65534 -> 1 % rows
 Partition  48 /  50 : min= 1 max= 65526 -> 1 % rows
 Partition  49 /  50 : min= 4 max= 65528 -> 2 % rows



๊ฐ ํŒŒํ‹ฐ์…˜์—๋Š” ์ƒค๋”ฉ(ํ•ด์‹œ ์ฝ”๋“œ ๋ฒ”์œ„ ๊ธฐ์ค€)์— ์‚ฌ์šฉ๋˜๋Š” ์ „์ฒด ๋ฒ”์œ„์˜ ํ•ด์‹œ ์ฝ”๋“œ(0์—์„œ 65535๊นŒ์ง€)๊ฐ€ ์žˆ์œผ๋ฉฐ ํŒŒํ‹ฐ์…˜์— ๋Œ€ํ•œ ํ–‰ ๋ถ„ํฌ๋Š” ๊ท ํ˜•์ด ์ž˜ ๋งž์Šต๋‹ˆ๋‹ค.

ํŒŒํ‹ฐ์…˜ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜๊ณ  yb_hash_code() ์žฌํŒŒํ‹ฐ์…˜์„ ์‚ดํŽด๋ด…๋‹ˆ๋‹ค.

yugabyte=# 

           select n,count(h) from (
            select yb_hash_code(id) h,count(*) n
            from demo42 group by yb_hash_code(id)
           ) x  group by n order by n;

 n | count
---+-------
 1 | 14791
 2 |  2216
 3 |   233
 4 |    18
 5 |     3
(5 rows)


์ด ํŒŒํ‹ฐ์…˜์˜ 20009์— ์žˆ๋Š” 14791๊ฐœ์˜ ํ–‰์— ๋Œ€ํ•ด yb_hash_code()๋Š” ํ•œ ํ–‰์—๋งŒ ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  5๊ฐœ์˜ ํ–‰์— ๋งคํ•‘๋˜๋Š” yb_hash_codes()๋Š” 3๊ฐœ๋ฟ์ž…๋‹ˆ๋‹ค. ์ด๋Š” ํŒŒํ‹ฐ์…˜์ด ์ด๋ฏธ ํ•ด์‹œ ๋ถ„ํ• ์˜ ๊ฒฐ๊ณผ์ธ ๊ฒฝ์šฐ์—๋„ ํŒŒํ‹ฐ์…˜ ๋‚ด์—์„œ ์–‘ํ˜ธํ•œ ๋ถ„ํฌ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

๊ฐ ํŒŒํ‹ฐ์…˜์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ ์ด ํŒŒํ‹ฐ์…˜์€ yb_hash_code()์˜ 3๊ฐ€์ง€ ๋ฒ”์œ„์—์„œ 3๊ฐœ์˜ ์ •์ œ๋กœ ๋ถ„ํ• ๋ฉ๋‹ˆ๋‹ค. hash_split: [0x0000, 0x5555)๋Š” 0์—์„œ 21844๊นŒ์ง€, hash_split: [0x5555, 0xAAAA)๋Š” 21845์—์„œ 43689๊นŒ์ง€, hash_split: [0xAAAA, 0xFFFF]๋Š” 43690์—์„œ 65535๊นŒ์ง€์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊ฐ€ ๋” ๋งŽ์„์ˆ˜๋ก ๋” ๋‚˜๋ˆ•๋‹ˆ๋‹ค.

ํ•ด์‹ฑ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ๋งค์šฐ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. PostgreSQL์€ ๋ชจ๋“ˆ๋กœ ์˜จ ํ•ด์‹œ ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” ํŒŒํ‹ฐ์…˜์˜ ๊ฐœ์ˆ˜๋ฅผ ์ฒ˜์Œ๋ถ€ํ„ฐ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. YugabyteDB๋Š” ๋ฒ”์œ„๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋” ์‰ฝ๊ฒŒ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐจ์ด์ ์— ๋Œ€ํ•œ ์ข‹์€ ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. https://ben.kirw.in/2018/12/02/hash-range-partitioning/

์š”์•ฝํ•˜๋ฉด Hash Partitioning + Hash Sharding์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์‚ฐ์‹œํ‚ค๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ํŒŒํ‹ฐ์…˜ ํ‚ค์™€ ๋ฐ์ดํ„ฐ ์œ ํ˜•์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€ ํ•„์š”ํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•ด์‹œ ์ƒค๋”ฉ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ํ•ด์‹œ ์ƒค๋”ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ํƒœ๋ธ”๋ฆฟ์„ ๋ณด์œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 65535 ํ•ด์‹œ ์ฝ”๋“œ ๊ฐ’๊ณผ recommended tablet count and size ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งค์šฐ ํฐ ํ…Œ์ด๋ธ”์„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜์‹ ๋‹ค๋ฉด ์œ ์Šค ์ผ€์ด์Šค์— ๋Œ€ํ•ด ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ