Чтение из дерева — это просто спуск. Но запись (INSERT/UPDATE) гораздо сложнее. База должна не просто записать данные, но и перестроить дерево так, чтобы оно осталось сбалансированным.
Проблема: Разделение узла
Поскольку каждый узел дерева имеет фиксированный размер (страницу диска 4-8 КБ), в него нельзя впихивать ключи бесконечно. Когда место заканчивается, происходит разделение узла (в англ литературе это называется node split):
- Разрыв: Узел делится пополам. Создается новая страница на диске.
- Переезд: Половина ключей переезжает в новый узел.
- Эффект домино: Средний ключ выталкивается наверх в родительский узел. Если родитель тоже полон — он тоже рвется. В редких случаях дерево может вырасти на один уровень вверх прямо до самого корня.
WAL (Write-Ahead Log)
Разделение узла — это долго. Нужно создать новые страницы, обновить указатели в старых, прописать ссылки в родителе. А что, если в середине этого процесса выключится свет? Дерево окажется в «разорванном» состоянии и индекс будет навсегда испорчен.
Чтобы этого не случилось, все СУБД используют WAL (Журнал упреждающей записи):
- Сначала Лог: Любое изменение сначала записывается в текстовый файл-лог (Append-only). Это очень быстро, потому что диск просто дописывает строку в конец.
- Потом Дерево: Только после того, как лог сохранен на диске, база начинает перестраивать само B+ дерево в памяти и на диске.
- Восстановление: Если сервер упал во время сплита, при перезагрузке база прочитает WAL и «доиграет» все незавершенные операции. Твои данные в безопасности.
Именно WAL позволяет базам данных гарантировать Durability (Надежность) из ACID. Если база ответила тебе «ОК», значит запись уже точно в WAL-логе.