BMC IO DocsBMC IO Docs
IO v6
IO v7
Notes
Docs
IO v6
IO v7
Notes
Docs
  • IO Framework v7

    • О фреймворке
    • Что изменилось в 7 версии
    • IO7 Framework vs Laravel 13 — Сравнение
    • Структура
    • Варианты проектов
    • Доступ к участкам проекта
    • Разворачивание нового проекта
    • Установка проекта
    • Запуск локальной разработки
  • API

    • Обзор API
    • IOCore::call() и IOCore::callAction()
    • Создание API
  • CLI

    • Обзор CLI
    • iocli
    • Кастомные CLI команды
  • Работа с базами данных

    • Обзор работы с базами данных
    • Cron / Планировщик задач (Schedule)
    • Фабрики
    • Миграции
    • Сидеры
    • Query Builder
    • Подключение MinIO
    • Подключение MongoDB
    • Подключение MySQL
    • Подключение PostgreSQL
    • Подключение ProxySQL
    • Подключение RabbitMQ
    • Подключение Redis
  • Логирование

    • Обзор логирования
    • IOEvent - Логирование событий
    • Pinba (PinbaManager) - Профилировщик
  • Модели

    • Обзор моделей
    • CRUD операции
    • Связи моделей
  • Очереди

    • Обзор очередей
    • RabbitMQ
  • Маршруты

    • Обзор маршрутов
    • Определение маршрутов
    • Middleware
  • Сессии

    • Обзор сессий
    • Кроссдоменная SSO
    • JWT авторизация
    • Социальная авторизация
    • OAuth2
    • Keycloak
  • Twig

    • Обзор Twig
    • Формы (FormBuilder)
    • Twig функции
  • Тестирование

    • Обзор тестирования
    • Unit тесты
  • Backend

    • Настройки проекта Conf.php
    • Настройки модуля Module.php
    • Настройки composer
    • Функции фреймворка
    • Вспомогательные функции (Helpers)
    • Глобальные переменные
    • Константы фреймворка
    • Event класс
    • Core класс
    • Route и работа с ними
    • Request и работа с ними
    • Middleware и работа с ними
    • Class и работа с ними
    • Api model и работа с ними
    • Template и работа с ними
    • Мультиязычность и словарь фраз
    • Bin скрипты и работа с ними
    • Cron скрипты и работа с ними
    • $ioSession и работа с сессией
    • $ioHash и работа с кешем
    • $ioXCache и работа с кешем
    • $iodb и работа с базой данных
    • Миграции
  • Frontend

    • Twig шаблонизатор и работа с ним
    • Twig функции
    • Twig фильтры
    • Twig контекст
    • Настройка NPM
    • Сборка Frontend составляющей
    • Обращение к Api через JS
    • Хранилище (store)
    • Навигация в хосте
    • Навигация в микрофронтенде
    • Параметры микрофронтенда
    • Проверка полномочий
  • Tailux UI Компоненты

    • Базовый UI

      • AvatarDot
      • Avatar
      • Badge
      • Button
      • Card
      • ConfirmModal
      • Divider
      • Highlight
      • Tag
    • Загрузка

      • Circlebar
      • Progress
      • Skeleton
      • Spinner
    • Навигация

      • Accordion
      • AccordionButton
      • AccordionHeader
      • AccordionItem
      • AccordionPanel
      • AccordionProvider
      • Pagination
      • PaginationControl
      • PaginationItems
      • PaginationDots
      • PaginationFirst
      • PaginationLast
      • PaginationNext
      • PaginationPrevious
    • Отображение данных

      • Box
      • Collapse
      • ContextualHelp
      • ScrollShadow
      • Timeline
      • TimelineItem
    • Элементы таблиц

      • Table
      • CollapsibleSearch
      • ColumnFilter
      • CopyableCell
      • DateFilter
      • FacetedFilter
      • FilterSelector
      • PaginationSection
      • RadioFilter
      • RangeFilter
      • ResponsiveFilter
      • RoleFilter
      • SelectedRowsActions
      • TableColumnVisibility
      • TableSettings
      • TableSortIcon
    • Элементы форм

      • Checkbox
      • Combobox
      • DatePicker
      • Filepond
      • Input
      • Listbox
      • Radio
      • Range
      • Select
      • StyledSwitch
      • Swap
      • SwapOn
      • SwapOff
      • Switch
      • Textarea
      • TextEditor
      • Upload

Подключение ProxySQL

Внимание

Данная статья документации написана нейросетью. Возможны некоторые расхождения и неточность информации!

  • Обзор
    • Архитектура
  • ProxySQLManager
    • Конструктор
    • Статические методы
    • Методы инициализации
    • Управление серверами
    • Управление правилами
    • Настройка Read/Write
    • Статистика
    • Сохранение конфигурации
  • Переменные окружения
  • Настройка Read/Write разделения
    • Полный пример
  • Примеры
    • Добавление серверов
    • Кастомные правила маршрутизации
    • Обслуживание сервера
    • Мониторинг
  • Рекомендации
  • FAQ
    • Как узнать текущую конфигурацию ProxySQL?
    • Как настроить балансировку между репликами?
    • Что делать при ошибке подключения?
    • Как сбросить конфигурацию ProxySQL?
    • Поддерживается ли PostgreSQL через ProxySQL?

Руководство по подключению и управлению ProxySQL в IO7 фреймворке.

Обзор

ProxySQL — прокси-сервер для MySQL/PostgreSQL, обеспечивающий:

  • Read/Write split — автоматическая маршрутизация SELECT на реплики,写操作 на мастер
  • Connection pooling — переиспользование соединений
  • Query routing — маршрутизация по регулярным выражениям
  • Health checking — автоматическое отключение нерабочих серверов
  • Statistics — сбор метрик по соединениям и запросам

Архитектура

┌─────────────┐     ┌──────────┐     ┌───────────────┐
│  Приложение │───▶│ ProxySQL  │───▶│ MySQL Master  │
│   (IO7)     │     │  (6033)  │     │ (hostgroup 0) │
└─────────────┘     │          │     └───────────────┘
                    │          │     ┌───────────────┐
                    │  (6032)  │───▶│ MySQL Replica  │
                    │   admin  │     │ (hostgroup 1) │
                    └──────────┘     └───────────────┘

ProxySQLManager

Пространство имён: IO\Database

Конструктор

public function __construct(?array $adminConfig = null)
ПараметрТипЗначение по умолчаниюОбязателенОписание
$adminConfig?\arraynullНетКонфигурация админского подключения (null для отложенной инициализации)

Статические методы

getInstance()

public static function getInstance(): self

Получить синглтон-экземпляр менеджера.

fromConfig()

public static function fromConfig(
    string $adminHost,
    int $adminPort = 6032,
    string $adminUser = 'admin',
    string $adminPass = 'admin'
): self

Создать менеджер из параметров подключения.

ПараметрТипЗначение по умолчаниюОбязателенОписание
$adminHoststring—ДаАдрес ProxySQL admin
$adminPortint6032НетПорт ProxySQL admin
$adminUserstring'admin'НетЛогин admin
$adminPassstring'admin'НетПароль admin
$manager = ProxySQLManager::fromConfig('127.0.0.1', 6032, 'admin', 'admin');

Методы инициализации

init()

public function init(
    string $host,
    int $port = 6032,
    string $user = 'admin',
    string $password = 'admin'
): self

Инициализация с админским подключением.

$manager = ProxySQLManager::getInstance();
$manager->init('127.0.0.1', 6032, 'admin', 'admin');

Управление серверами

addServer()

public function addServer(
    string $host,
    int $port,
    int $weight = 1,
    int $hostgroup = 0,
    int $maxConnections = 100
): bool

Добавить сервер в hostgroup.

ПараметрТипЗначение по умолчаниюОбязателенОписание
$hoststring—ДаАдрес сервера
$portint—ДаПорт сервера
$weightint1НетВес сервера (для балансировки)
$hostgroupint0НетID hostgroup
$maxConnectionsint100НетМакс. количество соединений
// Мастер в hostgroup 0
$manager->addServer('192.168.1.10', 3306, 1, 0, 100);

// Реплики в hostgroup 1
$manager->addServer('192.168.1.11', 3306, 1, 1, 100);
$manager->addServer('192.168.1.12', 3306, 1, 1, 100);

removeServer()

public function removeServer(string $host, int $port): bool

Удалить сервер.

$manager->removeServer('192.168.1.12', 3306);

getServers()

public function getServers(?int $hostgroup = null): array

Получить серверы. Если $hostgroup null — все серверы.

$allServers = $manager->getServers();
$replicas = $manager->getServers(1);

setOffline() / setOnline()

public function setOffline(string $host, int $port): bool
public function setOnline(string $host, int $port): bool

Переключить статус сервера.

// Взять сервер на обслуживание
$manager->setOffline('192.168.1.12', 3306);

// Вернуть в работу
$manager->setOnline('192.168.1.12', 3306);

Управление правилами

addQueryRule()

public function addQueryRule(
    string $pattern,
    int $destinationHostgroup,
    int $apply = 1,
    ?int $ruleId = null
): bool

Добавить правило маршрутизации.

ПараметрТипЗначение по умолчаниюОбязателенОписание
$patternstring—ДаРегулярное выражение для матча запроса
$destinationHostgroupint—ДаID целевого hostgroup
$applyint1НетПрименять ли правило (1 — да, 0 — нет)
$ruleId?\intnullНетID правила (null — генерируется из time())
// SELECT на реплики (hostgroup 1)
$manager->addQueryRule('^SELECT', 1);

// WRITE на мастер (hostgroup 0)
$manager->addQueryRule('^(UPDATE|INSERT|DELETE)', 0);

removeQueryRule()

public function removeQueryRule(int $ruleId): bool

Удалить правило.

$manager->removeQueryRule(100);

getQueryRules()

public function getQueryRules(): array

Получить все правила.

Настройка Read/Write

setupReadWriteSplit()

public function setupReadWriteSplit(
    string $masterHost,
    int $masterPort,
    array $replicaHosts,
    int $replicaPort = 3306
): self

Автоматическая настройка разделения чтения/записи.

ПараметрТипЗначение по умолчаниюОбязателенОписание
$masterHoststring—ДаАдрес мастера
$masterPortint—ДаПорт мастера
$replicaHostsarray<int, string>—ДаАдреса реплик
$replicaPortint3306НетПорт реплик
$manager->setupReadWriteSplit(
    '192.168.1.10',        // мастер
    3306,
    [                       // реплики
        '192.168.1.11',
        '192.168.1.12',
    ],
    3306
);

Создаёт правила:

  • ^SELECT → hostgroup 1 (реплики)
  • ^(UPDATE|INSERT|DELETE|ALTER|CREATE|DROP) → hostgroup 0 (мастер)
  • ^START TRANSACTION, ^COMMIT, ^ROLLBACK → hostgroup 0

Статистика

getStats()

public function getStats(): array

Получить статистику ProxySQL.

Возвращает:

[
    'connection_pool' => [...],  // Пул соединений
    'query_digest'    => [...],  // Дайджест запросов (топ 20 по времени)
    'global'          => [...],  // Глобальная статистика
]
$stats = $manager->getStats();

// Пул соединений
foreach ($stats['connection_pool'] as $pool) {
    echo "Hostgroup {$pool['hostgroup']}: {$pool['ConnUsed']} active\n";
}

// Топ запросов
foreach ($stats['query_digest'] as $query) {
    echo "Query: {$query['digest_text']}\n";
    echo "  Count: {$query['count_star']}\n";
    echo "  Time: {$query['sum_time']} мкс\n";
}

Сохранение конфигурации

saveConfig()

public function saveConfig(): void

Применить и сохранить конфигурацию на диск.

Важно: Вызывайте после всех изменений серверов и правил.

$manager->addServer('192.168.1.10', 3306, 1, 0);
$manager->addQueryRule('^SELECT', 1);
$manager->saveConfig();

Переменные окружения

# ProxySQL (перекрывает движок — подключение идёт через прокси)
PROXYSQL_HOST=proxysql
PROXYSQL_PORT=6033

# ProxySQL Admin (для управления через ProxySQLManager)
PROXYSQL_ADMIN_HOST=127.0.0.1
PROXYSQL_ADMIN_PORT=6032
PROXYSQL_ADMIN_USER=admin
PROXYSQL_ADMIN_PASS=admin

# Движок БД за ProxySQL
DB_ENGINE=mysql

Настройка Read/Write разделения

Полный пример

use IO\Database\ProxySQLManager;
use IO\Database\ConnectionManager;
use IO\Database\DB;

// 1. Инициализация ProxySQLManager
$manager = ProxySQLManager::fromConfig('127.0.0.1', 6032, 'admin', 'admin');

// 2. Настройка серверов
$manager->setupReadWriteSplit(
    '192.168.1.10',   // мастер
    3306,
    ['192.168.1.11', '192.168.1.12'],  // реплики
    3306
);

// 3. Сохранение конфигурации
$manager->saveConfig();

// 4. Подключение приложения через ProxySQL
DB::init(ConnectionManager::mysqlConfig(
    '127.0.0.1',  // ProxySQL адрес
    'mydb',
    'app_user',
    'secret',
    6033           // ProxySQL порт для приложений
));

// 5. Все запросы автоматически маршрутизируются
$users = DB::table('users')->where('active', 1)->get();  // → реплика
DB::table('users')->insert([...]);                        // → мастер

Примеры

Добавление серверов

$manager = ProxySQLManager::fromConfig('127.0.0.1');

// Мастер
$manager->addServer('db-master.local', 3306, 1, 0, 100);

// Реплики
$manager->addServer('db-replica1.local', 3306, 1, 1, 100);
$manager->addServer('db-replica2.local', 3306, 2, 1, 100);  // больший вес

$manager->saveConfig();

Кастомные правила маршрутизации

// Тяжёлые SELECT на выделенную реплику
$manager->addQueryRule('^SELECT.*JOIN.*orders', 2, 1, 100);

// Запросы к определённой таблице на мастер
$manager->addQueryRule('^SELECT.*FROM critical_table', 0, 1, 101);

// Транзакции всегда на мастер
$manager->addQueryRule('^START TRANSACTION', 0, 1, 200);
$manager->addQueryRule('^BEGIN', 0, 1, 201);

Обслуживание сервера

// Взять реплику на обслуживание
$manager->setOffline('db-replica2.local', 3306);

// Проверить статус
$servers = $manager->getServers(1);
foreach ($servers as $server) {
    echo "{$server['hostname']}: {$server['status']}\n";
}

// Вернуть в работу
$manager->setOnline('db-replica2.local', 3306);
$manager->saveConfig();

Мониторинг

$stats = $manager->getStats();

// Проверка пула соединений
foreach ($stats['connection_pool'] as $pool) {
    if ($pool['ConnUsed'] > $pool['max_connections'] * 0.8) {
        // Предупреждение: высокая нагрузка
        error_log("Высокая нагрузка на hostgroup {$pool['hostgroup']}");
    }
}

// Топ медленных запросов
foreach (array_slice($stats['query_digest'], 0, 5) as $query) {
    echo "Медленный запрос: {$query['digest_text']}\n";
    echo "  Среднее время: " . ($query['sum_time'] / $query['count_star']) . " мкс\n";
}

Рекомендации

  1. Всегда вызывайте saveConfig() после изменений серверов или правил
  2. Используйте setupReadWriteSplit() для типичных конфигураций master-slave
  3. Мониторьте статистику через getStats() для выявления проблем
  4. Используйте setOffline() перед обслуживанием серверов
  5. Разделяйте admin порт (6032) и proxy порт (6033)
  6. Тестируйте правила на dev-окружении перед продакшеном

FAQ

Как узнать текущую конфигурацию ProxySQL?

$servers = $manager->getServers();
$rules = $manager->getQueryRules();

echo "Серверы:\n";
foreach ($servers as $s) {
    echo "  {$s['hostname']}:{$s['port']} (hg={$s['hostgroup_id']}, status={$s['status']})\n";
}

echo "Правила:\n";
foreach ($rules as $r) {
    echo "  #{$r['rule_id']}: {$r['match_pattern']} → hg={$r['destination_hostgroup']}\n";
}

Как настроить балансировку между репликами?

// Реплика 1 с весом 1, реплика 2 с весом 2
$manager->addServer('replica1.local', 3306, 1, 1, 100);
$manager->addServer('replica2.local', 3306, 2, 1, 100);

Что делать при ошибке подключения?

try {
    $manager = ProxySQLManager::fromConfig('127.0.0.1');
} catch (\Exception $e) {
    error_log("ProxySQL недоступен: " . $e->getMessage());
    // Fallback на прямое подключение
    DB::init(ConnectionManager::mysqlConfig('db-master.local', 'mydb', 'root', 'secret'));
}

Как сбросить конфигурацию ProxySQL?

$manager->adminConnection->statement("DELETE FROM mysql_servers");
$manager->adminConnection->statement("DELETE FROM mysql_query_rules");
$manager->saveConfig();

Поддерживается ли PostgreSQL через ProxySQL?

Да, начиная с версии 2.0 ProxySQL поддерживает PostgreSQL. Используйте DB_ENGINE=pgsql и соответствующий порт.

Prev
Подключение PostgreSQL
Next
Подключение RabbitMQ