124 lines
4.5 KiB
Markdown
124 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'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
После записи новый ключ должен появиться на всех трёх узлах. Это упрощённая учебная реализация:
|
|||
|
|
она не гарантирует согласованность при частичных записях и не обрабатывает отказы как промышленная система.
|