ServerAdmin.ru
31.6K subscribers
851 photos
57 videos
23 files
3K links
Авторская информация о системном администрировании.

Информация о рекламе: @srv_admin_reklama_bot
Автор: @zeroxzed

Второй канал: @srv_admin_live
Сайт: serveradmin.ru

Ресурс включён в перечень Роскомнадзора
Download Telegram
На днях разбирался с одной относительно простой задачей, но из-за того, что затупил, потратил на неё очень много времени. Так как проделал множество всевозможных операций, решил всё подробно описать. Пригодится в других похожих историях.

Есть MySQL сервер, у которого, как мне показалось, очень большая нагрузка по CPU. Проект немного стух, записи в базу должно быть мало и мне было странно видеть высокую нагрузку от СУБД. Решил разобраться, в чём там дело.

Для начала просто посмотрел нагрузку через top. Mysql почти постоянно кушает одно ядро и периодически остальные дёргает. Дополнительно смотрю в iotop и pidstat и вижу постоянно запись со стороны службы mysqld с pid 19535:

# iotop -obPat
# pidstat -p 19535 -d 1

В таких случаях часто помогает strace. Можно подцепиться к процессу и посмотреть, что он пишет:

# strace -e trace=write -p 19535

В моём случае я не получил результата. В выводе просто было пусто. Я не понял, почему. Стал разбираться дальше. Смотрю потоки процесса:

# ps -L -o spid,%mem,%cpu,cmd 16005

Вижу, что CPU нагружает сильно больше всех остальных один поток с SPID 19547. Смотрю по нему информацию:

# cat /proc/19535/task/19547/stat
# cat /proc/19535/task/19547/status

В выводе только цифры и отсылка к основному потоку mysqld. То есть вообще не понятно, что там реально происходит.

Тут до меня доходит подцепиться к потоку через strace:

# strace -p 19547

Вижу системные вызовы futex (синхронизацией потоков), io_submit (асинхронные операции ввода-вывода) и некоторые другие.

Смотрю, в какие файлы пишет mysqld:

# inotifywait -m /var/lib/mysql

Это ib_logfile0 и xb_doublewrite. Никак не могу понять, почему он туда активно пишет, когда запросов к базе особо нет. И вот тут я как раз и затупил. Я смотрел запросы через mytop и SHOW FULL PROCESSLIST; И там их было очень мало. Эти команды показывают запросы в моменте.

А на серваке выполнялись десятки простых SELECT за считанные миллисекунды или ещё быстрее. Не отслеживал. И они тупо не попадали в вывод. И я думал, что запросов мало, а их было дохрена. Они и давали нагрузку на CPU.

Запросы увидел так. Не перезапуская сервер выполнил в консоли mysql:

> SET GLOBAL general_log = 'ON';
> SET GLOBAL general_log_file = '/var/log/mysql/general.log';

Так же это было видно в выводе:

> SHOW ENGINE INNODB STATUS;

В разделе I/O. Эти потоки отвечают за асинхронные операции ввода-вывода (AIO).

Достаточно было на несколько секунд запустить, чтобы понять всю картину. Потом сразу отключил, чтобы не нагружать диски.

Осталось разобраться, а что на диск то записывается. Вроде как файлы ib_logfile0 это файлы журналов транзакций InnoDB, а разве SELECT вызывает транзакции? Я тут сильно не погружался, но мельком глянул информацию и понял, что всякие очистки устаревших данных, обновление статистики, индексов, хэшей могут тоже провоцировать запись, а точнее обновление этого файла.

xb_doublewrite - это некий буфер InnoDB для повышения надёжности хранения, отключать не рекомендуется, хотя можно.

В общем, время я по сути потратил впустую, если не считать вот эту заметку итоговым результатом. Думаю, она мне ещё понадобится. Времени уже не оставалось дальше разбираться с этой историей. В целом, нагрузка там рабочая, абсолютно некритичная, так что разбираться с ней дальше большой нужды не было. Но я всё равно планирую подумать, как её снизить. И вообще не совсем понятно, почему её так много, с учётом того, что сайт активно кэшируется. Надо будет разбираться.

Отдельно отмечу, что очень активно нагружал этой темой DeepSeek, но он особо не помог. Да, много всякой информации давал и анализировал мои результаты, но фактически я сам догадался в чём причина. Он как-то больше по кругу ходил и всякие команды накидывал.

❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.

#mysql #perfomance
7👍237👎4
У меня уже были заметки про фишки LVM, сегодня расскажу про ещё одну, которая может пригодиться. С помощью LVM и снепшотов можно делать консистентные бэкапы баз данных Mysql на уровне файлов, а не дампов, практически без простоя. Для таких задач в СУБД Percona есть инструмент под названием XtraBackup, а вот если у вас обычная бесплатная MySQL или MariaDB, то с бэкапами на уровне файлов там не так всё просто, особенно если вы уже не можете делать дампы из-за их больших размеров.

Идея там вот в чём. Мы закрываем все открытые таблицы, сбрасываем кэш, блокируем изменение данных, после этого делаем снепшот раздела с базами данных и сразу отключаем блокировки. Это занимает буквально пару секунд, на которые блокируются все базы, а потом спокойно копируем файлы сервера, примонтировав снепшот.

Это всё хорошо работало до появления движка InnoDB. У него есть нюансы, когда блокировки не предотвращают изменение данных в таблицах. Нужно дополнительно сбрасывать на диск так называемые dirty_pages. Покажу всё это на практике. Способ немного костыльный и вряд ли подойдёт для прода, так как есть много нюансов. Но в каких-то ситуациях может помочь быстро снять консистентную копию без остановки сервера.

Для того, чтобы всё получилось, раздел, где хранятся данные MySQL сервера, должен располагаться на LVM, а в Volume Group должно быть свободное место для снепшотов. Проверяем так:

# pvs

Если места нет, надо добавить. Не буду на этом останавливаться, рассказывал ранее. Первым делом заходим в консоль mysql, сбрасываем кэш и включаем блокировки:

> SET GLOBAL innodb_max_dirty_pages_pct = 0;
> FLUSH TABLES WITH READ LOCK;

Если база не сильно большая и нагруженная, кэш быстро сбросится. Наблюдать можно так:

> SHOW ENGINE INNODB STATUS\G;

Следим за значением Modified db pages. Оно должно стать 0. После этого можно делать снэпшот:

# lvcreate --size 5G --snapshot --name mysql_snapshot /dev/vgroup/root

После этого возвращаем обратно настройку с dirty_pages в исходное значение (по умолчанию 90) и снимаем блокировки:

> SET GLOBAL innodb_max_dirty_pages_pct = 90;
> UNLOCK TABLES;

Когда это делается автоматически скриптом, всё это происходит быстро. Но есть нюансы. Команда блокировки дожидается завершения всех запросов. Если у вас там есть какие-то очень длинные запросы, то вся база будет заблокирована, пока они не завершатся. Так что имейте это ввиду.

Монтируем снепшот и копируем данные:

# mkdir /mnt/mysql_backup
# mount /dev/vgroup/mysql_snapshot /mnt/mysql_backup
# rsync -avz --delete /mnt/mysql_backup/var/lib/mysql/ user@10.20.1.5:/mnt/backup/mysql/

После копирования снепшот надо обязательно удалить.

# umount /mnt/mysql_backup
# lvremove -f /dev/vgroup/mysql_snapshot

Получили консистентный бэкап всех баз данных практически без остановки сервера, но с кратковременной блокировкой изменений.

Я собрал всё это в скрипт и протестировал на сервере с MariaDB и Zabbix Server. Сохранил бэкап локально, потом остановил MariaDB, полностью удалил директорию /var/lib/mysql и восстановил из бэкапа. Потом запустил СУБД. Запустилось без проблем, никаких ошибок. Бэкап получается консистентный.

Скрипт особо не отлаживал, так что аккуратнее с ним. Просто убедился, что работает. Сначала немного накосячил, так как после включения блокировки закрывал сессию, делал снепшот и подключался снова. Так нельзя, потому что команда FLUSH TABLES WITH READ LOCK живёт, пока открыто соединение. Надо в рамках него делать всё необходимое. Запустил lvcreate через SYSTEM.

Такой вот небольшой трюк в копилку LVM, который можно применять не только к СУБД, но и другим данным.

❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.

#lvm #mysql #backup
2👍115👎3
На днях разбирался с одной базой данных mysql на сервере, где есть пока неведомые проблемы с железом. Периодически бьются таблицы. Повезло, что база некритичная, будет плановый переезд. А пока занимался там частичным восстановлением. По ходу дела пришлось с дампами поработать. Сделаю несколько зарисовок оттуда на память. Возможно, кому-то будет полезна эта информация.

1️⃣ Я обычно использую следующий набор ключей при снятии дампа с помощью mysqldump:

# mysqldump -v --add-drop-database --add-locks --create-options --disable-keys --extended-insert --single-transaction --quick --set-charset --routines --events --triggers --comments --quote-names --order-by-primary --hex-blob --databases dbname01 > dbname01.sql

Не буду их все пояснять, можно у ИИ спросить. Сделаю акцент на --single-transaction. Этот ключ позволяет избежать блокировок при снятии дампа, в отличие от ключа --opt, который используется по умолчанию и включает в том числе --lock-tables. Пришлось от него отказаться и вручную перечислить некоторые его ключи, убрав блокировку.

Если какая-то таблица из-за ошибок не бэкапится и останавливает процесс снятия дампа, её можно исключить из дампа ключом --ignore-table=dbname01.table_01.

2️⃣ В таком дампе из-за ключа --add-drop-database в начале идёт команда на удаление базы данных: DROP DATABASE IF EXISTS `dbname01`;и создание новой: CREATE DATABASE IF NOT EXISTS `dbname01`; Я использую этот ключ, потому что удобно автоматически восстанавливать исходники и дамп базы по расписанию на каком-то тестовом сервере. Не нужно менять никакие настройки, не надо удалять старую базу и создавать новую. Берём бэкапы с прода и восстанавливаем их в неизменном виде.

Но это создаёт неудобство, если нужно из бэкапа восстановить копию базы на проде. Если забыть об этом, то при восстановлении базы данных, исходная удаляется. Поэтому перед восстановлением нужно заменить в дампе имя базы данных. Это нетривиальная задача, если дамп очень большой. Нельзя просто открыть его и вручную изменить имя базы. Сделать это проще всего с помощью sed. Меняем dbname01 на dbname01_copy, везде, где встречается:

# sed -i 's/`dbname01`/`dbname01_copy`/g' dbname01.sql

После этого можно создавать базу данных dbname01_copy и восстанавливать в неё содержимое дампа:

# mysql
> CREATE DATABASE dbname01_copy;
> use dbname01_copy;
> source ~/dbname01.sql;

Получили рядом копию рабочей базы.

3️⃣ Проще и удобнее делать полный дамп базы, особенно если в ней очень много таблиц. Можно бэкапить потаблично, но лично я так почти никогда не делаю. Из полного дампа легко получить бэкап отдельной таблицы, если это потребуется. Поможет awk:

# cat dbname01.sql | /usr/bin/awk '/CREATE TABLE `table_01`/,/UNLOCK TABLES/' > /tmp/table_01.sql

Теперь можно восстановить отдельную таблицу.

❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.

#mysql
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍138👎2
Продолжение утренней темы с Mysql. Не стал всё в одной публикации делать, чтобы не смешивать темы. У меня необычная ситуация возникла, с которой столкнулся впервые. Я ранее уже делал заметку по ошибке с базой данных. Это её продолжение.

Напомню, что в базе Mysql с движком InnoDB внезапно появляется ошибка такого вида:

[ERROR] InnoDB: Index corruption: rec offs 12361 next offs 0[page id: space=33355, page number=1250], index `PRIMARY` o
f table `dbname01`.`table_01`. Run CHECK TABLE. You may need to restore from a backup, or dump + drop + reimport the table.
[ERROR] InnoDB: We detected index corruption in an InnoDB type table. You have to dump + drop + reimport the table or, 
in a case of widespread corruption, dump all InnoDB tables and recreate the whole tablespace.
[ERROR] Got error 126 when reading table './dbname01/table_01'
[ERROR] mariadbd: Index for table 'table_01' is corrupt; try to repair it

При этом сама база работает нормально, данные пишутся, читаются. Просто спотыкается чтение определённых строк в проблемной таблице. Это становится заметно при снятии дампа, так как он останавливается на этих строках.

Никакие рекомендованные процедуры лично мне не помогли восстановить таблицу. Пришлось восстанавливать из бэкапа.

А странность тут вот в чём. Ещё когда первый раз я с этим столкнулся, то восстановил всю виртуалку из бэкапа на другой сервер. Запустил её там и спокойно всё прочитал в таблицах, снял дамп. Никаких повреждений данных, всё на месте. То есть данные на самом деле не пострадали.

И вот эта история повторилась на том же сервере спустя более чем полгода. Опять побилась таблица. Развернул ночной бэкап виртуалки – там всё в порядке. Все данные на месте. Делают тут же без остановки ещё один бэкап проблемной виртуалки с помощью PBS, восстанавливаю на другом сервере, снимаю дамп. Опять всё в порядке, все данные на месте.

Не понимаю, что происходит, и как такое возможно. Идея только одна – какие-то проблемы с железом. Так как это уже не первый случай, принял решение отказаться от аренды этого сервера. Перед окончанием оплаченного периода закажу другой и перееду туда, а этот сервер сдам с тикетом в поддержку с предложением проверить хорошенько память. По идее это она может давать такие проблемы, но лично я с подобным не сталкивался.

Мне непонятен такой момент. Если с памятью проблемы, то данные по идее должны всё же биться и теряться. А тут восстанавливаешь глючащую виртуалку в другом месте и с ней вдруг всё становится в порядке, данные на месте. СУБД обычно чувствительна к таким сбоям, база теряет свою целостность. А тут всё в порядке.

У кого-то есть ещё идеи по этой ошибке? Может кто-то сталкивался с подобным. А то сервер сдам и больше не узнаю, что с ним было. Особо нет времени и возможности полноценно тестировать чужой сервер, не имея к нему полного доступа.

#mysql #ошибка
2👍50👎2
В последних публикациях на тему бэкапа баз в Postgresql не раз всплывал вопрос: "А есть ли веб панели для управления бэкапами баз MySQL?". Я сам никогда их не использовал и не встречал. Знаю, что инструменты для подобного бэкапа есть в панелях по управлению веб серверами, типа Hestiacp, aaPanel и им подобным. Очевидно, если у вас не веб сервер, то такую панель ставить не имеет смысла. А специализированных панелей только для бэкапов не существует.

Решение, как оказалось, есть на поверхности. Существует известная веб панель для управления сервером - Webmin. Странно, что я по ней ни одной заметки ещё не написал. Хоть сам и не ставлю на свои сервера, но приходилось работать. Там есть интересные возможности, ради которых её можно использовать.

В Webmin есть модуль для управления MySQL. Я его попробовал. Настроек немного, но сделано вполне функционально. Я там немного покумекал, как сделать наиболее удобно и получилось неплохо. Расскажу по шагам, как я всё настроил.

1️⃣ Установка. Панель собрана в пакеты под популярные дистрибутивы. Подключить репозиторий можно автоматически через скрипт:

# curl -o webmin-setup-repo.sh https://raw.githubusercontent.com/webmin/webmin/master/webmin-setup-repo.sh
# sh webmin-setup-repo.sh

Либо вручную, добавив репозиторий и ключ от него. Для Debian это:

deb https://download.webmin.com/download/newkey/repository stable contrib

# apt-get install webmin --install-recommends

После установки можно идти по IP адресу сервера на порт 10000 и заходить под учёткой root. После этого в разделе Webmin ⇨ Webmin Configuration ⇨ IP Access Control ограничьте доступ к панели по IP адресам. Её нельзя выставлять в публичный доступ. В панели периодически находят уязвимости, а у неё полный доступ к серверу.

2️⃣ Если MySQL сервер устанавливался после установки Webmin, то модуль управления будет в списке Un-used Modules, если после, то в Servers. Чтобы модуль переехал из Un-used, нажмите на Refresh Modules.

3️⃣ В модуле есть возможность настроить подключение к MySQL серверу в разделе Configuration ⇨ Server Connections. По умолчанию используется localhost, но вы можете подключиться к любому другому серверу, в том числе в контейнере. Главное, чтобы к нему был доступ извне.

4️⃣ Можно настроить бэкап как конкретной базы, так и всех разом. При бэкапе всех баз, каждая из них сохраняется в отдельный файл и может при желании сразу сжиматься. Бэкап выполняется через стандартный mysqldump. Ключи запуска могут добавлять при необходимости.

5️⃣ Для бэкапов есть планировщик. Работает в формате заданий cron. Да и сами задания добавляются в системный файл /var/spool/cron/crontabs/root.

6️⃣ Основное неудобство – задание бэкапа каждый раз перезаписывает файлы. Нет возможности хранить несколько копий и автоматически удалять старые. Я решил этот вопрос максимально просто в лоб.

Можно указать команды, которые будут выполняться до запуска бэкапа и после. Настроил бэкап в директорию /root/sql_backup а после бэкапа добавил команду:

mkdir /mnt/backup/`date +"%Y-%m-%d_%H-%M"` && cp -R /root/sql_backup/* /mnt/backup/`date +"%Y-%m-%d_%H-%M"`/

Она создаёт директорию вида 2025-07-23_16-27 и копирует туда созданные файлы. При следующем запуске имя директории будет другое.

Для того, чтобы очищать старые бэкапы, добавил команду перед запуском:

find /mnt/backup -type d -printf '%T@ "%p"\n' | sort -n | head -n -30 | awk '{print $2}' | xargs -I {} rm -r {}

Она оставляет 30 новых директорий, а более старые удаляет. Вопрос с ротацией бэкапов решён. Работает просто и наглядно. При желании можно добавить ещё одну команду, которая будет куда-нибудь во вне копировать эти файлы.

7️⃣ Для восстановления бэкапов достаточно создать новую базу или выбрать существующую, зайти в неё, выбрать раздел Execute SQL ⇨ Run SQL from file ⇨ From local file и выбрать один из дампов. При желании можно подключиться к другому серверу и восстановить бэкап туда.

Получилось простое в настройке и вполне функциональное решение.

❗️Если заметка вам полезна, не забудьте 👍 и забрать в закладки.

#backup #mysql
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍75👎5