VShard - горизонтальное масштабирование в Tarantool
Шпилевой Владислав
Tarantool
План доклада
- Зачем?
- Shard
- VShard
- Использование
- Ребалансировка
- Прокси
- Auto read failover
- Write failover
- Мониторинг
New version: https://slides.com/gerold103/vshard-highload
Зачем масштабировать?
Место
Вычисления
Горизонтальное масштабирование
Репликация
ABCD
ABCD
ABCD
ABCD
Горизонтальное масштабирование
Шардинг
A
B
C
D
Шардинг
Range
0
1900
1970
2018
2001
1895
1965
SELECT * FROM movie WHERE year > 1950;
Шардинг
Hash
Lord of The Rings
The Arrival of a Train
Операция Ы
SELECT * FROM movie WHERE name = "Lord of The Rings";
Tarantool Shard
function shard_function(primary_key)
return guava(crc32(primary_key))
end
Первый шардинг в Tarantool
Request
old hash
new hash
Нет локальности
Медленный решардинг
Нестабильные чтения
SELECT
master
Tarantool Shard
format = {{'id', 'unsigned'},
{'email', 'string'}}
box.schema.create_space('customer', {format = format})
format = {{'id', 'unsigned'},
{'customer_id', 'unsigned'},
{'balance', 'number'}}
box.schema.create_space('account', {format = format})
user {100, 'foo@mail.ru'}
account {1, 100, 123}
100 != 1 → разные хеши → разные узлы
Tarantool VShard
Buckets
Replicaset
Data → Bucket → Storage
User defined map
Internal map
format = {{'id', 'unsigned'},
{'email', 'string'},
{'bucket_id', 'unsigned'}}
box.schema.create_space('customer', {format = format})
format = {{'id', 'unsigned'},
{'customer_id', 'unsigned'},
{'balance', 'number'},
{'bucket_id', 'unsigned'}}
box.schema.create_space('account', {format = format})
Локальность по bucket_id
Связка данных
user { 100, 'bar@mail.ru', 5 }
account {1, 100, 123, 5 }
Использование
5
Можно еще раз? Что это дает?
Связка данных
Использование
customer {1, 'bar@mail.ru'}
account {1, 1, 100}
account {2, 1, 200}
account {3, 1, 300}
SELECT * FROM accounts WHERE customer_id = 1;
3 запроса
customer {1, 'bar@mail.ru', 5}
account {1, 1, 100, 5}
account {2, 1, 200, 5}
account {3, 1, 300, 5}
1 запрос
...
...
5
Shard
VShard
Использование
function create_user(email)
local customer_id = next_id()
local bucket_id = crc32(customer_id)
box.space.customer:insert(customer_id, email, bucket_id)
end
function add_account(customer_id)
local id = next_id()
local bucket_id = crc32(customer_id)
box.space.account:insert(id, customer_id, 0, bucket_id)
end
Как масштабировать?
Использование
#
= Const
Реконфигурация
vshard.storage.cfg(new_config)
Failover
Rebalancer
Zookeeper
new_cfg.sharding[new_uuid] = {
uuid = new_uuid,
replicas = {
{
name = new_replica1,
uri = 'example.com:113',
uuid = replica1_uuid,
master = true,
},
{
name = new_replica2,
uri = 'example.com:114',
uuid = replica2_uuid,
}
}
}
vshard.storage.cfg(new_cfg)
Ребалансировка
- вычислить баланс
- построить маршруты бакетов
- отправить маршруты
Routes
Routes
vshard.storage.info()
100
200
#
= 1000
#
= 2000
new_cfg.sharding[uuid1].weight = 100
new_cfg.sharding[uuid2].weight = 200
vshard.storage.cfg(new_cfg)
Data
Ребалансировка
Read
Write
Сеть
Read
Write
Read
Как не блокировать запись?
Write
Bucket Pin
box.begin()
vshard.storage.bucket_pin(10)
vshard.storage.bucket_pin(11)
box.commit()
--
-- Do anything with these buckets.
--
box.begin()
vshard.storage.bucket_unpin(10)
vshard.storage.bucket_unpin(11)
box.commit()
Replicaset Lock
new_cfg.sharding[uuid].lock = true
vshard.storage.cfg(new_cfg)
Что осталось?
- Нет локальности
- Решардинг замедляет все
- Нестабильные чтения
?
Прокси
bucket_id → storage
vshard.router
Прокси
r = vshard.router
accounts = r.call(bucket_id,
'get_accounts',
{customer_id})
function get_accounts(customer_id)
local customer =
box.space.accounts.index.customer
return customer:select({customer_id})
end
Routing
?
Развернуть
функцию на
стораджах
Auto read failover
Write
Read
Зона 1
Зона 2
Зона ...
Зона K - 1
Зона K
Зона N
Зона 1 | Зона 2 | ... | Зона K | |
Зона 1 | w(1, 1) | w(1, 2) | w(1, i) | w(1, K) |
Зона 2 | w(2, 1) | w(2, 2) | w(2, i) | w(2, K) |
... | w(j, 1) | w(j, 2) | w(j, i) | w(j, K) |
Зона K | w(K, 1) | w(K, 2) | w(K, i) | w(K, K) |
Auto read failover
new_cfg.weights = {
[1] = {
[1] = 0,
[2] = 100,
[3] = 200,
},
[2] = {
[1] = 300,
[2] = 0,
[3] = 400,
},
[3] = {
[1] = 500,
[2] = 600,
[3] = 0,
}
}
new_cfg.zone = 1
vshard.router.cfg(new_cfg)
Зона 1
Зона 2
Зона 3
100
200
300
400
500
600
Write failover
replicas = new_cfg.sharding[uud].replicas
replicas[old_master_uuid].master = false
replicas[new_master_uuid].master = true
vshard.storage.cfg(new_cfg)
Read
Write
Be Master
Write
Write
Sync
Read
Write
Мониторинг
vshard.storage.info()
---
- replicasets:
<replicaset_2>:
uuid: <replicaset_2>
master:
uri: storage@127.0.0.1:3303
<replicaset_1>:
uuid: <replicaset_1>
master: missing
bucket:
receiving: 0
active: 0
total: 0
garbage: 0
pinned: 0
sending: 0
status: 2
replication:
status: slave
alerts:
- ['MISSING_MASTER', 'Master is not configured for '
'replicaset <replicaset_1>']
vshard.storage.buckets_info()
Мониторинг
vshard.router.info()
---
- replicasets:
<replicaset_2>:
replica: &0
status: available
uri: storage@127.0.0.1:3303
uuid: 1e02ae8a-afc0-4e91-ba34-843a356b8ed7
bucket:
available_rw: 500
uuid: <replicaset_2>
master: *0
<replicaset_1>:
replica: &1
status: available
uri: storage@127.0.0.1:3301
uuid: 8a274925-a26d-47fc-9e1b-af88ce939412
bucket:
available_rw: 400
uuid: <replicaset_1>
master: *1
bucket:
unreachable: 0
available_ro: 800
unknown: 200
available_rw: 700
status: 1
alerts:
- ['UNKNOWN_BUCKETS', '200 buckets are not discovered']
Итоги
... локальность данных,
... гранулярный решардинг,
... auto read failover