Thank you for reading this post, don't forget to subscribe!
Масштабирование баз данных — самая сложная задача во время роста проекта. 90% всех усилий обычно приходится как раз на работу, связанную с ростом объема данных и операций с ними. Классическая схема работы приложения с базой данных выглядит так:
Один сервер базы данных в какой-то момент перестает справляться с нагрузкой. В этот момент и следует применять описанные тут техники масштабирования.
Перед тем, как приступать к масштабированию, необходимо провести анализ медленных запросов и убедиться, что сервер MySQL настроен оптимально.
Стратегии
В основе масштабирования данных лежит тот же принцип, что и в основе масштабирования Web приложений. Это разделение данных на группы и выделение их на отдельные сервера. Существует две основные стратегии — репликация и шардинг.
Репликация
Репликация позволяет создать полный дубликат базы данных. Так, вместо одного сервера у Вас их будет несколько:
Master-slave
Чаще всего используют схему master-slave:
- Master — это основной сервер БД, куда поступают все данные. Все изменения в данных (добавление, обновление, удаление) должны происходить на этом сервере.
- Slave — это вспомогательный сервер БД, который копирует все данные с мастера. С этого сервера следует читать данные. Таких серверов может быть несколько.
Репликация позволяет использовать два или больше одинаковых серверов вместо одного. Операций чтения (SELECT) данных часто намного больше, чем операций изменения данных (INSERT/UPDATE). Поэтому, репликация позволяет разгрузить основной сервер за счет переноса операций чтения на слейв.
Работа из приложения
В приложении у Вас будет два соединения с базой данных. Одно — для мастера и одно для слейва:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<? <b>$master</b> = mysql_connect('10.10.0.1', 'root', 'pwd'); <em>$slave</em> = mysql_connect('10.10.0.2', 'root', 'pwd'); <span class="comment"> # какой-то код и все такое… </span> $q = mysql_query('INSERT INTO users …', <b>$master</b>); <span class="comment"> # еще какой-то код… </span> $q = mysql_query('SELECT * FROM users WHERE…', <em>$slave</em>); |
# При выполнении запросов необходимо использовать соответствующее соединение
Репликация обычно поддерживается самой СУБД (например, MySQL) и настраивается независимо от приложения. Читайте детальнее про настройку, использование и типы репликации данных на примере MySQL.
Следует отметить, что репликация сама по себе не очень удобный механизм масштабирования. Причиной тому — рассинхронизация данных и задержки в копировании с мастера на слейв. Зато это отличное средство для обеспечения отказоустойчивости. Вы всегда можете переключиться на слейв, если мастер ломается и наоборот. Чаще всего репликация используется совместно с шардингом именно из соображений надежности.
Шардинг (sharding)
Шардинг (иногда шардирование) — это другая техника масштабирования работы с данными. Суть его в разделении (партиционирование) базы данных на отдельные части так, чтобы каждую из них можно было вынести на отдельный сервер. Этот процесс зависит от структуры Вашей базы данных и выполняется прямо в приложении в отличие от репликации:
Вертикальный шардинг
Вертикальный шардинг — это выделение таблицы или группы таблиц на отдельный сервер. Например, в приложении есть такие таблицы:
- users — данные пользователей
- photos — фотографии пользователей
- albums — альбомы пользователей
Таблицу users Вы оставляете на одном сервере, а таблицы photos и albums переносите на другой. В таком случае в приложении Вам необходимо будет использовать соответствующее соединение для работы с каждой таблицей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<? <b>$users_connection</b> = mysql_connect('10.10.0.1', 'root', 'pwd'); <em>$photos_connection</em> = mysql_connect('10.10.0.2', 'root', 'pwd'; <span class="comment"> # какой-то код и все такое… </span> $q = mysql_query('SELECT * FROM <b>users</b> WHERE …', <b>$users_connection</b>); <span class="comment"> # еще какой-то код… </span> $q = mysql_query('SELECT * FROM <em>photos</em> WHERE…', <em>$photos_connection</em>); <span class="comment"> # еще какой-то код… </span> $q = mysql_query('SELECT * FROM <em>albums</em> WHERE…', <em>$photos_connection</em>); |
# Для каждой таблицы или группы таблиц будет отдельное соединение
В отличие от репликации, мы используем разные соединения для любых операций, но с определенными таблицами. Читайте подробнее об использовании вертикального шардинга на практике.
Горизонтальный шардинг
Горизонтальный шардинг — это разделение одной таблицы на разные сервера. Это необходимо использовать для огромных таблиц, которые не умещаются на одном сервере. Разделение таблицы на куски делается по такому принципу:
- На нескольких серверах создается одна и та же таблица (только структура, без данных).
- В приложении выбирается условие, по которому будет определяться нужное соединение (например, четные на один сервер, а нечетные — на другой).
- Перед каждым обращением к таблице происходит выбор нужного соединения.
Допустим, наше приложение работает с огромной таблицей, которая хранит фотографии пользователей. Мы подготовили два сервера (обычно они называются шардами) для этой таблицы. Для нечетных пользователей мы будем работать с первыми сервером, а для четных — со вторым. Таким образом, на каждом из серверов будет только часть всех данных о фотках пользователей. Это будет выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<? <span class="comment"> # список соединений для таблицы с фотками </span>$photo_connections = [ '1' => '10.10.0.1', '2' => '10.10.0.2', ]; $user_id = $_SESSION['user_id']; <span class="comment"> # получение фотографий для пользователя $user_id </span><b>$connection_num = $user_id % 2 == 0 ? 1 : 2;</b> <b>$connection</b> = mysql_connect(<b>$photo_connections[$connection_num]</b>, 'root', 'pwd'); $q = mysql_query('SELECT * FROM photos WHREE user_id = ' . intval($user_id), <b>$connection</b>); |
# Перед обращением к таблице, мы выбираем нужное нам соединение
Результат вот этой операции $user_id % 2 будет остатком от деления на 2. Т.е. для четных чисел — 0, а для нечетных — 1.
Любая работа с таблицей photos теперь будет происходить только после получения нужного соединения на основе $user_id.
Горизонтальный шардинг — это очень мощный инструмент масштабирования данных. Но в то же время и очень нетривиальный. Читайте детально об использовании горизонтального шардинга на практике.
Не следует применять технику шардинга ко всем таблицам. Правильный подход — это поэтапный процесс разделения растущих таблиц. Следует задумываться о горизонтальном шардинге, когда количество записей в одной таблице переходит за пределы от нескольких десятков миллионов до сотен миллионов.
Совместное использование
Шардинг и репликация часто используются совместно. В нашем примере, мы могли бы использовать по два сервера на каждый шард таблицы:
- photos_master_1 — мастер первой половины таблицы.
- photos_slave_1 — слейв первой половины таблицы.
- photos_master_2 — мастер второй половины таблицы.
- photos_slave_2 — слейв второй половины таблицы.
Тогда в приложении работа с этой табличкой может выглядеть так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<? <span class="comment"> # список соединений, для каждого шарда - мастер и слейв </span>$photo_connections = [ '1' => [ 'master' => '10.10.0.10', 'slave' => '10.10.0.11', ], '2' => [ 'master' => '10.10.0.20', 'slave' => '10.10.0.21', ], ]; $user_id = $_SESSION['user_id']; <span class="comment"> # Читаем данные со слейвов </span>$connection_num = $user_id % 2 == 0 ? 1 : 2; $connection = mysql_connect($photo_connections[$connection_num]<b>['slave']</b>, 'root', 'pwd'); $q = mysql_query('SELECT * FROM photos WHREE user_id = ' . intval($user_id), $connection); <span class="comment"> # какой-то код… </span> <span class="comment"> # Изменение данных происходит на мастерах </span>$photo_id = 7; $connection_num = $user_id % 2 == 0 ? 1 : 2; $connection = mysql_connect($photo_connections[$connection_num]<b>['master']</b>, 'root', 'pwd'); $q = mysql_query('UPDATE photos SET views = views + 1 WHREE photo_id = ' . intval($photo_id), $connection); |
# Читаем данные со слейвов, а записываем на мастер-сервера
Такая схема часто используется не для масштабирования, а для обеспечения отказоустойчивости. Так, при выходе из строя одного из серверов шарда, всегда будет запасной.
Key-value базы данных
Следует отметить, что большинство Key-value баз данных поддерживает шардинг на уровне платформы. Например, Memcache. В таком случае, Вы просто указываете набор серверов для соединения, а платформа сделает все остальное:
1 2 3 4 5 6 7 |
<? $m = new Memcache; <b>$m->addServer('10.5.0.1'); $m->addServer('10.5.0.2'); $m->addServer('10.5.0.3');</b> ... $m->get('user1') |
# Мемкеш сам умеет определять нужный сервер для каждого ключа
Шардинг и репликация — это популярные и мощные техники масштабирования систем работы с данными. Несмотря на примеры для MySQL, эти подходы универсальны и могут применяться для любой технологии.