Основной причиной перехода с SQLite на MySQL базу данных является внезапное повреждение файла .db, после которого невозможно ни открыть файл, ни восстановить оставшиеся данные — ни в этот, ни в новый файл базы.
В данной публикации описан пошаговый алгоритм миграции действующей базы данных без использования docker.

sqlite> .tables
Error: file is not a database
database disk image is malformed

Подробнее можно почитать тут: https://www.sqlite.org/faq.html#q21

Установка MySQL на Ubuntu версии 20+

sudo apt install mysql-server -y ;\
mysql --version ;\
sudo mysql_secure_installation ;\
systemctl status mysql

Создание MySQL пользователя

Генерируем рандомный пароль, например через uuid.uuid4() и локально подключаемся к базе через командную строку

sudo mysql

Создаем пользователя и базу данных, значения <user> и <password> нужно заменить на свои

CREATE USER '<user>'@'localhost' IDENTIFIED WITH mysql_native_password BY '<password>';
GRANT ALL PRIVILEGES ON *.* TO '<user>'@'localhost' WITH GRANT OPTION;

CREATE USER '<user>'@'%' IDENTIFIED WITH mysql_native_password BY '<password>';
GRANT ALL PRIVILEGES ON *.* TO '<user>'@'%' WITH GRANT OPTION;

FLUSH PRIVILEGES;

create database marzban;

Для того, чтобы база была видна извне - нужно изменить строку в /etc/mysql/mysql.conf.d/mysqld.cnf

#bind-address           = 127.0.0.1  # Старое значение
bind-address            = 0.0.0.0    # Новое значение

Установка sqlite3 и снятие дампа

cd ~ ;\
wget https://github.com/nalgeon/sqlite/releases/download/3.47.0/sqlite3-ubuntu ;\
mv sqlite3-ubuntu sqlite3 ;\
chmod +x sqlite3

Снимаем дамп текущей базы (sqlite):
db_file - файл базы Marzban панели
dump_file - файл будущего дампа

mkdir -p $HOME/dump ;\
db_file=$HOME/sqlite3_backups/db_backup_2024-12-24_10-30-01.sqlite3
dump_file=$HOME/dump/marzban_dump.sql
$HOME/sqlite3 $db_file '.dump --data-only' | sed "s/INSERT INTO \([^ ]*\)/REPLACE INTO \`\\1\`/g" > $dump_file
ls -la $dump_file

Восстановление и переключение на MySQL

Нужно заменить строку подключения в .env файле marzban.
У меня он находится в $HOME/code/Marzban/.env, новая строка:

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://<user>:<password>@127.0.0.1:3306/marzban"

Значения <user> и <password> нужно заменить на созданные выше (логин и пароль пользователя в базе MySQL).

Далее нужно применить миграции к новой, пустой базе, предварительно активировав виртуальное окружение, либо запустить команду внутри контейнера (если вы работаете с панелью через docker)

alembic upgrade head

Восстановление дампа в MySQL

Внимание!

Формируемый с помощью SQLite файл бэкапа не оптимизирован под быстрое восстановление: вместо конструкции INSERT там используется REPLACE. Подозреваю, что восстановление проходит с построчным коммитом, что сильно замедляет процесс.
Для примера: мой файл db.sqlite3 занимал 230 МБ, а после конвертации в MySQL формат размер файла уменьшился до 167 МБ. На обработку такого файла у MySQL ушло больше часа.

Проблема в таблице node_user_usages, в которой хранится история «утилизации трафика» пользователями. У меня в ней было ~1,5 млн строк.

Рекомендация следующая: если у вас уже есть пользователи, и их количество больше 1000, то есть переход выполняется не между пустыми базами, — нужно подготовить сервисы к бесшовному переходу, так как окно технических работ может привести к негативному опыту у ваших клиентов.

Ориентировочно ваш план должен быть следующим:

  • Остановка бота

  • Остановка marzban - панели

  • Снятие бэкапа

  • Конвертация

  • Включение панели marzban (откат в .env файле на sqlite)

  • Восстановление бэкапа в MySQL базу данных

  • Изменение .env на pymysql

  • Перезапуск панели marzban

  • Синхронизация изменений панели

mysql -u <user> -p<password> -h 127.0.0.1 marzban -e "SET FOREIGN_KEY_CHECKS = 0; SET NAMES utf8mb4; SOURCE /root/dump/marzban_dump.sql;"

Обратите внимание, что параметр -p вводится слитно со значением пароля. Далее включение marzban панели и проверка корректности подключения

systemctl restart marzban

На этом этапе переход на MySQL базу данных завершен! ?Рекомендую сразу настроить регулярный бэкап базы и выгрузку дамп файла на сторонний сервер.


Большое спасибо всем за внимание! Если вам интересны подобные рассуждения - подписывайтесь на мой канал artydev & Co.

Комментарии (13)


  1. badangel
    13.01.2025 11:24

    Я так пешком стал ходить, когда в автомобиле внезапно кончился бензин /s


  1. MonkAlex
    13.01.2025 11:24

    А бэкапы на sqlite делать не хотелось, зато после перехода начали делать на mysql? =)


    1. artydev Автор
      13.01.2025 11:24

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

      Проблема простая: откуда взять информацию за последние 30 минут?


      1. MonkAlex
        13.01.2025 11:24

        mysql как то решит эту проблему? Я не в курсе, в мелких личных проектах использую sqlite и тоже где то раз в сутки бэкаплю


        1. artydev Автор
          13.01.2025 11:24

          Тоже использую sqlite в личных проектах и никаких проблем нет, тема поста скорее решает проблему конкретного сервиса/продукта (marzban - панель), в котром непонятное стечение абстоятельств приводит к повреждению файла (поймать ошибку не удалось)


          1. am_i_dead
            13.01.2025 11:24

            причина в том, что в sqlite данные повреждаются при многопоточной записи, а marzban как раз работает в многопоток и игнорирует механизм защиты данных, чтобы работало быстрее

            connect_args={"check_same_thread": False}


            1. MonkAlex
              13.01.2025 11:24

              А по ссылке из статьи https://www.sqlite.org/faq.html#q21 заявляется что нельзя повредить файл штатной работой

              Это какая то нештатная возможность питоновской либы?

              UPD: похоже что marzban использует sqlalchemy, а у того поведение по умолчанию check_same_thread=False, что может быть важно для питоновской обвязки. За питоном где то есть ещё sqlite в каком-то виде, который в теории таки безопасен. Я запутался, кто тут что делает не так, но выглядит странно =)


            1. andreymal
              13.01.2025 11:24

              Это не должно приводить к повреждению, если sqlalchemy использует пул соединений


      1. andreymal
        13.01.2025 11:24

        после первого падения базы

        То есть падение далеко не единственное? Кажется, вам надо не на mysql переходить, а проблемы с сервером чинить...

        Использую sqlite3 в личных проектах разной степени нагруженности уже лет десять — ни разу ничего не падало


        1. artydev Автор
          13.01.2025 11:24

          Сложно чинить проблему стороннего сервиса (marzban - панель), поэтому было принято решение о переходе на другую СУБД, с которой, по опыту сообщества - таких проблем нет


        1. am_i_dead
          13.01.2025 11:24

          проблема в том как marzban работает с базой


  1. Akina
    13.01.2025 11:24

    То есть Error: file is not a database , но дамп с него всё равно снимается... забавненько.


    1. artydev Автор
      13.01.2025 11:24

      В этом и проблема, что не снимается (единственный вариант - использовать предыдущий дамп)