шардинг на коленке

127
Шардинг на коленке Календарев А.М. OTG

Upload: alexandre-kalendarev

Post on 12-Feb-2017

85 views

Category:

Internet


1 download

TRANSCRIPT

Шардинг на коленке

Календарев А.М.

OTG

Шардинг на коленкеhttps://github.com/akalend/mysql-shard

Немного скучной Теории

Можно поспать

Немного скучной Теории

Можно поспать

Шардинг - теория

Шардинг – метод масштабирования БД, в котором база данных делится на меньшие части

Шардинг - теория

БД-1

БД-2

БД-n

. . .

Шардинг - теория

БД-1

БД-2

БД-n

. . .

Таких способов разбиенияможет быть несколько

Шардинг - теория

Стратегия шардинга -принцип разбиения БД

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Москва

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Москва Санкт-Петербург

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Москва Н.НовгородСанкт-Петербург

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Москва Н.НовгородСанкт-Петербург

Нереализованно

Стратегии шардинга

Гео – данные распределяются по ключам, или префиксам

Москва Н.НовгородСанкт-Петербург

Нереализованно

Не рассматриваем

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д.

Стратегии шардинга

Линейная – сперва заполняется одна часть, потом следующая, и т.д. И так далее...

Пока хватит места

Линейная стратегия

№shr = ceil(№rec / Tmax)

Линейная стратегия

№shr = ceil(№rec / Tmax)

 №shr номер шарды

№rec номер записи

Tmax  размер таблицы

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

Стратегии шардинга

Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...

И так,пока не заполнится

Циклическая стратегия

Циклическая стратегия

№shr = №rec % Sdb

Циклическая стратегия

 №shr номер шарды

№rec номер записи

Sdb  кол-во шард№shr = №rec % Sdb

Циклическая стратегия

Недостаток – очень трудно масштабируется

Циклическая стратегия

Недостаток – очень трудно масштабируется

+

Циклическая стратегия

Недостаток – очень трудно масштабируется

+ ?

РешардингЧуть позже

Pftterns & Anti Patternshttp://highload.guide/blog/sharding-patterns-and-antipatterns.html

Что за кадром ?

Линейнная стратегия: INSERT INTO tab_? (data) VALUES ($data)

не знаем в какую таблицу вставлять данные

Что за кадром ?

Линейнная стратегия: INSERT INTO tab_0 (data) VALUES ($data)

не знаем в какую таблицу вставлять данные

Что за кадром ?Храним в редисНомер таблицы

Какие ещё осталисьПодводные камни

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 1 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 1 INSERT INTO tab (data) VALUES ($data)0 2 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 1 INSERT INTO tab (data) VALUES ($data) 0 2 INSERT INTO tab (data) VALUES ($data)

. . .0 98 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 1 INSERT INTO tab (data) VALUES ($data) 0 2 INSERT INTO tab (data) VALUES ($data)

. . .0 98 INSERT INTO tab (data) VALUES ($data) 0 99 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 98 INSERT INTO tab (data) VALUES ($data) 0 99 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 98 INSERT INTO tab (data) VALUES ($data) 0 99 INSERT INTO tab (data) VALUES ($data) 0 100 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 98 INSERT INTO tab (data) VALUES ($data) 0 99 INSERT INTO tab (data) VALUES ($data) 0 100 INSERT INTO tab (data) VALUES ($data)0 101 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 98 INSERT INTO tab (data) VALUES ($data) 0 99 INSERT INTO tab (data) VALUES ($data) 0 100 INSERT INTO tab (data) VALUES ($data)0 101 INSERT INTO tab (data) VALUES ($data)1 100 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Конкуренция при вставке: какие варианты? - Используем блокировки

- Резервируем место

Линейнная стратегия

Конкуренция при вставке: какие варианты? - Используем блокировки

- Резервируем место

ТеряемСкорость

Линейнная стратегия

Конкуренция при вставке: Sh № INSERT INTO tab (data) VALUES ($data)

0 1 INSERT INTO tab (data) VALUES ($data)

0 2 INSERT INTO tab (data) VALUES ($data)

. . .

0 79 INSERT INTO tab (data) VALUES ($data)

0 80 INSERT INTO tab (data) VALUES ($data)

0 81 INSERT INTO tab (data) VALUES ($data)

1 100 INSERT INTO tab (data) VALUES ($data)

Линейнная стратегия

Табличное пространство

Bd_0

Tab_nTab_1

Tab_0

Bd_0

Tab_nTab_1

Tab_0Одна База – несколько таблиц,кол-во определяется в конфиге

Табличное пространство

Bd_0 Bd_n

Один экземпляр MySQL - несколько Баз Данных,кол-во определяется в конфиге

. . .

Табличное пространство

Один экземпляр MySQL - несколько Баз Данных,кол-во определяется в конфиге

. . . Один физический сервер,несколько экземпляровMySQL (как правило 2)

Табличное пространство

Bd_0

Tab_nTab_1

Tab_0

Bd_n

Tab_n+mTab_n+2

Tab_n+1

Bd_0

Tab_nTab_1

Tab_0

Bd_x

Tab_z+nTab_Z+1

Tab_z

. . . . . .. . . Bd_xn

. . .

Конфигурационный файлreturn [ 'profile' => 'local', // this parameter for check testing 'user' => 'akalend', 'pass' => '12345', // password for db: instance 1 or 2 'instance' => [ [ 'host' => '127.0.0.1', 'port' => 3306, 'db' => [ 'lines' => [0,1,4,6,8], 'months' => [0,1], ], [ 'host' => '127.0.0.1', 'port' => 3307, 'db' => [ 'lines' => [3,5,7,9,10], 'months' => [2,3,4], ], ];

Конфигурационный файлreturn [ 'profile' => 'local', // this parameter for check testing 'user' => 'akalend', 'pass' => '12345', // password for db: instance 1 or 2 'instance' => [ [ 'host' => '127.0.0.1', 'port' => 3306, 'db' => [ 'lines' => [0,1,4,6,8], 'months' => [0,1], ], [ 'host' => '127.0.0.1', 'port' => 3307, 'db' => [ 'lines' => [3,5,7,9,10], 'months' => [2,3,4], ], ];

lines_0 lines_1 lines_4 lines_6

lines_8 month_0 month_1

Конфигурационный файлreturn [ 'profile' => 'local', // this parameter for check testing 'user' => 'akalend', 'pass' => '12345', // password for db: instance 1 or 2 'instance' => [ [ 'host' => '127.0.0.1', 'port' => 3306, 'db' => [ 'lines' => [0,1,4,6,8], 'months' => [0,1], ], [ 'host' => '127.0.0.1', 'port' => 3307, 'db' => [ 'lines' => [3,5,7,9,10], 'months' => [2,3,4], ], ];

lines_3 lines_5 lines_7 lines_9

lines_10 month_2 month_3 month_4

ЧтоПод капотом?

Под капотом

MySQLShard Strategy

AbstractStrategyConfig

Под капотом

nnnStrategy – набор стратегий вычисляет $tab_id, $db_id

MySQLShard – по шаблону строим SQL запрос – открываем connection(...) – выполняем запрос – отдаем ответ

Класс MySQLShard

Начинка – MySQLi,Ранние версии был PDO

MySQLi – поддерживает асинхронность

Класс MySQLShard

Начинка – MySQLi,Ранние версии был PDO

MySQLi – поддерживает асинхронность

SQL Шаблоны

SELECT * FROM %db.clicks_%t WHERE ….

UPDATE %db.users_%t SET …. WHERE …

%db - подставляется актуальная БД%t - подставляется актуальное значение

номера таблицы

И вот мы шли, шли, шли,И наконец - дошли

Пример

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->query( "INSERT INTO `%db`.`xdata` (id,data)

VALUES( $i, 'xxx')", $strategy);}

Пример

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->query( "INSERT INTO `%db`.`xdata` (id,data)

VALUES( $i, 'xxx')", $strategy);}

конфиг

Имя БД_%n

Имя таблицы

Привязка Стратегии

Пример

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->query( "INSERT INTO `%db`.`xdata` (id,data)

VALUES( $i, 'xxx')", $strategy);}

id = nullТак как мы вставляем

Записи пачками

Тут как разНазначаем конкретный Id

Пример

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->query( "INSERT INTO `%db`.`xdata` (id,data)

VALUES( $i, 'xxx')", $strategy);}

id = nullТак как мы вставляем

Записи пачками

Тут как разНазначаем конкретный Id

А теперь выполняем запрос с заданной стратегией

Пример 2

$shard = new MysqlShard();$id= 21;$strategy = new CycleStrategy($id, 'months', $shard->getConfig());$shard->query( "SELECT * FROM `%db`.`xdata` WHERE id=$id", $strategy);

Пример 2

$shard = new MysqlShard();$id= 21;$strategy = new CycleStrategy($id, 'months', $shard->getConfig());$shard->query( "SELECT * FROM `%db`.`xdata` WHERE id=$id", $strategy);

Тут как разНазначаем конкретный Id

Пример 2

$shard = new MysqlShard();$id= 42;$strategy = new CycleStrategy($id, 'months', $shard->getConfig());$shard->query( "SELECT * FROM `%db`.`xdata` WHERE id=$id", $strategy);

А теперь выполняем запрос с заданной стратегией

Тут как разНазначаем конкретный Id

Пример 3

$shard = new MysqlShard();$strategy = new LinearStrategy(null,'lines', $shard->getConfig());for ($i = 0; $i < 42; $i++) { $sql = "INSERT INTO %db.lines_%t (ts,ua_id) VALUES($time, $tab_id)"; $shard->query($sql, $strategy); }

Пример 3

$shard = new MysqlShard();$strategy = new LinearStrategy(null,'lines', $shard->getConfig());for ($i = 0; $i < 42; $i++) { $sql = "INSERT INTO %db.lines_%t (ts,ua_id) VALUES(time(), $id)"; $shard->query($sql, $strategy); }

id = nullТак как мы не знаем id

Маркетинги

Аналитика

Аналитика в шардингеНужно пробежаться по всем нардам и выбрать данные:

$shard = new MysqlShard();

$strategy = new MonthStrategy(null, 'lines', $shard->getConfig(), '2016-06');

$shard->setStrategy($strategy);

foreach ($shard as $shardItem) {

$query = “SELECT * FROM %db.tab_%t WHERE city_id = 42”;

$res = $shardItem->query( $query, $strategy);

while(($row = $res->fetch_array()) != null ) { …. } // идет обработка строк

}

Создание таблиц

Создание таблиц. Линейная стратегия.

Поле id avto_increment

В каждой новой шарде необходимо продолжать инкрементировать значение, а не начинать с нуля:

Создание таблиц. Линейная стратегия.

$table = 'CREATE TABLE %%db.lines_%s (

id bigint unsigned NOT NULL AUTO_INCREMENT,

ts int DEFAULT NULL,

ua_id int DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=%d';

Создание таблиц. Линейная стратегия.$table = 'CREATE TABLE %%db.lines_%s (

id bigint unsigned NOT NULL AUTO_INCREMENT,

ts int DEFAULT NULL,

ua_id int DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=%d';

$begin_id = LinearStrategy::MAX_RECORD_COUNT * $shard_id;

$sql = sprintf(self::table, $shard_id, $begin_id);

Создние таблиц. Линейная стратегия.

$shard= new MysqlShard($conf);$strategy = new LinearStrategy(null, 'lines', $shard->getConfig());for ($i = 0 to $i < 42; $i++) {

$sql = LinesGenerator::getCreateTableSQL($i);

$shard->query($sql,$strategy);

}

Создние таблиц. Циклическая стратегия.

$shard= new MysqlShard($conf);$strategy = new CycleStrategy(null, 'stats', $shard->getConfig());foreach ($shard as $shardItem) {

$sql = StatsGenerator::getCreateTableSQL();

$shard->query($sql,$strategy);

}

Расширяемость

Расширяемость

Пишем свой класс MySuperPuperStrategy,который реализует нашу стратегию.

Переопределяем Методы: function checkInserted($last_id); function getShardId(); function getTabId(); function getKey();

Расширяемость

Класс MonthlyStrategyclass MonthStrategy extends CycleStrategy{

public function getTabId() { if ($this->_params == null) throw new \Exception('must be set data as parameter');

$day = \MysqlShard\Tools\Day::convertMysqlToArray($this->_params);

return sprintf( "{$day[0]}_%'02d", $day[1]); }}

Расширяемость

Класс MonthlyStrategy

Bd_0

Stats_2016_08 Stats_2016_09 Stats_2016_10 Stats_2016_11

Асинхронность

Асинхронность

Драйвер mysqlnd поддерживает асинхронность,

Давайте попробуем ?

Асинхронность

Драйвер mysqlnd поддерживает асинхронность,

Давайте попробуем ?

SQL Запрсы

Connection

Connection

Connection

Асинхронность

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->push( "INSERT INTO `%db`.`xdata` (id,data) VALUES( $i, 'xxx')", $strategy);}$shard->flush();

Асинхронность

$shard = new MysqlShard($config);$strategy = new CycleStrategy(null, 'months', $shard->getConfig());$shard->setStrategy($strategy);

for ($i=1; $i < 100; $i++ ) {$strategy->setId($i);$res = $shard->push( "INSERT INTO `%db`.`xdata` (id,data) VALUES( $i, 'xxx')", $strategy);}$shard->flush();

Отправляем запросыкаждый

в свой пул

Выполняем запросыкаждый

в своей коннекции

В поисках жуков

Отладка

$shard = new MysqlShard($conf);

$shard->noExec(); // запросы не отсылаем

Отладка

$shard = new MysqlShard($conf);

$shard->noExec(); // запросы не отсылаем

echo $shard->getQuery();// вывод запроса.

Отладка

$shard = new MysqlShard($conf);

$shard->noExec(); // запросы не отсылаем

echo $shard->getQuery();// вывод запроса.

$shard->checkDeveloperConfig(); // проверка

на тип конфига

Решардинг

Решардинг

Такая операция, которой лучше избегать

Решардинг

Шаг 1 – пишем заглушку стратегии

РешардингШаг 1 – пишем заглушку стратегии

MySQLShard Strategy

AbstractStrategy

Config

РешардингШаг 1 – пишем заглушку стратегии

MySQLShard Strategy

AbstractStrategy

ConfigБыло

РешардингШаг 1 – пишем заглушку стратегии

MySQLShard Strategy

AbstractStrategy

Config

Копируем код Strategyв StabStrategy

И переименовываем,Создаем класс пустышку

Strategy

StabStrategy

РешардингШаг 2 – Определяем Id начала Решардинга

MySQLShard Strategy

AbstractStrategy

Config

Реализуем новые методыв Strategy,

checkInserted( $id )StabStrategy

РешардингШаг 1 – пишем заглушку стратегии

Шаг 2 – Определяем Id начала Решардинга

Шаг 3 – Реализуем новый алгоритм Strategy

Шаг 4 – Проверяем checkInserted($id)

Шаг 5 – При checkInserted($id) = true, перегружаем конфиг.

Шаг 6 – Приводим код к первоночальному виду

А планов наших -громадьё

Планы

● Прокси

MySQLShardProxy

Планы

● Прокси MySQLShardProxy● Привести к PSR-4● Сделать интеграцию с популрными фреймворками:

Slim, Laravel/Lumen, Simphony…● Реализовать пулы для INSERT● Интегрировать с Phalcon,● Переписать на Zephir и скомпилить как модуль.

Планы

● Прокси MySQLShardProxy● Привести к PSR-4● Сделать интеграцию с популрными фреймворками:

Slim, Laravel/Lumen, Simphony…● Реализовать пулы для INSERT● Интегрировать с Phalcon,● Переписать на Zephir и скомпилить как модуль.

Планы

● Прокси MySQLShardProxy● Привести к PSR-4● Сделать интеграцию с популрными фреймворками:

Slim, Laravel/Lumen, …● Реализовать пулы для INSERT● Интегрировать с Phalcon,● Переписать на Zephir и скомпилить как модуль.

ПланыРеализовать пулы для INSERT

INSERT INTO tab (id, data) VALUES (1,'xxx-1')

INSERT INTO tab (id, data) VALUES (2,'xxx-2')

INSERT INTO tab (id, data) VALUES (3, 'yyy-1')

INSERT INTO tab (id, data) VALUES (4, 'yyy-2')

INSERT INTO tab (id, data) VALUES (5, 'zzz-1')

INSERT INTO tab (id, data) VALUES (6, 'zzz-2')

INSERT INTO tab (id,data, ) VALUES (1,'xxx-1'),(3,'yyy-1'),(5,'zzz-1')

INSERT INTO tab (id,data, ) VALUES (2,'xxx-2'),(4,'yyy-2'),(6,'zzz-2')

Планы

● Прокси MySQLShardProxy● Привести к PSR-4● Сделать интеграцию с популрными фреймворками:

Slim, Laravel/Lumen, …● Реализовать пулы для INSERT● Интегрировать с Phalcon,● Переписать на Zephir и скомпилить как модуль.

Что почитать?

2016 № 9

Еще раз про шардинг

Cпасибо за внимание