forked from course/hse-2026
125 lines
4.5 KiB
Markdown
125 lines
4.5 KiB
Markdown
# Шардирование на примере простого key-value-хранилища
|
||
|
||
Этот пример показывает, как разделить данные между несколькими узлами. В стенде работают три простых
|
||
key-value-хранилища: каждое принимает ключ и значение и сохраняет их в памяти. Перед ними находится прокси
|
||
с единым API. Клиент обращается только к прокси, а тот определяет владельца ключа и пересылает запрос
|
||
нужному хранилищу.
|
||
|
||
В основном режиме прокси вычисляет `SHA-256(key) mod N` и направляет запись и чтение владельцу ключа.
|
||
В конце также кратко рассмотрен учебный режим репликации.
|
||
|
||
## API
|
||
|
||
- `POST /put?key=<key>` записывает значение из JSON-поля `value`.
|
||
- `GET /get?key=<key>` возвращает значение или `404`, если ключа нет на выбранном узле.
|
||
- `GET /state` показывает содержимое всех хранилищ.
|
||
|
||
## Запуск
|
||
|
||
Из корня репозитория:
|
||
|
||
```bash
|
||
cd materials/08-scaling/seminar/kv
|
||
docker compose up -d --build
|
||
docker compose ps
|
||
```
|
||
|
||
Прокси будет доступен на порту 5005. Хранилища будут доступны на портах 5001, 5002 и 5003.
|
||
|
||
## Запись и чтение
|
||
|
||
Записать одно значение через прокси:
|
||
|
||
```bash
|
||
curl -sS -X POST 'http://localhost:5005/put?key=key-0' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{"value":"value-0"}'
|
||
```
|
||
|
||
Прочитать его:
|
||
|
||
```bash
|
||
curl -sS 'http://localhost:5005/get?key=key-0'
|
||
```
|
||
|
||
Записать `N` значений с ключами от `key-0` до `key-(N-1)`:
|
||
|
||
```bash
|
||
N=20
|
||
|
||
for ((i = 0; i < N; i++)); do
|
||
curl -sS -X POST "http://localhost:5005/put?key=key-$i" \
|
||
-H 'Content-Type: application/json' \
|
||
-d "{\"value\":\"value-$i\"}"
|
||
echo
|
||
done
|
||
```
|
||
|
||
## Проверка распределения
|
||
|
||
Получить состояние всех узлов через прокси:
|
||
|
||
```bash
|
||
open 'http://localhost:5005/state'
|
||
```
|
||
|
||
Состояние отдельных хранилищ:
|
||
|
||
```bash
|
||
curl -sS 'http://localhost:5001/state'
|
||
curl -sS 'http://localhost:5002/state'
|
||
```
|
||
|
||
Для текущей конфигурации `key-0` находится на третьем узле:
|
||
|
||
```bash
|
||
curl -i 'http://localhost:5003/get?key=key-0' # 200
|
||
curl -i 'http://localhost:5002/get?key=key-0' # 404
|
||
```
|
||
|
||
## Изменение конфигурации
|
||
|
||
Состав узлов задаётся в `proxy/compose_config.yml`:
|
||
|
||
```yaml
|
||
mode: sharding
|
||
nodes:
|
||
- name: node1
|
||
url: http://kv1:5000
|
||
- name: node2
|
||
url: http://kv2:5000
|
||
- name: node3
|
||
url: http://kv3:5000
|
||
```
|
||
|
||
Конфигурация подключена к контейнеру как файл только для чтения. После её изменения достаточно
|
||
перезапустить прокси:
|
||
|
||
```bash
|
||
docker compose restart proxy
|
||
```
|
||
|
||
Хранилища при этом не перезапускаются, и данные в их памяти сохраняются.
|
||
|
||
## Режим репликации
|
||
|
||
В режиме `replication` прокси записывает каждое значение во все хранилища. При чтении он опрашивает
|
||
узлы по очереди и возвращает первый успешный ответ. В отличие от шардирования, данные не разделяются,
|
||
а копируются.
|
||
|
||
Чтобы включить этот режим, изменить первую строку в `proxy/compose_config.yml`:
|
||
|
||
```yaml
|
||
mode: replication
|
||
```
|
||
|
||
Затем перезапустить только прокси и повторить команды записи и чтения:
|
||
|
||
```bash
|
||
docker compose restart proxy
|
||
open 'http://localhost:5005/state'
|
||
```
|
||
|
||
После записи новый ключ должен появиться на всех трёх узлах. Это упрощённая учебная реализация:
|
||
она не гарантирует согласованность при частичных записях и не обрабатывает отказы как промышленная система.
|