В этой статье мы поделимся некоторыми результатами своего исследования и пошаговым руководством по настройке расширенной архитектуры базы данных с использованием AWS Relational Database Service и Ludicrous DB, расширенного класса баз данных, созданного для повышения производительности.
Однако хотим подчеркнуть, что выбор облачных провайдеров (AWS) и плагина базы данных (LudicrousDB) несколько произвольный. Хотя у нас есть конкретные причины для того, чтобы сосредоточиться на этих двух элементах технологии, такой же архитектурный шаблон может быть достигнут с помощью любой другой комбинации поставщика облачных вычислений (Azure, GCP, Digital Ocean) и плагина базы данных (HyperDB, MultiDB).
Давайте начнем с того, что немного подробнее рассмотрим каждую концепцию, удаленные серверы, сегментирование и репликацию.
Удаленные серверы
Из всех концепций, о которых мы поговорим в этой статье, идея добавления удаленного сервера должна быть наиболее простой, как с точки зрения концептуальных основ, так и с точки зрения простоты реализации. В большинстве случаев установки WordPress для одного экземпляра, то есть, когда вся инфраструктура вашего сайта (MySQL, PHP, Apache/NGINX) работает на одном и том же сервере, мы создаем экземпляр MySQL на компьютере и подключаемся к нему с помощью localhost.
Есть несколько преимуществ для запуска вещей таким образом. Во-первых, он позволяет хранить все вещи на одной машине. Если вы включите SSH, вы можете получить любые инструменты, которые вам нужны для работы на сайте. Во-вторых, поскольку веб-сервер и база данных находятся на одном и том же компьютере, задержка сети при обращении к базе данных отсутствует.
Тем не менее, есть также много причин, почему эта установка одного экземпляра не является идеальной в определенных сценариях. По мере того, как ваш сайт масштабируется, база данных, вероятно, станет критической точкой до того, как ваши процессы PHP заблокируются. Перемещение вашей базы данных на ее собственную машину позволяет вам создать специальную среду, специфичную для SQL, которая может быть настроена и выделена специально для этой цели. Кроме того, когда мы начинаем исследовать отказоустойчивость между частями нашей системы, хранение вещей в одном экземпляре означает, что все тесно связано. Если одна часть вашего сервера по какой-либо причине блокируется, они все делают, потому что все процессы совместно используют одни и те же физические/виртуальные ресурсы.
Настройка удаленных серверов с нелепой БД
Настроить удаленный сервер баз данных с Ludicrous DB не сложнее, чем настроить его для одной установки, и нет ничего, что говорило бы о том, что вы не можете использовать удаленную базу данных, просто используя базовую конфигурацию базы данных в wp-config.php.
Следующий пример взят из моего рабочего файла db-config.php, который представляет собой раскрывающийся файл конфигурации, расположенный в корне установки WordPress. Вы можете найти основные инструкции по настройке DB в README.
$ wpdb - > add_database ( array ( 'host' = > 'wordpress.remote.database.3434223.aws.amazon.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'wp_remote_db' , 'dataset' = > 'global' ) ) ;
При взгляде на этот код, нет ничего особенного в этом методе. По правде говоря, мы можем сделать что-то вроде этого, используя конфигурацию базы данных по умолчанию:
// ** Настройки MySQL - Вы можете получить эту информацию с вашего веб-хостинга ** // / ** Имя базы данных для WordPress * / define ( 'DB_NAME' , 'wp_remote_db' / ** Имя пользователя базы данных MySQL * / define ( 'DB_USER' , 'root' ) ; / ** Пароль базы данных MySQL * / define ( 'DB_PASSWORD' , 'root' ) ; / ** MySQL hostname * / define ( 'DB_HOST' , 'wordpress.remote.database.3434223.aws.amazon.com' ) ;
За исключением небезопасных имен пользователей и паролей, все, что мы делаем в этом сценарии, это направляет все вызовы нашей базы данных на внешний IP или DNS-адрес.
Для тестирования мы использовали службу реляционной базы данных в AWS,чтобы начать работу ради простоты. RDS имеет тенденцию быть немного дорогостоящим, но некоторые из его удобных опций, которые мы рассмотрим позже, заслуживают внимания, если вы планируете добавить несколько удаленных серверов баз данных на свой сайт.
Для дополнительной безопасности мы также заблокировали свою базу данных с помощью встроенных в AWS настроек брандмауэра группы безопасности. Сервер базы данных будет принимать трафик только с эластичного IP-адреса моего сервера или с диапазона IP-адресов, назначенного моим интернет-провайдером дома. Таким образом, используя довольно простые в настройке параметры, вам нужно либо войти на сервер, либо в мою домашнюю сеть, чтобы получить доступ к содержимому удаленной базы данных.
Сегмент Базы Данных
Разделение базы данных – это сложная техническая концепция, когда вы начинаете изучать детали, но на первый взгляд это звучит довольно просто: взять большой набор данных, разбить его таким образом, который имеет смысл для схемы, и распределить нагрузку с помощью различных архитектурных приемов.
В некоторых случаях это может быть ненужной оптимизацией, и репликация лучше будет обслуживать многие варианты использования с интенсивным чтением. Мы собираемся поговорить о разбиении базы данных с использованием многосайтовой схемы данных WordPress, поскольку этот конкретный вариант использования, особенно в больших масштабах, кажется, хорошо подходит для распределения нагрузки на базу данных.
Мультисайтовая база данных по умолчанию
При настройке WP Multisite по умолчанию, использующей одну базу данных на одном сервере, мы могли бы иметь сотни тысяч таблиц, застрявших внутри одной базы данных на одном сервере базы данных, удаленном или локальном.
По умолчанию каждый новый сайт, добавленный в сеть, добавляет около 10 новых таблиц, специфичных для этого сайта. Как только мы достигнем размера сети около 1000, мы смотрим на таблицы 10К и более. Каждая таблица, вероятно, имеет свой собственный индекс, так что в итоге мы получаем кучу накладных расходов, которые мы помещаем в одну базу данных и на один сервер, чтобы не отставать.
Большая часть того, что происходит, когда сайт добавляется в сеть, отражает настройки базы данных WordPress по умолчанию, но вы также можете взглянуть на особенности базы данных Multisite.
Несколько баз данных, один сервер
Одна из техник, которые такие плагины, как Ludicrous DB и Hyper DB,позволяют нам начать реализацию, – это разбиение или разбиение таблиц Multisite на несколько баз данных. Для этого мы добавляем дополнительные базы данных в наш конфигурационный файл, присваиваем им уникальные имена в виде набора данных, а затем пишем функцию обратного вызова, которая сообщает классу базы данных, где искать каждую таблицу.
Например, следуя приведенной выше схеме многоузловых данных, мы можем получить три базы данных: одну для глобальных данных, одну для сайтов с четным номером сайта и одну для нечетных сайтов.
Поскольку мы знаем, что все новые таблицы будут построены с использованием следующего шаблона, мы можем воспользоваться этим, чтобы помочь нам определить, как разделить наши таблицы: wp_34_posts
Прелесть здесь в том, что количество баз данных и их организация могут быть произвольными в зависимости от того, сколько вы хотите иметь. Люди, работающие с Pressbooks, недавно написали статью о том, как они переместили полмиллиона таблиц в 101 базу данных . У нас есть личная многосайтовая настройка с использованием четных/нечетных настроек, и наша установка на Rampages с использованием хеш-функции md5 для разделения данных на 256 возможных баз данных.
Несколько баз данных, несколько серверов
После реализации описанной выше стратегии разделения следующим логическим шагом будет размещение подмножеств этих баз данных на их собственных компьютерах. Учитывая гибкую природу плагинов базы данных, мы также можем сделать это произвольно. Например, у вас есть 101 база данных, и первые 25 находятся на одном сервере базы данных, следующие 25 – на другом, и так далее.
Как только вы доберетесь до этого места, мир станет вашим.
Давайте посмотрим, как это работает на практике.
Реализация шардинга базы данных с нелепой БД
Первый шаг к реализации некоторого сегментирования включает в себя фактическое создание баз данных на любом типе сервера или серверов, которые вы используете. Этот процесс выглядит примерно так же, как и при добавлении одного набора данных с использованием Ludicrous:
$ wpdb - > add_database ( array ( 'host' = > 'localhost' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_global' , 'dataset' = > 'global' ) ) ; $ wpdb - > add_database ( array ( 'host' = > 'localhost', // Если порт отличается от 3306, используйте host: port. 'user' = > 'root', 'password' = > 'root', 'name' = > 'multisite_site_even', ' dataset' = > 'even_sites' ) ) ; $ wpdb - > add_database ( array ( 'host' = > 'localhost', // Если порт отличается от 3306, используйте host: port. 'user' = > 'root', 'password' = > 'root', 'name' = > 'multisite_site_odd', ' dataset' = > 'odd_sites', ) ) ;
В этом примере нам нужно создать уникальные имена для каждого набора данных, термин, который некоторые из этих плагинов базы данных используют, чтобы устранить некоторую путаницу в том, означает ли база данных сервер или базу данных внутри сервера. Для этого примера кода мы создали три набора данных с именами «global», «even_sites» и «odd_sites».
Эти параметры конфигурации устанавливают соединения, необходимые для связи с этими наборами данных. Следующий шаг включает добавление функции обратного вызова в класс $ wpdb, который помогает нам вычислить местоположение конкретной таблицы на лету при оценке каждого запроса.
$wpdb->add_callback('resolve_with_dataset'); function resolve_with_dataset ($query, $wpdb) { // Таблицы мультисайтового блога "{$base_prefix}{$blog_id}_*" if ( preg_match("/^{$wpdb->base_prefix}\d+_/i", $wpdb->table) ) { // просто разбейте wp_86_ prefix $prefix_matches = array(); preg_match("/^{$wpdb->base_prefix}\d+_/i", $wpdb->table, $prefix_matches); $site_table_prefix = $prefix_matches[0]; $site_id_matches = array(); // вытащите любые числовые совпадения здесь preg_match("/\d+/i", $site_table_prefix, $site_id_matches); $site_id = $site_id_matches[0]; if ( ((int)$site_id % 2) == 0) { return 'even_sites'; } else { return 'odd_sites'; } } else { return null; } }
Здесь много регулярных выражений, но эта функция на самом деле довольно проста. В первом операторе if мы просматриваем запрашиваемую таблицу и ищем шаблон типа «wp_23_», чтобы определить, нацелен ли этот запрос на таблицу, используемую сайтом в нашей сети или на глобальный набор данных.
Если эта функция возвращает null, класс $wpdb предполагает, что мы запрашиваем глобальный набор данных. Если мы обнаруживаем шаблон сайта в сети, мы запускаем дополнительное регулярное выражение, чтобы извлечь идентификатор сайта из строки таблицы. Оттуда мы используем извлеченный идентификатор сайта и оператор по модулю, чтобы разделить идентификатор сайта на 2 и оценить остаток.
Если он четный, наш обратный вызов разрешается с помощью набора данных even_sites, в противном случае он указывает на набор данных odd_sites. Отсюда мы можем экстраполировать основы этого шаблона обратного вызова, чтобы увидеть, как мы могли бы использовать сто различных наборов данных в качестве схемы разбиения.
Также очень легко объединить это разбиение с идеей удаленных серверов из предыдущего примера. Все, что нам нужно сделать, это предоставить IP или DNS-адрес удаленному серверу, на котором живет конкретный набор данных, и плагин базы данных будет достаточно умен, чтобы направить запрос в соответствующее место:
$ wpdb - > add_database ( array ( 'host' = > 'remote.wordpress.database.8787878.aws.amazon.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_site_even' , ' dataset' = > 'even_sites' ) ) ;
Эти два метода, используемые вместе, могут быть очень мощными, если использование вашего сайта предъявляет особый уровень требований к вашей базе данных. В следующем примере мы рассмотрим, как сделать эту настройку еще более пуленепробиваемой с помощью репликации.
Репликация базы данных
Репликация базы данных – это сложный процесс, который связывает две или более баз данных в отношениях, часто называемых главной/подчиненной или основной/репликой. Это то, что настраивается на уровне базы данных, но, как правило, оно работает так, что в качестве данных, записываемых в первичную базу данных, он асинхронно реплицирует эти изменения в реплику. Обычно эта репликация выполняется с задержкой менее миллисекунды.
Преимущества использования репликации базы данных решают несколько различных проблем при создании приложений для масштабирования. Во-первых, создание баз данных реплики позволяет распределять трафик базы данных по ряду различных серверов баз данных, которые программно синхронизируются друг с другом. Во-вторых, это позволяет уменьшить площадь поверхности для отказа вашего приложения, добавив избыточность в ваш дизайн. Если у нас есть полностью обновленная реплика чтения, которую мы можем продвинуть к нашему первичному экземпляру, всегда будет другая база данных, ожидающая на случай, если ваша первичная база данных начнет выходить из строя.
Используя некоторые встроенные инструменты AWS, описанные ниже, мы также можем сделать нашу архитектуру отказоустойчивой в географическом отношении и развернуть реплики в центрах обработки данных по всей стране.
Если вы посмотрите на шаги, описанные в этом руководстве по созданию реплик чтения MySQL , мы снова сможем оценить простоту службы реляционной базы данных в AWS, поскольку мы можем выполнить все эти шаги одним нажатием кнопки, используя их функции чтения реплик.
Маршрутизация чтения/записи трафика с нелепой БД
Как и в большинстве примеров, которые мы рассмотрели при использовании расширенных классов базы данных, это довольно стандартный вариант использования, который легко настраивается. Все, что нам нужно сделать, это добавить еще одну запись набора данных для нашей реплики чтения, а затем мы добавим свойства записи и чтения в определения базы данных.
// Первичная база данных записи $ wpdb - > add_database ( array ( 'host' = > 'wordpress-remote-db-primary.h3h3k38sa.us-east-1.rds.amazonaws.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_site_odd' , ' dataset' = > 'odd_sites' , 'write' = > 1 , 'read' = > 0 ) ) ; // Читаем реплику $ wpdb - > add_database ( array ( 'host' = > 'wordpress-remote-db-replica.h3h3k38sa.us-east-1.rds.amazonaws.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_site_odd' , ' dataset' = > 'odd_sites' , 'write' = > 0 , 'read' = > 1 ) ) ;
В приведенном выше примере мы определяем приоритет, присваивая каждому свойству целые числа, которые используются в качестве логических ключей. Однако мы также можем немного усложнить эту конкретную настройку и воспользоваться преимуществами использования нескольких реплик чтения в разных зонах доступности.
Например, если мы прочитали реплики как в us-east-1 (Северная Вирджиния), так и в us-west-2 (Орегон), мы можем указать топологию для наших центров обработки данных, чтобы наше приложение сначала считывало более производительную базу данных.
// Читаем реплику us-east-1 в Вирджинии $ wpdb - > add_database ( array ( 'host' = > 'wordpress-remote-db-replica.h3h3k38sa.us-east-1.rds.amazonaws.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_site_odd' , ' dataset' = > 'odd_sites' , 'write' = > 0 , 'read' = > 1 ) ) ; // Читаем реплику us-west-2 в штате Орегон $ wpdb - > add_database ( array ( 'host' = > 'wordpress-remote-db-replica.h3h3k38sa.us-west-2.rds.amazonaws.com' , // Если порт отличается от 3306, используйте host: port. 'user' = > 'root' , 'password' = > 'root' , 'name' = > 'multisite_site_odd' , ' dataset' = > 'odd_sites' , 'write' = > 0 , 'read' = > 2 ) ) ;
Поскольку наш веб-сервер также расположен в us-east-1, мы можем воспользоваться преимуществами высокопроизводительной сети, доступной в том же центре обработки данных. Но если по какой-либо причине эта реплика недоступна, мы можем вернуться к реплике, доступной по всей стране. Эту же конфигурацию можно продолжить для любого количества уровней в зависимости от требований приложения.
Завершение
Что касается веб-приложений, WordPress в некотором смысле имеет самый низкий барьер для начала, но существует множество узких мест производительности, которые могут возникнуть, если что-то не настроено должным образом. Для большинства людей изложенные здесь стратегии могут оказаться ненужными после реализации более простых вещей, таких как NGINX и статическое кэширование страниц, но, тем не менее, более популярные сайты столкнутся с некоторыми проблемами производительности базы данных в определенный момент времени.
Как мы уже говорили в начале, эти стратегии могут быть изменены в зависимости от вашего предпочтительного поставщика облачных вычислений и уровня, на котором вы чувствуете необходимость реализации этих шаблонов.