BST, B-Tree, B+Tree ๊ตฌํ
xfs๋ ๋๋ ํฐ๋ฆฌ, Extent ๊ด๋ฆฌ, free space ๊ด๋ฆฌ ๋ฑ ์ฌ๋ฌ ํ์ผ์์คํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ B+Tree๋ก ๊ด๋ฆฌํ๋ค.
Linux ํ์ผ์์คํ ์ชฝ์ ๊ณต๋ถํ๋ฉด์ ๋ดค๋ ๋ฌธ๊ตฌ ์ ๋๋ค. ์ฌ๊ธฐ์์ B+Tree๋ก ๊ด๋ฆฌํ๋ค๊ณ ํ๋ ๊ฑธ ๋ดค๋๋ฐ, ์ด๋๋ ๋ด๊ฐ B+Tree๋ฅผ ํ๋ฒ๋ ์ง์ ๊ตฌํํด๋ณธ ์ ์ด ์๋ค๋ ๊ฑธ ๊นจ๋ซ๊ฒ ๋์์ด์.
๋, ์ฆ๊ฒจ๋ณด๋ ์ ํ๋ฒ ์ฝ๋ฉ์ ํ์ โindex๊ฐ ๋ญ์ง ์ค๋ช ํด๋ณด์ธ์ (๊ฐ๋ฐ๋ฉด์ ์๊ฐ)โ ์์์์ DB Index๋ฅผ ์ค๋ช ํ๋ฉด์ B-Tree์ B+Tree๋ฅผ ์ค๋ช ์ด ์์๋๋ฐ์. B+Tree๊ฐ ์ข์ ์๋ฃ๊ตฌ์กฐ์ธ ๊ฑด ์๊ฒ ๋๋ฐ, ์ด๊ฒ ๊ตฌ์ฒด์ ์ผ๋ก ์ด๋ป๊ฒ ๋์ํ๋์ง ๋ ์ค์ค๋ก ์ค๋ช ํ ์ ์๋์ง ํ๊ฐํ๊ณ ์ถ์์ด์.
๊ทธ๋์ ์ด๋ฒ ์ฃผ๋ง์ ๊ฐ ์ก๊ณ ์ด๊ฑธ python์ผ๋ก ๊ตฌํํด๋ดค์ต๋๋ค! ์ด ํฌ์คํธ๋ ๊ตฌํ ๊ณผ์ ์์์ ํ๋ ์๊ฐ๋ค๊ณผ ๊ตฌํ ๊ณผ์ ์ ์์นด์ด๋น ํ๋ ๋ชฉ์ ์ ํฌ์คํธ์์.
์ ๊ฐ ์ค์ค๋ก ์ดํดํ๊ณ ์ค๋ช ํ๊ธฐ ์ํด์ ์ ์ ์๊ฐ์ ํ๋ฆ๋๋ก ์์ฑํ๊ณ , ์ฝ๋๋ ์ฌ๊ตฌ์ฑํ๋ฉด์ ์ผ๋ถ ์ค๋ฅ๊ฐ ์์ ์๋ ์์ต๋๋ค.
์ฒ์ ์์์ BST(Binary Search Tree)๋ก ์์ํ๊ณ , ๊ฑฐ๊ธฐ์๋ถํฐ B-Tree, B+Tree ์์๋๋ก ๊ตฌํ ํ๋ค.
Binary Search Tree
์ผ๋จ BST์ ๋ ธ๋๋ถํฐ ์ ์ํ๋ค.
from __future__ import annotations
class Node:
# ํ์ฌ ๋
ธ๋๊ฐ ์ ์ฅํ ๊ฐ.
def __init__(self, key):
self.key = key
self.left: Node | None = None
self.right: Node | None= None
key๋ณด๋ค ํฌ๋ฉด, right์ ๋ฐฐ์นํ๊ณ , key๋ณด๋ค ์์ผ๋ฉด left์ ๋ฐฐ์นํ๋ค.
class BST():
def __init__(self):
self.root = None
def insert(self, key: int):
pass
def search(self, key: int):
pass
def remove(self, key: int):
pass
๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ 3๊ฐ ํจ์๋ฅผ ๊ตฌํํ๋ค.
Insert
class BST():
...
def insert(self, key: int):
if self.root is None:
print(f"Insert {key} as root")
self.root = Node(key)
return
current_node = self.root
while True:
if current_node.key == key:
print(f"{key} already exists in BST")
return
elif current_node.key > key:
if current_node.left is None:
print(f"Insert {key} as left child of {current_node.key}")
current_node.left = Node(key)
return
current_node = current_node.left
else:
if current_node.right is None:
print(f"Insert {key} as right child of {current_node.key}")
current_node.right = Node(key)
return
current_node = current_node.right
current_node์ key์ ํ์ key๋ฅผ ๋น๊ตํ๋ฉฐ, ์ข/์ฐ ๋
ธ๋๋ก ์ด๋ํ๋ค.
Search
class BST():
...
def search(self, key: int):
current_node = self.root
while current_node:
if current_node is None:
print(f"{key} not found in BST")
return False
elif current_node.key == key:
print(f"{key} found in BST")
return True
elif current_node.key > key:
current_node = current_node.left
else:
current_node = current_node.right
print(f"{key} not found in BST")
return False
๋ง์ฐฌ๊ฐ์ง๋ก current_node์ key์ ํ์ key๋ฅผ ๋น๊ตํ๋ฉฐ, ์ข/์ฐ ๋
ธ๋๋ก ์ด๋ํ๋ค.
Remove
์ฝ์ /ํ์ ์ฐ์ฐ๋ณด๋ค ์ฝ๊ฐ ๋ณต์กํ๋ค.
class BST():
...
def remove(self, key: int):
if self.root is None:
print("BST is empty")
return
parent = None
current = self.root
# ์ด๋ค ๋
ธ๋๋ฅผ ์ญ์ ํด์ผ ํ๋์ง ํ์
while True:
if key == current.key:
break
elif key < current.key:
parent = current
current = current.left
else:
parent = current
current = current.right
if current is None:
print(f"{key} not found in BST")
return
๋์ผํ๊ฒ ์ญ์ ํ ๋ ธ๋๋ฅผ Binary Search ๋ฐฉ์์ผ๋ก ์ฐพ๋๋ค.
์ด์ ๋ ธ๋๋ฅผ ์ญ์ ํด์ผ ํ๋๋ฐ, ์์ ๋ ธ๋๊ฐ ๋ช๊ฐ ์๋์ง์ ๋ฐ๋ผ ๊ณผ์ ์ด ๋ฌ๋ผ์ง๋ค.
- ์ญ์ ํ ๋
ธ๋๊ฐ ์์ ๋
ธ๋ ์์
- ๋ ธ๋๋ฅผ ์ญ์ ํ๊ณ , ๋ถ๋ชจ ๋ ธ๋์์๋ ์ฐ๊ฒฐ ํด์
- ์ญ์ ํ ๋
ธ๋๊ฐ ์์ ๋
ธ๋ 1๊ฐ๋ฅผ ๊ฐ์ง
- ๋ ธ๋๋ฅผ ์ญ์ ํ๊ณ , ๋ถ๋ชจ ๋ ธ๋์ ํด๋น ๋ ธ๋๋ฅผ ์ฐ๊ฒฐ
- ์ญ์ ํ ๋
ธ๋๊ฐ ์์ ๋
ธ๋ 2๊ฐ๋ฅผ ๊ฐ์ง
- ๋ ธ๋๋ฅผ ์ญ์ ํ๊ณ
- ์ผ์ด์ค๊ฐ ๋๋๋ค.
- ์์ ๋
ธ๋ ์ค ์ค๋ฅธ์ชฝ ์๋ธํธ๋ฆฌ์์
- ๊ฐ์ฅ ์์ ๊ฐ์ ์ฐพ์์ ์๋ก์ด ๋ฃจํธ ๋ ธ๋๋ก ์ผ์
- ๊ทธ๋ฆฌ๊ณ ์๋ก์ด ๋ฃจํธ ๋ ธ๋๋ฅผ ๋ถ๋ชจ ๋ ธ๋์ ์ฐ๊ฒฐ
- ์์ ๋
ธ๋ ์ค ์ผ์ชฝ ์๋ธํธ๋ฆฌ์์
- ๊ฐ์ฅ ํฐ ๊ฐ์ ์ฐพ์์ ์๋ก์ด ๋ฃจํธ ๋ ธ๋๋ก ์ผ์
- ๊ทธ๋ฆฌ๊ณ ์๋ก์ด ๋ฃจํธ ๋ ธ๋๋ฅผ ๋ถ๋ชจ ๋ ธ๋์ ์ฐ๊ฒฐ
- ์์ ๋
ธ๋ ์ค ์ค๋ฅธ์ชฝ ์๋ธํธ๋ฆฌ์์
class BST():
...
def remove(self, key: int):
...
# current์ ์์์ด 0๊ฐ์ธ ๊ฒฝ์ฐ
if current.left is None and current.right is None:
print(f"Remove {key} from BST with 0 children")
if parent is None:
self.root = None
elif parent.left == current:
parent.left = None
else:
parent.right = None
# current์ ์์์ด 1๊ฐ์ธ ๊ฒฝ์ฐ
if current.left is None and current.right is not None:
print(f"Remove {key} from BST with 1 child")
if parent is None:
self.root = current.right
elif parent.left == current:
parent.left = current.right
else:
parent.right = current.right
if current.left is not None and current.right is None:
print(f"Remove {key} from BST with 1 child")
if parent is None:
self.root = current.left
elif parent.left == current:
parent.left = current.left
else:
parent.right = current.left
# current์ ์์์ด 2๊ฐ์ธ ๊ฒฝ์ฐ
# ์ค๋ฅธ์ชฝ ์๋ธํธ๋ฆฌ์์ ๊ฐ์ฅ ์์ ๊ฐ์ ์ฐพ์์ ๊ต์ฒด
if current.left is not None and current.right is not None:
print(f"Remove {key} from BST with 2 children")
right = current.right
right_parent = current
# `right.left`์ ํ์ํ๋ฉด์, ๊ฐ์ฅ ์์ ๊ฐ์ ๋
ธ๋๋ฅผ ํ์
while right.left:
right_parent = right
right = right.left
if right_parent == current:
# while ๋ฃจํ๋ฅผ ํ๋ฒ๋ ์ ๋ ์ผ์ด์ค
right_parent.right = right.right
else:
# while ๋ฃจํ๋ฅผ ํ๋ฒ์ด๋ผ๋ ํ ์ผ์ด์ค
# ๊ฐ์ฅ ์์ ๊ฐ์ ๋ํ right ๋
ธ๋๊ฐ ์กด์ฌํ ์ ์์.
# ์ด๊ฑธ ํด๋น ๋
ธ๋์ left์ ์ฐ๊ฒฐํด์ค์ผ ํจ.
right_parent.left = right.right
current.key = right.key
print(f"Replace {key} with {right.key}")
B-Tree
B-Tree๋ BST์ ๋ค๋ฅธ ์๋ฃ๊ตฌ์กฐ์ ๋๋ค.
- BST๋ ๋
ธ๋๋น ๊ฐ์ง ์ ์๋ key๊ฐ 1๊ฐ ์
๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ์์ ๋ ธ๋๋ ์ต๋ 2๊ฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
- B-Tree๋ ๋
ธ๋๋น key๊ฐ ์ต๋
2t - 1๊ฐ ๊ฐ๋ฅํฉ๋๋ค.- ๋ ธ๋ ๋ด์์๋ key๋ฅผ ์ ๋ ฌ๋ ์ํ๋ก ์ ์ฅํฉ๋๋ค.
- ๊ทธ๋ฆฌ๊ณ ์์ ๋
ธ๋๋ key ๊ฐฏ์
k์ผ ๋,k+1์ด ๊ฐ๋ฅํฉ๋๋ค.
์๋ฅผ ๋ค์ด, B-Tree๋ ์ด๋ ๊ฒ
[10 | 20 | 30]
/ | | \
c0 c1 c2 c3
B-Tree์์ ๋
ธ๋๊ฐ ์ผ๋ง๋ ์ฑ์์ง ์ ์๋์ง๋ฅผ ๊ฒฐ์ ํ๋ ํ๋ผ๋ฏธํฐ t๋ฅผ ์ต์ ์ฐจ์(minimum degree)๋ผ๊ณ ํฉ๋๋ค.
t๊ฐ ์ปค์ง์๋ก ํ ๋
ธ๋์ ๋ ๋ง์ ํค์ ์์์ ๋ด์ ์ ์์ต๋๋ค. ๊ทธ๋์ t=2์ผ ๋์ t=100์ผ ๋๋ฅผ ๋น๊ตํ๋ฉด, t=100์ธ B-Tree๋ ๋ ์์ ๋์ด๋ฅผ ๊ฐ์ง๋๋ค.
์ฌ๊ธฐ์์๋ t=2๋ก ๊ณ ์ ํ๊ณ ์ฝ๋๋ฅผ ์์ฑ ํ์ต๋๋ค.
from __future__ import annotations
class BTreeNode:
def __init__(self, leaf: bool = True):
# leaf ์ฌ๋ถ๋ len(children) == 0์ผ๋ก๋ ํ๋จํ ์ ์์.
self.leaf = leaf
self.keys: list[int] = []
self.children: list[BTreeNode] = []
class BTree:
def __init__(self):
# ๊ฐ ๋
ธ๋๋ ์ต๋ 3(=2t-1)๊ฐ์ ํค๋ฅผ ๊ฐ์ง ์ ์์.
self.t = 2
self.root = BTreeNode(leaf=True)
def insert(self, key: int):
pass
def search(self, key: int):
pass
๊ธฐ๋ณธ์ ์ธ ์ฐ์ฐ์ธ insert()์ search()๋ง ๊ตฌํ ํ์ต๋๋ค.
Insert
์ฐ์ B-Tree๊ฐ ์ต์ด๋ก ๋ง๋ค์ด์ง๋, root ๋ ธ๋๊ฐ ์์ฑ๋๋ ๋จ๊ณ๋ถํฐ ์์ํฉ๋๋ค.
class BTree:
...
def insert(self, key: int):
root = self.root
if root.leaf and len(node.keys) < 3:
if key in node.keys:
print(f"Key {key} already exists in the tree.")
return
node.keys.append(key)
node.keys.sort()
return
else:
pass
๋ฃจํธ ๋ ธ๋๋ง ์กด์ฌํ๊ณ , ๋ฃจํธ ๋ ธ๋์ ์ ์ฅ๋ ํค๊ฐ 3๋ณด๋ค ์๋ค๋ฉด, ๋ฃจํธ ๋ ธ๋์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ผ๋ฉด ๋ฉ๋๋ค.
๊ฒฝ๊ณ๋ ๋ฃจํธ ๋
ธ๋์ ํค ๊ฐฏ์๊ฐ 3์ธ ์๊ฐ๋ถํฐ ์
๋๋ค. B-Tree๋ ๋
ธ๋์ ํค๊ฐ 2t-1์ ๋์ง ์๋๋ก ํด์ผ ํ๋ค๋ ๊ฑธ ๋ฐ๋์ ์ง์ผ์ผ ํฉ๋๋ค. ๋
ธ๋๊ฐ ๋๋ฌด ๋ง์ ํค๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค๋ฉด, _split() ์ฐ์ฐ์ผ๋ก ๋
ธ๋๋ฅผ ๋ถํ ํฉ๋๋ค.
[ 10, 20, 30 ]
---
์๋ก์ด root ์์ฑ
old root๋ฅผ new_root์ 0๋ฒ์งธ child๋ก ์ฐ๊ฒฐ
[ ] <- new_root
|
|
[ 10, 20, 30 ] <- old root
---
๊ฐ์ด๋ฐ ํค 20์ parent๋ก ์ฌ๋ฆฝ๋๋ค.
[ 20 ]
/ \
[ 10 ] [ 30 ]
class BTree:
...
def insert(self, key: int):
root = self.root
if root.leaf and len(root.keys) < 3:
if key in root.keys:
print(f"Key {key} already exists in the tree.")
return
root.keys.append(key)
root.keys.sort()
return
elif root.leaf and len(root.keys) == 3:
# ์ ๊ท ๋ฃจํธ๋ฅผ ๋ฐ๊ธ
new_root = BTreeNode(leaf=False)
# ์ง๊ธ์ ๋ฃจํธ๋ฅผ ์ ๊ท ๋ฃจํธ์ ๋ถ์
new_root.children.append(root)
self.root = new_root
self._split_child(new_root, 0)
...
else:
pass
๋น์ฐํ๊ฒ๋ ์ง๊ธ ๊ฝ์ฐฌ ๋ฃจํธ๋ฅผ ๋์ ํ ์๋ก์ด ๋ฃจํธ ๋
ธ๋ new_root๋ฅผ ๋ง๋ จ ํฉ๋๋ค.
์ด์ ๋ ธ๋ ๋ถํ ๋ก์ง์ ๊ตฌ์ฑํฉ๋๋ค.
class BTree:
...
def _split_child(self, parent: BTreeNode, idx: int):
# parent์ idx ๋ฒ์งธ ์์ ๋
ธ๋๋ฅผ ๋ถํ ํด parent์ ์์ ๋
ธ๋๋ก ์ถ๊ฐํ๋ ํจ์
child = parent.children[idx]
mid_key = child.keys[1]
new_child = BTreeNode(leaf=child.leaf)
# key ๋ถํ
new_child.keys = child.keys[2:]
child.keys = child.keys[:1]
# ๋ถ๋ชจ ๋
ธ๋์ idx ๋ฒ์งธ ์์ ๋
ธ๋์ mid key๋ฅผ ์ฝ์
parent.keys.insert(idx, mid_key)
# ๋ถ๋ชจ์ ์์ ๋ชฉ๋ก์ ์ child๋ฅผ ์ฝ์
parent.children.insert(idx + 1, new_child)
์ด์ insert() ํจ์๋ฅผ ์์ฑ ํฉ์๋ค.
class BTree:
...
def insert(self, key: int):
root = self.root
if root.leaf and len(root.keys) < 3:
if key in root.keys:
print(f"Key {key} already exists in the tree.")
return
root.keys.append(key)
root.keys.sort()
return
elif root.leaf and len(root.keys) == 3:
# ์ ๊ท ๋ฃจํธ๋ฅผ ๋ฐ๊ธ
new_root = BTreeNode(leaf=False)
# ์ง๊ธ์ ๋ฃจํธ๋ฅผ ์ ๊ท ๋ฃจํธ์ ๋ถ์
new_root.children.append(root)
self.root = new_root
self._split_child(new_root, 0)
# ์ด์ ์๋ก์ด key๋ฅผ insert
## ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ๊ธฐ
idx = -1
for i, n_key in enumerate(new_root.keys):
if key == n_key:
print(f"Key {key} already exists in the tree.")
return
if key < n_key:
idx = i
break
if idx == -1:
idx = len(new_root.keys)
## ์ ์ ํ child์ insert
node = new_root.children[idx]
node.keys.append(key)
node.keys.sort()
return
else:
pass
์ง๊ธ๊น์ง๋ ๋ฃจํธ ๋ ธ๋๊ฐ ์ต์ด๋ก ์ฑ์์ง ๋, ๊ทธ๋ฆฌ๊ณ ๋ฃจํธ ๋ ธ๋๊ฐ ์ต์ด๋ก ๊ฝ ์ฐจ์ ๋ถํ ์ด ์ผ์ด๋๋ ์ํฉ์ด์์ต๋๋ค. ์ด์ ์ฝ๋๋ฅผ ์ผ๋ฐํ ํด์ผ ํฉ๋๋ค.
๊ฐ์ฅ ๋จผ์ , ๋
ธ๋์ key๋ฅผ ์ถ๊ฐํ๋ ๋ถ๋ถ์ ํจ์ํ ํ ์ ์์ต๋๋ค. ์ด๋ฅผ _insert_non_full()๋ก ๊ณตํตํ ํฉ๋๋ค.
class BTree:
...
def insert(self, key: int):
root = self.root
if root.leaf and len(root.keys) < 3:
self._insert_non_full(root, key) # ์ฌ๊ธฐ
elif root.leaf and len(root.keys) == 3:
pass
def _insert_non_full(self, node: BTreeNode, key: int):
if len(node.keys) >= 3:
raise ValueError("Node is full.")
if node.leaf:
if key in node.keys:
print(f"Key {key} already exists in the tree.")
return
node.keys.append(key)
node.keys.sort()
return
pass
๊ฐ์ฅ ๋จผ์ , ๋ฃจํธ ๋ ธ๋๊ฐ ์ต์ด๋ก ์ฑ์์ง ๋์ ์ฝ๋๋ฅผ ์ฎ๊ฒผ์ต๋๋ค. ์ด์ , ๋ฃจํธ ๋ ธ๋ ๋ถํ ํ์ ๋์๋ ์ฎ๊ฒจ์ค๋๋ค.
class BTree:
...
def insert(self, key: int):
root = self.root
if root.leaf and len(root.keys) < 3:
self._insert_non_full(root, key) # ์ฌ๊ธฐ
elif root.leaf and len(root.keys) == 3:
# ์ ๊ท ๋ฃจํธ๋ฅผ ๋ฐ๊ธ
new_root = BTreeNode(leaf=False)
# ์ง๊ธ์ ๋ฃจํธ๋ฅผ ์ ๊ท ๋ฃจํธ์ ๋ถ์
new_root.children.append(root)
self.root = new_root
self._split_child(new_root, 0)
# ์ด์ ์๋ก์ด key๋ฅผ insert
self._insert_non_full(new_root, key) # ์ฌ๊ธฐ
def _insert_non_full(self, node: BTreeNode, key: int):
if len(node.keys) >= 3:
raise ValueError("Node is full.")
if node.leaf:
if key in node.keys:
print(f"Key {key} already exists in the tree.")
return
node.keys.append(key)
node.keys.sort()
return
## ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ๊ธฐ
idx = -1
for i, n_key in enumerate(node.keys):
if key == n_key:
print(f"Key {key} already exists in the tree.")
return
if key < n_key:
idx = i
break
if idx == -1:
idx = len(node.keys)
## ์ ์ ํ child์ insert
node = node.children[idx]
node.keys.append(key)
node.keys.sort()
์ด๋ ๊ฒ ํ์ผ๋ฉด, ์ด์ insert() ์ฝ๋๋ฅผ ๋ฃจํธ๋ฅผ ๋์ด, non-root ๋
ธ๋์๋ insert ํ๋ ์ฝ๋๋ก ๋ฐ๊ฟ ์ ์์ต๋๋ค.
class BTree:
...
def insert(self, key: int):
root = self.root
if len(root.keys) < 3:
self._insert_non_full(root, key)
elif len(root.keys) == 3:
# ์ ๊ท ๋ฃจํธ๋ฅผ ๋ฐ๊ธ
new_root = BTreeNode(leaf=False)
# ์ง๊ธ์ ๋ฃจํธ๋ฅผ ์ ๊ท ๋ฃจํธ์ ๋ถ์
new_root.children.append(root)
self.root = new_root
self._split_child(new_root, 0)
# ์ด์ ์๋ก์ด key๋ฅผ insert
self._insert_non_full(new_root, key)
๋ค๋ง, _insert_non_full()์์ ์ด ๋ถ๋ถ์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
class BTree:
...
def _insert_non_full(self, node: BTreeNode, key: int):
if len(node.keys) >= 3:
raise ValueError("Node is full.")
if node.leaf:
if key in node.keys:
print(f"Key {key} already exists in the tree.")
return
node.keys.append(key)
node.keys.sort()
return
## ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ๊ธฐ
idx = -1
for i, n_key in enumerate(node.keys):
if key == n_key:
print(f"Key {key} already exists in the tree.")
return
if key < n_key:
idx = i
break
if idx == -1:
idx = len(node.keys)
# ์ฌ๊ธฐ
if len(node.children[idx].keys) == 3:
# ๋ด๋ ค๊ฐ child๊ฐ ์ด๋ฏธ ๊ฝ ์ฐจ ์๋ค๋ฉด, ๋จผ์ ๋ถํ ํด์ผ ํจ.
self._split_child(node, idx)
# ๋ถํ ํ์ ๋ค์ ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ์์ผ ํจ.
idx = ...
# ์ฌ๊ท ํธ์ถ๋ก ๋ณ๊ฒฝ
self._insert_non_full(node.children[idx], key)
์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง idx๋ฅผ ์ฐพ๋ ์ฝ๋๋ ์์ฃผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ํจ์ํ ํด์ ํธํ๊ฒ ์์๋ค.
class BTree:
...
def _find_child_index(self, node: BTreeNode, key: int) -> tuple[int, bool]:
"""
internal node์์ ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ๊ฒฐ์ .
"""
for i, n_key in enumerate(node.keys):
if key == n_key:
return i, True
if key < n_key:
return i, False
return len(node.keys), False
def _insert_non_full(self, node: BTreeNode, key: int):
if len(node.keys) >= 3:
raise ValueError("Node is full.")
if node.leaf:
if key in node.keys:
print(f"Key {key} already exists in the tree.")
return
node.keys.append(key)
node.keys.sort()
return
# leaf๊ฐ ์๋๋ผ๋ฉด, ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ์์ผ ํจ.
idx, key_exists = self._find_child_index(node, key) # ์ฌ๊ธฐ
if key_exists:
print(f"Key {key} already exists in the tree.")
return
if len(node.children[idx].keys) == 3:
# ๋ด๋ ค๊ฐ child๊ฐ ์ด๋ฏธ ๊ฝ ์ฐจ ์๋ค๋ฉด, ๋จผ์ ๋ถํ ํด์ผ ํจ.
self._split_child(node, idx)
# ๋ถํ ํ์ ๋ค์ ์ด๋ child๋ก ๋ด๋ ค๊ฐ์ง ์ฐพ์์ผ ํจ.
idx, key_exists = self._find_child_index(node, key) # ์ฌ๊ธฐ
if key_exists:
print(f"Key {key} already exists in the tree.")
return
# ์ฌ๊ท ํธ์ถ๋ก ๋ณ๊ฒฝ
self._insert_non_full(node.children[idx], key)
์์ง ๋ฉ์์ต๋๋คโฆ ใ
ใ
๋
ธ๋๋ฅผ ๋ถํ ํ๋ _split_child()๋ internal node๋ฅผ ๋ถํ ํ๋ ๊ฒฝ์ฐ, ์์ ๋
ธ๋๋ฅผ ๋ถํ ํด์ค์ผ ํด์.
class BTree:
...
def _split_child(self, parent: BTreeNode, idx: int):
# parant์ idx ๋ฒ์งธ ์์ ๋
ธ๋๋ฅผ ๋ถํ ํด parent์ ์์ ๋
ธ๋๋ก ์ถ๊ฐํ๋ ํจ์
child = parent.children[idx]
mid_key = child.keys[1]
new_child = BTreeNode(leaf=child.leaf)
# key ๋ถํ
new_child.keys = child.keys[2:]
child.keys = child.keys[:1]
# ์ฌ๊ธฐ๊ฐ ์ถ๊ฐ๋จ.
# child๊ฐ ๋ด๋ถ ๋
ธ๋๋ผ๋ฉด, children๋ ๋ถํ
if not child.leaf:
new_child.children = child.children[2:]
child.children = child.children[:2]
# ๋ถ๋ชจ ๋
ธ๋์ idx ๋ฒ์งธ ์์ ๋
ธ๋์ mid key๋ฅผ ์ฝ์
parent.keys.insert(idx, mid_key)
# ๋ถ๋ชจ์ ์์ ๋ชฉ๋ก์ ์ child๋ฅผ ์ฝ์
parent.children.insert(idx + 1, new_child)
4์ ๋๋ฌํ๋ฉด ๋ ธ๋๋ฅผ ๋ถํ ํ๋๊ฒ ์๋๋ผ, ์ 3๊น์ง ์ฐจ๋ฉด ๋ ธ๋๋ฅผ ๋ถํ ํ ๊น?
์ keys๊ฐ 3์ด ๋๋ฉด ์ ์ ์ ์ผ๋ก split ํ๋๊ฐ?
์ฝ๋๋ฅผ ๊ตฌํํ๋ฉด์ ๋ค์๋ ์๊ฐ์ด์์.
[10 | 20 | 30]
/ | | \
5 15 25 35
์ด ์ํฉ์์ 40์ ์ฝ์ ํ๋ฉด ๊ฐ๋ฅํ ์ ํ์ง๋ 1๋ฒ/2๋ฒ์ด์์.
1๋ฒ: ๋ ธ๋๋ฅผ ๋ถํ ํ๊ณ insert ํ๋ค.
[20]
/ \
[10] [30]
/ \ / \
[5] [15] [25] [35|40]
2๋ฒ: ์ค์ ์ฝ์ ์ด ๋๋ ๋์ ๋ ธ๋๊ฐ ํ๊ณ(=4)์ ๋๋ฌํ๋ฉด split node
[10 | 20 | 30]
/ | | \
5 15 25 [35|40]
1๋ฒ๊ณผ 2๋ฒ ๋๋ค B-Tree ๊ตฌ์กฐ๋ฅผ ์ถฉ์กฑํฉ๋๋ค.
1๋ฒ ๋ฐฉ์์ top-down์ด๋ผ๊ณ ํฉ๋๋ค. ์ฝ์
์ฐ์ฐ์ ํ๊ธฐ ์ํด ํธ๋ฆฌ๋ฅผ ์ํํ ๋๋ง๋ค,
if node.key is full: split node๋ฅผ ์ฒดํฌํ๊ณ , ๋
ธ๋ ๋ถํ ์ ์ํํฉ๋๋ค.
์ฆ, full(=3) ์ํ์ ๋ ธ๋ ํ์ ๊ณผ์ ์์ ๋ชจ๋ ์ฌ์ ๋ถํ (pre-split) ๋ฉ๋๋ค.
๋ฐ๋ฉด์, 2๋ฒ์ ๋ด๊ฐ ์ค์ ๋ก ์ฝ์ ํ๋ ๋ ธ๋๊ฐ ํ๊ณ์ ๋ ธ๋ฌํ๋ฉด split node๋ฅผ ์ํํฉ๋๋ค. ์ด ๊ฒฝ์ฐ, split ํ๋ฉด์ child -> parent๋ก mid_key๋ฅผ ์ฌ๋ ค์ฃผ๊ฒ ๋๋๋ฐ, ๋ง์ฝ parent ๋ ธ๋๊ฐ ๊ฝ์ฐฌ ์ํ์๋ค๋ฉด, parent๋ ๋ ธ๋ ๋ถํ ์ ํด์ผ ํฉ๋๋ค.
[40|80|120]
/ | | \
[10|20|30] [50|60|70] [90|100|110] [130]
์ด ์ํ์์ 5๋ฅผ insert ํ๊ฒ ๋๋ฉด,
[40|80|120]
/ | | \
[5|10|20|30] [50|60|70] [90|100|110] [130]
(overflow)
---
[20 | 40 | 80 | 120]
(overflow)
/ | | | \
[5|10] [30] [50|60|70] [90|100|110] [130]
(20์ด ๋ถ๋ชจ๋ก ์ฌ๋ผ๊ฐ.)
---
[80]
/ \
[20|40] [120]
/ | \ / \
[5|10] [30] [50|60|70] [90|100|110] [130]
2๋ฒ ๋ฐฉ์์ split ํ, parent๋ก ์ฌ๋ผ๊ฐ mid_key๋ก ์ธํด parent overflow๊ฐ ๋ฐ์ํ๊ณ , split ๋ ์ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋, mid_key๊ฐ ์ฌ๋ผ๊ฐ๋ฉด์ overflow -> split์ ์ ๋ฐํฉ๋๋ค. ์ฆ, split ์ด๋ผ๋ ํ์ ์์ฒด๊ฐ ์ฐ์์ ์ผ๋ก bottom-up์ผ๋ก ์ ํ ๋ฉ๋๋ค.
ํ์ง๋ง, 1๋ฒ์ split์ด ์ฐ์์ ์ผ๋ก ๋ฐ์ํ์ง ์๊ณ ์ ํ๋์ง๋ ์์ต๋๋ค. ์์ ๋ ธ๋๋ฅผ ์ํํ ๋, ์ด๋ฏธ full ์ํ์ธ ๋ ธ๋๋ฅผ ์ ๋ฆฌํ๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋์ ์ค๊ฐ/๋ฆฌํ ๋ ธ๋์์ split์ด ๋ฐ์ํ๋๋ผ๋, ๊ทธ๊ฒ ์ฐ์์ ์ผ๋ก ์ ํ๋์ง ์์ต๋๋ค.
Search
ํ์์ ๊ทธ๋๋ก ์ฝ์ต๋๋ค ใ ใ
class BTree:
...
def search(self, key: int):
return self._search_node(self.root, key)
def _search_node(self, node: BTreeNode, key: int):
# ์ด๋ค child๋ฅผ ํ์ํ ์ง ๊ฒฐ์
idx, key_exists = self._find_child_index(node, key)
# ๋ง์ฝ key๊ฐ node์ ์๋ค๋ฉด, True๋ฅผ ๋ฐํ
if key_exists:
return True
if node.leaf:
return False
return self._search_node(node.children[idx], key)
B+Tree
์ ์ด์ , ์ด ๊ธ์ ์์ฑํ๊ฒ ๋ง๋ ์ฃผ์ธ๊ณต B+Tree ์ ๋๋คโฆ!
B-Tree์ B+Tree๋ ํธ๋ฆฌ์ ํ์์ ๋์ผํฉ๋๋ค. ๋๋ค t ๊ณ์๋ฅผ ๊ฐ์ง๊ณ ์๊ณ , ๋
ธ๋์ ํค ๊ฐฏ์๋ 2t-1๊น์ง ๊ฐ๋ฅํฉ๋๋ค. ๋
ธ๋๊ฐ ์ฐจ๋ฉด ๋
ธ๋๋ฅผ ๋ถํ ํด ์ ์ฝ ์กฐ๊ฑด์ ๋ง์กฑํ๊ฒ ํฉ๋๋ค.
์ผ๋จ ์ค์ผ๋ ํค ๋ถํฐ ๋ง๋ จ ํฉ์๋ค.
class BPlusTreeNode:
def __init__(self, leaf: bool = True):
self.leaf = leaf
self.keys: list[int] = []
# internal node์์๋ง ์ฌ์ฉ
self.children: list[BPlusTreeNode] = []
# leaf node์์๋ง ์ฌ์ฉ
self.next: BPlusTreeNode | None = None
class BPlusTree:
def __init__(self):
self.t = 2
self.root = BPlusTreeNode(leaf=True)
def insert(self, key: int):
pass
def search(self, key: int):
pass
B+Tree๋ ๋ฆฌํ ๋
ธ๋๋ฅผ Linked List๋ก ์ฐ๊ฒฐํฉ๋๋ค. ๊ทธ๋์ self.next ๋ณ์๊ฐ ์ถ๊ฐ๋์์ต๋๋ค.
Insert
class BPlusTree:
...
def insert(self, key: int):
root = self.root
if len(root.keys) == 3:
# root๊ฐ ๊ฝ ์ฐจ ์๋ค๋ฉด, ์๋ก์ด root๋ฅผ ๋ง๋ค๊ณ split
new_root = BPlusTreeNode(leaf=False)
# ์ง๊ธ์ ๋ฃจํธ๋ฅผ ์ ๊ท ๋ฃจํธ์ ๋ถ์
new_root.children.append(root)
self.root = new_root
self._split_child(new_root, 0)
# ์ด์ ์๋ก์ด key๋ฅผ insert
self._insert_non_full(root, key)
else:
self._insert_non_full(root, key)
def _insert_non_full(self, node: BPlusTreeNode, key: int):
# B-Tree์ ๋์ผ
B+Tree๋ B-Tree ํธ๋ฆฌ์ ํ์์ด ๋์ผํ๊ธฐ ๋๋ฌธ์, ๋ ธ๋๋ฅผ ์ถ๊ฐํ๋ ๊ณผ์ ์ด ํฐ ํ์์ ๋์ผํฉ๋๋ค.
ํ์ง๋ง, _find_child_index()์ _split_child() ๋จ๊ณ์์ ์ฐจ์ด๊ฐ ์์ต๋๋ค.
class BPlusTree:
...
def _find_child_index(self, node: BPlusTreeNode, key: int) -> int:
# B+tree์์๋ key == n_key์ธ ๊ฒฝ์ฐ, ์ค๋ฅธ์ชฝ child๋ก ๋ด๋ ค๊ฐ.
# B+tree์์ internal node์ key๋ ๊ธธ ์๋ด์ฉ key์ด๊ธฐ ๋๋ฌธ์.
# internal node ์ ์ฉ ํจ์
if node.leaf:
raise ValueError("Node is a leaf, cannot find child index.")
for i, n_key in enumerate(node.keys):
if key < n_key:
return i
return len(node.keys)
B-Tree์์ key == n_key๋ก ์ผ์นํ๋ ๊ฒฝ์ฐ, ํ์์ ์ค๋จํ๊ณ ๋
ธ๋์ ์์น๋ฅผ ๋ฐํํ๊ฑฐ๋ ๋
ธ๋ ์ฝ์
์ ์ค๋จ ํ์ต๋๋ค.
ํ์ง๋ง, B+Tree์์ internal node์์ key == n_key๋ก ์ผ์นํ๋ค๋ฉด, ๊ทธ๊ฑธ ๊ธฐ์ค์ผ๋ก ๋ค์ ์ํ ๋
ธ๋๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
[20 | 40]
/ | \
[5|10] -> [20|30] -> [40|50]
๊ทธ ์ด์ ๋ B+Tree์์ ์ค์ง ๋ฆฌํ ๋
ธ๋๋ฅผ ๊ธฐ์ค์ผ๋ก Point Query, Range Query์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ๊ท์น์ด ์๊ธฐ ๋๋ฌธ์
๋๋ค.
๊ทธ๋์ผ WHERE id BETWEEN 10 AND 20๊ณผ ๊ฐ์ Range Query๊ฐ ๋ฆฌํ ๋
ธ๋ 10์์ ์์ํด Linked List๋ก 20๊น์ง ์ํํ ์ ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ค๊ฐ์ internal node์์ ํ์์ ๋ฉ์ถ๋ค๋ฉด, ์ด๋ฐ Range Query๋ฅผ ์ํด ๋ณ๋ ๊ตฌํ์ ๋ง๋ จํด์ผ ํ ๊ฒ ์ ๋๋ค.
class BPlusTree:
...
def _split_child(self, parent: BPlusTreeNode, idx: int):
child = parent.children[idx]
if child.leaf:
self._split_child_leaf(parent, idx)
else:
self._split_child_internal(parent, idx)
B+Tree๋ ๋ ธ๋ ๋ถํ ๋, leaf์ธ์ง internal node์ธ์ง ๊ตฌ๋ถํด์ ์ํํฉ๋๋ค.
def _split_child_leaf(self, parent: BPlusTreeNode, idx: int):
# B-tree์์๋ leaf node๊ฐ ๊ฝ ์ฐจ๋ฉด, ๊ฐ์ด๋ฐ key๋ฅผ parent๋ก ์ฌ๋ฆฌ๊ณ , ์ค๋ฅธ์ชฝ ์ ๋ฐ์ ์๋ก์ด leaf node๋ก ๋ง๋ฆ.
# B+Tree์์๋ leaf node๊ฐ ๊ฝ ์ฐจ๋ฉด, ์ค๋ฅธ์ชฝ ์ ๋ฐ์ ์๋ก์ด leaf node๋ก ๋ง๋ค๊ณ , parent์๋ ์ค๋ฅธ์ชฝ ์ ๋ฐ์ ์ฒซ ๋ฒ์งธ key๋ฅผ ์ฌ๋ฆผ.
child = parent.children[idx]
new_child = BPlusTreeNode(leaf=True)
# [10|20|30] -> [10], [20|30]
new_child.keys = child.keys[1:] # ์ฌ๊ธฐ๊ฐ ๋ค๋ฆ.
child.keys = child.keys[:1]
# [B+Tree] leaf linked list ์ฐ๊ฒฐ
new_child.next = child.next
child.next = new_child
# parent์ new_child ์ถ๊ฐ
parent.children.insert(idx + 1, new_child)
# ๋ถ๋ชจ์ ์์ ๋ชฉ๋ก์ ์ child๋ฅผ ์ฝ์
parent.keys.insert(idx, new_child.keys[0])
B+Tree์์ ๋ฆฌํ ๋ ธ๋์ Linked List ๊ตฌ์กฐ๊ฐ ๋ถํ ํ์๋ ์ด์ด์ ธ์ผ ํฉ๋๋ค. ๊ทธ๋์ paraent์ key ๋ชฉ๋ก์ mid_key๋ฅผ ์ถ๊ฐ๋ง ํ ๋ฟ, mid_key ์์ฒด๋ ์ฌ์ ํ ๋ฆฌํ ๋ ธ๋์ key ๋ชฉ๋ก์ ์กด์ฌํฉ๋๋ค.
def _split_child_internal(self, parent: BPlusTreeNode, idx: int):
# ๋ด๋ถ ๋
ธ๋๋ ๋ถ๋ชจ๋ก key๋ฅผ ์ด๋ ์์ผ์ผ ํจ(move-up).
child = parent.children[idx]
new_child = BPlusTreeNode(leaf=False)
# [10|20|30] -> [10], 20์ ๋ถ๋ชจ๋ก, [30]
promote_key = child.keys[1] # move-up ๋์
new_child.keys = child.keys[2:] # move-up์ผ๋ก ์ธํด ์ญ์
child.keys = child.keys[:1]
# child ๋ถํ
new_child.children = child.children[2:]
child.children = child.children[:2]
# parent์ new_child ์ถ๊ฐ
parent.children.insert(idx + 1, new_child)
# ๋ถ๋ชจ์ ์์ ๋ชฉ๋ก์ ์ child๋ฅผ ์ฝ์
parent.keys.insert(idx, promote_key)
ํ์ง๋ง, ๋ด๋ถ ๋ ธ๋๋ B-Tree์ ๋์ผํ๊ฒ ๋ ธ๋ ๋ถํ ์ด ์ด๋ค์ง๋๋ค. parent์ mid_key๋ฅผ ์ด๋์ํต๋๋ค(move-up).
Search
ํ์์ ์ข๋ ์ฝ๊ฒ ๊ตฌํ ํฉ๋๋ค.
class BPlusTree:
..
def search(self, key: int):
# internal node์ ์๋ key๋ ๊ธธ ์๋ด์ฉ key์.
# ์ค๊ฐ์ key์ ์ผ์นํ๋ ๊ฐ์ด internal node์ ์๋๋ผ๋, ํญ์ leaf node ๋๊น์ง ๋ด๋ ค๊ฐ์ผ ํจ.
leaf = self._find_leaf(self.root, key)
return key in leaf.keys
def _find_leaf(self, node: BPlusTreeNode, key: int):
"""
key๊ฐ ๋ค์ด์์ ๊ฐ๋ฅ์ฑ์ด ์๋ leaf node๊น์ง ๋ด๋ ค๊ฐ๋ ํจ์
B+Tree๋ ๊ฒ์์ด ํญ์ leaf์์ ๋๋จ.
"""
if node.leaf:
return node
else:
idx = self._find_child_index(node, key)
return self._find_leaf(node.children[idx], key)
์ค๊ฐ ๋ ธ๋์ ์๋ keys๋ค์ ์ฐธ๊ณ ํด ๋ฆฌํ ๋ ธ๋๊น์ง ๋๋ฌํฉ๋๋ค. ๋ฆฌํ ๋ ธ๋์ ๋๋ฌ ํ์ ๋, ์ฐพ๊ณ ์ ํ๋ key๊ฐ ๋ฆฌํ ๋ ธ๋์ ์กด์ฌํ๋์ง๋ฅผ ํ์ธ ํฉ๋๋ค.
๋งบ์๋ง
์ง์ ๊ตฌํํด๋ณด๋ ์ง๊ธ๊น์ง ์ ๋งคํ๊ณ ์ดํดํ๊ณ ๋ฐ์๋ค์๋ ๊ฒ๋ค์ด ๊ตฌ์ฒดํ ๋์๋ค. BST์ B-Tree์ ์ฐจ์ด์ , ๊ทธ๋ฆฌ๊ณ B-Tree์ B+Tree์ ์ฐจ์ด์ . ์ด๋ฐ ๊ฒ๋ค์ ์ด์ ์๋ ๋ค ๋น์ทํ ๊ฑฐ๋ผ๊ณ ์๊ฐํ๊ณ ๋์ด๊ฐ์๋ค.
ํ์ง๋ง, ์ด์ ์์์ด๋ค!! ๋ณธ๋ xfs, ext4 ํ์ผ ์์คํ
์ ์ดํดํ๋ ๊ณผ์ ์์ ์ด๊ฑธ ๋ง๋ฌ๊ธฐ ๋๋ฌธ์, ๋ค์ธ ์๊ฐ์ด ์จ์ ํ ๊ฐ์น๋ฅผ ๋ด๋ ค๋ฉด xfs, ext4๋ฅผ ์ดํดํ๋๋ฐ ์ด๊ฑธ ์จ๋จน์ด์ผ ํ๋ค.
์๋ฃ๊ตฌ์กฐ ์์ฒด๋ฅผ ๊ตฌํํ๋ ๋ฅ๋ ฅ์ ์ง๊ธ ์๋์ ์ค์ํ์ง ์๋ค. ์ค์ํ ๊ฑด ๋๊ตฌ๋ฅผ ์ธ์ /์/์ด๋ป๊ฒ ์จ์ผ ํ๋์ง๋ฅผ ์๋ ๊ฒ์ด๋ค.