Тюнінг продуктивності MySQL (innodb_buffer_pool, query_cache)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Тюнінг продуктивності MySQL (innodb_buffer_pool, query_cache)
Складна
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Тюнінг продуктивності MySQL (innodb_buffer_pool, query_cache)

MySQL зі стандартною конфігурацією — innodb_buffer_pool_size = 128M на сервері з 64 ГБ ОЗУ. Додається зламаний query_cache (який у MySQL 8.0 повністю видалено), max_connections = 151 при сотнях активних користувачів. Тюнінг — це послідовна робота з пам'яттю, дисковим вводом-виводом та планувальником запитів.

InnoDB Buffer Pool: головний параметр

Buffer pool — це кеш сторінок даних та індексів InnoDB у пам'яті. Аналог shared_buffers у PostgreSQL. Чим більше робочий набір даних вміщується в пам'ять, тим рідше MySQL звертається до диска.

# /etc/mysql/conf.d/performance.cnf
[mysqld]

# 70-80% ОЗУ для виділеного сервера
# Для сервера з 32 ГБ ОЗУ:
innodb_buffer_pool_size = 24G

# Кілька інстансів buffer pool — знижують конфлікти при паралельному доступі
# 1 інстанс на кожен 1 ГБ, мінімум 1, максимум 64
innodb_buffer_pool_instances = 24

# Прогрів buffer pool при перезавантаженні (MySQL 5.7+)
innodb_buffer_pool_dump_at_shutdown = ON
innodb_buffer_pool_load_at_startup  = ON

Перевірка ефективності buffer pool:

SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';

-- Ключові метрики:
-- Innodb_buffer_pool_reads       — читання з диска (хочемо мінімуму)
-- Innodb_buffer_pool_read_requests — всього запитів до buffer pool
-- Hit rate = (1 - reads/read_requests) * 100
-- Мета: > 99%

SELECT
    (1 - (variable_value / (
        SELECT variable_value
        FROM information_schema.global_status
        WHERE variable_name = 'Innodb_buffer_pool_read_requests'
    ))) * 100 AS hit_rate_pct
FROM information_schema.global_status
WHERE variable_name = 'Innodb_buffer_pool_reads';

InnoDB Redo Log

# Великі redo log файли = рідше контрольні точки = вища пропускна здатність запису
# MySQL 5.7 та нижче: зміни потребують зупинки, видалення старих файлів, перезавантаження
innodb_log_file_size = 1G    # 1-4 ГБ для високонавантажених серверів
innodb_log_files_in_group = 2
innodb_log_buffer_size = 64M

# MySQL 8.0.30+: динамічний redo log, innodb_redo_log_capacity
innodb_redo_log_capacity = 4G

Query Cache (MySQL 5.7 та нижче)

# Query cache — ВИМКНУТИ. Це мьютекс на весь кеш при будь-якому записі у таблицю.
# При високому паралелізмі Query Cache стає вузьким місцем.
query_cache_type = 0
query_cache_size = 0

У MySQL 8.0 query_cache видалено. Правильний кеш для веб-додатків — на рівні додатку (Redis, Memcached), а не на рівні MySQL.

Sort та Join буфери

# Буфер для дискової сортування (ORDER BY без індексу)
sort_buffer_size = 4M

# Буфер для JOIN без індексів (уникайте таких запитів!)
join_buffer_size = 4M

# Тимчасові таблиці в пам'яті
tmp_table_size    = 256M
max_heap_table_size = 256M

# Буфер для читання під час повного сканування таблиці
read_buffer_size       = 2M
read_rnd_buffer_size   = 4M

sort_buffer_size виділяється на кожне з'єднання при необхідності сортування — не встановлювати занадто великим при великому max_connections.

З'єднання та потоки

# Максимум з'єднань
max_connections = 500

# Кеш потоків — уникаємо створення нового потоку з кожним з'єднанням
thread_cache_size = 50

# Розмір стека потоку (звичайно не змінювати)
thread_stack = 256K

# Таймаути для неактивних з'єднань
wait_timeout         = 300   # 5 хвилин
interactive_timeout  = 300

# Відкриті файли (таблиці, файли даних)
open_files_limit      = 65535
table_open_cache      = 4000
table_definition_cache = 2000

Тюнінг вводу-виводу для SSD

# Метод flush — O_DIRECT обходить OS page cache (немає подвійного кеширування)
innodb_flush_method = O_DIRECT

# Паралельний вхід-вихід
innodb_read_io_threads  = 8
innodb_write_io_threads = 8
innodb_io_capacity      = 2000   # IOPS диска (SSD ~10000, NVMe ~100000)
innodb_io_capacity_max  = 4000

# Адаптивне очищення — MySQL вирішує, коли агресивніше очищати dirty pages
innodb_adaptive_flushing = ON

# Рідне AIO (Linux)
innodb_use_native_aio = ON

Повільні запити: включення логування

# Повільні запити
slow_query_log           = ON
slow_query_log_file      = /var/log/mysql/slow.log
long_query_time          = 1      # секунди (1 = запити > 1 сек)
log_queries_not_using_indexes = ON  # навіть швидкі без індексів
min_examined_row_limit   = 100    # ігнорувати запити, що переглянули < 100 рядків

Аналіз через pt-query-digest:

pt-query-digest /var/log/mysql/slow.log \
  --limit 20 \
  --output report > /tmp/slow_report.txt

Планувальник: EXPLAIN FORMAT=JSON

EXPLAIN FORMAT=JSON
SELECT o.*, u.name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'pending'
  AND o.created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY o.created_at DESC\G

-- Шукайте: "access_type": "ALL" — повне сканування таблиці, потрібен індекс
-- "rows": 1000000 — велика кількість переглянутих рядків
-- "using_filesort": true — сортування без індексу

Оптимальний складений індекс для вищезгаданого запиту:

ALTER TABLE orders
  ADD INDEX idx_status_date (status, created_at DESC);
-- Охоплює фільтр за status ТА сортування за created_at

Моніторинг через Performance Schema

-- Топ запитів за сумарним часом виконання
SELECT digest_text,
       count_star,
       ROUND(avg_timer_wait / 1e12, 3) AS avg_sec,
       ROUND(sum_timer_wait / 1e12, 3) AS total_sec
FROM performance_schema.events_statements_summary_by_digest
WHERE schema_name = 'mydb'
ORDER BY sum_timer_wait DESC
LIMIT 10;

-- Таблиці з найбільшою кількістю повних сканувань
SELECT object_schema, object_name,
       count_read,
       count_full_scan
FROM performance_schema.table_io_waits_summary_by_table
WHERE object_schema = 'mydb'
ORDER BY count_full_scan DESC
LIMIT 10;

Типовий результат тюнінгу

На сервері з 32 ГБ ОЗУ з проектом Laravel (MySQL 5.7), до тюнінгу:

  • innodb_buffer_pool_size = 128M — hit rate 87%
  • query_cache = ON — 40% часу CPU на мьютекс
  • повільні запити > 1s: 200–400 за хвилину

Після тюнінгу (buffer pool до 24G, query_cache OFF, індекси за slow log):

  • hit rate 99.4%
  • повільні запити > 1s: 3–7 за хвилину
  • p95 latency API впав з 450ms до 85ms