шардинг на коленке
TRANSCRIPT
Стратегии шардинга
Линейная – сперва заполняется одна часть, потом следующая, и т.д.
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Гео – данные распределяются по ключам, или префиксам
Москва Н.НовгородСанкт-Петербург
Стратегии шардинга
Гео – данные распределяются по ключам, или префиксам
Москва Н.НовгородСанкт-Петербург
Нереализованно
Стратегии шардинга
Гео – данные распределяются по ключам, или префиксам
Москва Н.НовгородСанкт-Петербург
Нереализованно
Не рассматриваем
Стратегии шардинга
Линейная – сперва заполняется одна часть, потом следующая, и т.д. И так далее...
Пока хватит места
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
Стратегии шардинга
Циклическая - сперва пише одну запись впервую часть, следующую во вторую, потом в третью... и так до конца, потом перехдим к первой...
И так,пока не заполнится
Линейнная стратегия: INSERT INTO tab_? (data) VALUES ($data)
не знаем в какую таблицу вставлять данные
Что за кадром ?
Линейнная стратегия: INSERT INTO tab_0 (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
Под капотом
nnnStrategy – набор стратегий вычисляет $tab_id, $db_id
MySQLShard – по шаблону строим SQL запрос – открываем connection(...) – выполняем запрос – отдаем ответ
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]); }}
Асинхронность
Драйвер 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(); // запросы не отсылаем
echo $shard->getQuery();// вывод запроса.
Отладка
$shard = new MysqlShard($conf);
$shard->noExec(); // запросы не отсылаем
echo $shard->getQuery();// вывод запроса.
$shard->checkDeveloperConfig(); // проверка
на тип конфига
РешардингШаг 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● Привести к 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 и скомпилить как модуль.