Чтобы подключиться к удалённому серверу MySQL, нужны пять вещей: имя хоста или IP-адрес, порт, имя пользователя, его пароль и имя базы данных. С ними mysql -h host -P port -u user -p database даёт вам приглашение командной строки, а те же пять значений составляют строку подключения для вашего приложения. Большинство сбоев происходит в одном из трёх мест: сеть (порт неверный или недоступен), учётная запись (пользователь существует для другого шаблона хоста, чем тот, с которого вы подключаетесь) или плагин аутентификации (старый клиент, который не понимает caching_sha2_password, а MySQL 8.4 использует его по умолчанию и при первом входе требует зашифрованного соединения).
Это руководство проходит по каждой части в том порядке, в каком вы с ними сталкиваетесь: нужные данные, клиент командной строки, шифрование и --ssl-mode, плагин аутентификации, строки подключения для распространённых сред выполнения, графические инструменты и сообщения об ошибках, которые люди действительно видят. Всё здесь относится к MySQL 8.4 LTS, с пометками там, где 8.0 ведёт себя иначе.
Пять параметров и откуда они берутся#
| Параметр | Пример | Примечания |
|---|---|---|
| Хост | db.example.net или 203.0.113.20 | Имя или адрес. С другой машины - никогда не localhost |
| Порт | 3306 или тот, что указан в тарифе | 3306 - лишь значение по умолчанию. Хостинг часто использует другой |
| Пользователь | app | Учётная запись - это имя пользователя плюс шаблон хоста |
| Пароль | сгенерированный | Храните его в переменной окружения, а не в коде |
| База данных | appdb | Необязательна при подключении, но приложение должно её указывать |
На вашей собственной машине MySQL слушает 3306, а учётная запись, под которой вы работаете, часто root@localhost. На хостинговой базе данных ничего из этого нельзя считать само собой разумеющимся. Хост - это тот адрес, который даёт провайдер, порт может быть любым, а учётная запись для повседневной работы - пользователь приложения с правами на одну базу данных, а не root.
На RE:NODE сервер MySQL создаётся с паролем root, базой данных приложения и пользователем приложения, всё сгенерировано, а доступен он по хосту и порту тарифа, которые показаны в панели. Именно эти два значения и нужно копировать: не угадывайте 3306. На тарифах баз данных нет слота прокси, поэтому к серверу подключаются напрямую по этому адресу и порту, а не через домен и сертификат, которыми управляет панель.
Одно правило избавит от массы путаницы потом: localhost в MySQL - особый случай. Когда клиенту передают localhost, он подключается через файл Unix-сокета, а не по TCP, и полностью игнорирует порт. Если вы на той же машине и хотите TCP, используйте 127.0.0.1. С другой машины localhost просто означает ваш собственный компьютер.
Подключение через клиент командной строки mysql#
Клиент mysql входит в каждый пакет сервера MySQL и в пакеты только с клиентом (mysql-client в Debian и Ubuntu, mysql в Homebrew, MySQL Installer или ZIP-архив в Windows). Используйте клиент серии 8.x или новее. Клиенты старше 8.0 не могут пройти аутентификацию в учётной записи 8.4 с настройками по умолчанию - об этом ниже.
$ mysql -h db.example.net -P 30412 -u app -p appdbEnter password:Welcome to the MySQL monitor. Commands end with ; or \g.Server version: 8.4.6 MySQL Community Server - GPLmysql>-p без ничего после него заставляет клиент спросить пароль, и тогда он не попадает ни в историю шелла, ни в список процессов. -pSecret (без пробела) работает, но оставляет пароль в ~/.bash_history. -p Secret (с пробелом) делает не то, что кажется: Secret воспринимается как имя базы данных, а пароль у вас всё равно спросят.
Войдя, проверьте, куда вы попали:
SELECT CURRENT_USER(), USER(), DATABASE(), @@version, @@port;USER() - это имя и хост, которые вы заявили; CURRENT_USER() - учётная запись, которую MySQL на самом деле сопоставил, и именно её привилегии действуют. Если они неожиданно различаются - вы подключились как app, но сопоставились с ''@'%', анонимной учётной записью, - вы нашли причину, по которой ваши GRANT как будто не работают. \s (или status) выводит сводку соединения, в том числе зашифровано ли оно:
SSL: Cipher in use is TLS_AES_256_GCM_SHA384Connection: db.example.net via TCP/IPServer version: 8.4.6 MySQL Community Server - GPLProtocol version: 10Учётные данные в файле опций
Набирать хост и порт каждый раз быстро надоедает. Клиент читает файлы опций, и группа [client] в ~/.my.cnf (или %APPDATA%\MySQL\.mylogin.cnf через инструмент ниже) задаёт значения по умолчанию:
[client]host=db.example.netport=30412user=appssl-mode=REQUIREDНе кладите пароль в обычный файл и дайте клиенту его спросить, либо используйте mysql_config_editor, который пишет обфусцированный .mylogin.cnf:
$ mysql_config_editor set --login-path=prod --host=db.example.net \ --port=30412 --user=app --password$ mysql --login-path=prod appdbОбфускация защищает от случайного взгляда, а не от настойчивого читателя. Поставьте chmod 600 на любой из этих файлов. Клиент отказывается читать файл опций, доступный на запись всем, и прямо об этом сообщает.
Шифрование: ssl-mode и что означает каждое значение#
Соединение MySQL либо зашифровано TLS, либо нет, а насколько настойчиво этого требовать, решает клиент через --ssl-mode. По умолчанию стоит PREFERRED: шифровать, если сервер это предлагает, и молча откатываться на открытый текст, если нет.
--ssl-mode | Шифрует | Проверяет сертификат | Когда использовать |
|---|---|---|---|
DISABLED | Нет | Нет | Никогда через интернет |
PREFERRED | Если предложено | Нет | По умолчанию; при сбое пропускает |
REQUIRED | Да, или ошибка | Нет | Самоподписанный сертификат сервера |
VERIFY_CA | Да | Подписан вашим CA | У вас есть файл CA |
VERIFY_IDENTITY | Да | CA и имя хоста | Публичный CA, имя совпадает |
MySQL 8.x при первом запуске генерирует самоподписанный сертификат и ключ в каталоге данных, если оператор это не отключил, поэтому большинство серверов предлагают TLS из коробки. Самоподписанный сертификат шифрует соединение, но ничего не доказывает о том, кто на другом конце, поэтому VERIFY_CA и VERIFY_IDENTITY с ним не пройдут, если только вам не дали файл CA и вы не передали его через --ssl-ca=ca.pem.
Для базы данных, к которой подключаются через публичный интернет, ставьте как минимум REQUIRED. На современном железе это не стоит ничего измеримого и закрывает случай, когда неправильно настроенный сервер молча понижает вас до открытого текста. Затем проверьте через \s или из SQL:
SHOW SESSION STATUS LIKE 'Ssl_version';SHOW SESSION STATUS LIKE 'Ssl_cipher';Пустой Ssl_cipher означает, что сессия не зашифрована. MySQL 8.4 по умолчанию принимает только TLS 1.2 и 1.3, а старый серверный переключатель --ssl и переменную have_ssl в нём убрали; если руководство велит проверить have_ssl, оно написано для 5.7 или 8.0.
Сервер тоже может настаивать. REQUIRE SSL на учётной записи (ALTER USER 'app'@'%' REQUIRE SSL;) запрещает этому пользователю любое незашифрованное соединение, а require_secure_transport=ON запрещает его всем. Обе вещи полезно знать, если у вас есть учётная запись root.
caching_sha2_password и проблема первого подключения#
Начиная с MySQL 8.0 плагин аутентификации по умолчанию - caching_sha2_password. В MySQL 8.4 старый плагин mysql_native_password не просто вышел из моды - он отключён по умолчанию, и его нужно включить через mysql_native_password=ON в конфигурации сервера, прежде чем сможет войти хоть одна учётная запись, которая его использует. В MySQL 9.0 его удалили совсем. Рассчитывайте на caching_sha2_password везде.
У плагина есть одна особенность, которая порождает большую часть непонятных ошибок. Когда пользователь впервые проходит аутентификацию после перезапуска сервера (или после смены пароля), у сервера нет кэшированного хэша для этого пользователя, и чтобы его вычислить, пароль нужно передать безопасно. «Безопасно» означает одно из двух:
- Соединение зашифровано TLS, и тогда пароль идёт внутри туннеля, или
- Клиент получает публичный RSA-ключ сервера и шифрует пароль им.
Если ни то ни другое не выполняется, вход завершается ошибкой:
ERROR 2061 (HY000): Authentication plugin 'caching_sha2_password' reportederror: Authentication requires secure connection.Лечение - использовать TLS, что вам в любом случае стоит делать. Если не получается, клиент может запросить ключ через --get-server-public-key (командная строка), allowPublicKeyRetrieval=true (JDBC) или аналогичную опцию в вашем драйвере. Запрос ключа по незашифрованному соединению открыт для атаки «человек посередине», которая подменит ключ, и именно поэтому по умолчанию это выключено.
Другая ошибка - удел старых клиентов и старых драйверов:
Authentication plugin 'caching_sha2_password' cannot be loadedЭто клиент эпохи 5.7, старый PHP mysqlnd или библиотека вроде исходного пакета Node mysql (не mysql2), которая так и не научилась этому плагину. Обновите клиент или драйвер. Пересоздание пользователя с mysql_native_password сработает, только если на сервере этот плагин включён, а в 8.4 он выключен, пока кто-то его не включит, - исправить клиент выгоднее. Полный список изменений аутентификации есть в статье MySQL 8.4 LTS: что изменилось.
Строки подключения для распространённых сред выполнения#
Каждому драйверу нужны те же пять значений, просто расставленные по-разному. Храните их в переменных окружения и собирайте строку во время выполнения; как это делать, описано в статье переменные окружения и секреты.
DB_HOST=db.example.netDB_PORT=30412DB_USER=appDB_PASSWORD=change-meDB_NAME=appdbDATABASE_URL=mysql://app:change-me@db.example.net:30412/appdbURL ломается, если в пароле есть @, :, / или #, потому что это синтаксис URL. Либо закодируйте их процентной кодировкой (@ превращается в %40), либо передавайте части по отдельности, что позволяет большинство драйверов.
import mysql from "mysql2/promise";const pool = mysql.createPool({ host: process.env.DB_HOST, port: Number(process.env.DB_PORT), user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, connectionLimit: 10, ssl: { rejectUnauthorized: false }, // TLS on, self-signed certificate accepted});from sqlalchemy import create_engineimport osurl = (f"mysql+pymysql://{os.environ['DB_USER']}:{os.environ['DB_PASSWORD']}" f"@{os.environ['DB_HOST']}:{os.environ['DB_PORT']}/{os.environ['DB_NAME']}" "?charset=utf8mb4")engine = create_engine(url, pool_size=5, pool_recycle=1800, # TLS on; no CA given, so a self-signed cert is accepted connect_args={"ssl": {"check_hostname": False}})$dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', getenv('DB_HOST'), getenv('DB_PORT'), getenv('DB_NAME'));$pdo = new PDO($dsn, getenv('DB_USER'), getenv('DB_PASSWORD'), [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,]);jdbc:mysql://db.example.net:30412/appdb?sslMode=REQUIRED&characterEncoding=UTF-8Две настройки должны быть в каждой строке. Кодировка должна быть utf8mb4, иначе однажды ваше приложение сохранит эмодзи как четыре вопросительных знака - смотрите utf8mb4 и правила сравнения. И что-то должно пересоздавать простаивающие соединения раньше, чем их закроет серверный wait_timeout (по умолчанию 28800 секунд, восемь часов), иначе первый запрос после тихой ночи упадёт с «MySQL server has gone away». Этим занимаются pool_recycle в SQLAlchemy и таймаут простоя пула в других драйверах. Каким должен быть размер каждого пула - отдельная тема: лимиты соединений MySQL и пулы.
Если говорить именно о PHP, статья PHP PDO и MySQL подробнее разбирает подготовленные запросы и режимы ошибок, а сторона C# - MySqlConnector против Connector/NET от Oracle - описана в статье .NET с PostgreSQL, MySQL или SQL Server.
GUI-инструменты: Workbench, DBeaver, HeidiSQL и другие#
Подойдёт любой из распространённых графических клиентов, и все они спрашивают одни и те же поля. Различается лишь то, где они прячут опции TLS и публичного ключа.
- MySQL Workbench (Oracle, бесплатный): Connection Method
Standard (TCP/IP), хост, порт, пользователь. На вкладке SSL выставьте Use SSL вRequire. Workbench ближе всех к собственным возможностям сервера, включая визуальный EXPLAIN. - DBeaver (бесплатная community-редакция): новое подключение MySQL, заполните Server Host и Port. На вкладке Driver properties выставьте
allowPublicKeyRetrievalвtrue, если вы не используете TLS и получили ошибку публичного ключа; на вкладке SSL отметьте Use SSL, а для самоподписанного сертификата снимите галочку проверки сертификата. - HeidiSQL (Windows, бесплатный): Network type
MariaDB or MySQL (TCP/IP), затем вкладка SSL. Лёгкий и быстрый, вполне годится для просмотра и быстрых правок. - TablePlus, DataGrip, Beekeeper Studio: те же поля, TLS на вкладке SSL или Advanced.
Общая для всех ловушка - версия встроенного драйвера. Инструмент со старым коннектором может падать с ошибкой плагина, описанной выше, хотя клиент командной строки работает. Это исправляет обновление инструмента или драйвера внутри него. Статья сравнение GUI-клиентов MySQL разбирает каждый подробнее.
Когда подключение не удаётся: расшифровка ошибок#
ERROR 2003 (HY000): Can't connect to MySQL server on 'db.example.net:3306' (110)Сетевой сбой ещё до того, как дело дошло до MySQL. Ошибка 110 - это таймаут (что-то отбрасывает пакеты: файрвол, неверный адрес), 111 - «connection refused» (адрес верный, но на этом порту никто не слушает). Сначала проверьте порт - 3306 в сообщении выдаёт, что клиент взял значение по умолчанию, потому что вы забыли -P. Затем проверьте, что ваша собственная сеть разрешает исходящие соединения на этот порт; некоторые офисные и университетские сети блокируют всё, кроме веб-трафика. nc -vz db.example.net 30412 или Test-NetConnection db.example.net -Port 30412 в Windows покажет, отвечает ли порт вообще.
ERROR 1045 (28000): Access denied for user 'app'@'198.51.100.7' (using password: YES)До MySQL дошли, и он отклонил вход. Либо пароль неверный, либо нет учётной записи app, чей шаблон хоста совпадает с вашим адресом 198.51.100.7. В сообщении указан хост, который увидел MySQL, и совпадать должен именно он. using password: NO означает, что клиент вообще не отправил пароль - обычно пропущен -p или переменная окружения пуста. Как сопоставляются шаблоны хостов, объясняет статья пользователи и привилегии MySQL.
ERROR 1044 (42000): Access denied for user 'app'@'%' to database 'other'Вы вошли нормально, но запросили базу данных, на которую у учётной записи нет прав. Подключитесь к нужной базе или попросите GRANT.
ERROR 1129 (HY000): Host '198.51.100.7' is blocked because of many connection errorsС вашего адреса было больше неудачных рукопожатий, чем разрешает max_connect_errors (по умолчанию 100). Кто-то с правами администратора снимает блокировку через TRUNCATE TABLE performance_schema.host_cache;. В MySQL 8.4 FLUSH HOSTS больше нет; замена ему - этот TRUNCATE (или mysqladmin flush-hosts). Затем найдите то, что давало сбои, - обычно это проверка здоровья, которая открывает TCP-соединение и закрывает его, не входя.
ERROR 1040 (HY000): Too many connectionsСервер упёрся в max_connections (по умолчанию 151). Что-то теряет соединения, или пул больше, чем сервер способен выдержать. Повышение лимита редко бывает решением; почему - объясняет статья пулы соединений и лимиты.
ERROR 2013 (HY000): Lost connection to MySQL server during queryERROR 2006 (HY000): MySQL server has gone awayСоединение умерло. Обычные причины: сервер закрыл простаивающее соединение после wait_timeout, отдельный оператор превысил max_allowed_packet (в 8.x по умолчанию 64 МБ), сервер перезапустился или сетевое устройство посередине убило долго простаивавшую TCP-сессию. Пересоздавайте соединения пула чаще, чем истекает таймаут, и переподключайтесь при этой ошибке.
Как обезопасить удалённую базу данных#
База данных, доступная из интернета, становится мишенью в тот же момент, как появляется. Сканеры весь день перебирают root с распространёнными паролями на каждом адресе и порту, который находят. Защита несложная:
- Для приложения используйте пользователя приложения, а не root. Root оставьте для администрирования и дайте ему длинный сгенерированный пароль.
- Требуйте TLS для всего, что идёт через интернет.
- Дайте каждому приложению собственного пользователя с правами только на его базу данных, чтобы утечка одних учётных данных не открывала остальное.
- Меняйте пароль в тот момент, когда он мог утечь (закоммиченный
.env, скриншот в переписке с поддержкой), - MySQL 8 поддерживает двойные пароли черезRETAIN CURRENT PASSWORD, так что менять можно без простоя. - Следите за неудачными входами.
performance_schema.host_cacheсчитает ошибки по каждому адресу.
Остальное описано в статье чек-лист безопасности базы данных, включая то, что делать, если вы подозреваете, что учётными данными воспользовался кто-то другой.
FAQ#
Какой порт использует MySQL?
3306 по умолчанию и 33060 для X Protocol, который использует хранилище документов MySQL Shell. Хостинговый сервер может слушать любой порт, поэтому используйте тот, что дал провайдер, а не предполагайте значение по умолчанию. Если вы забудете -P, клиент молча возьмёт 3306.
Почему localhost работает на сервере, но не с моего ноутбука?
Потому что localhost означает «эта машина», а в MySQL ещё и «использовать Unix-сокет». С вашего ноутбука localhost - это ваш ноутбук. Используйте имя хоста или адрес сервера и убедитесь, что существует учётная запись с шаблоном хоста, который совпадает с местом, откуда вы подключаетесь.
Нужен ли TLS, если пароль надёжный?
Да. Без TLS каждый запрос и каждый результат идут по сети открытым текстом, а caching_sha2_password приходится откатываться на обмен RSA-ключом при первом входе после перезапуска. Выставьте --ssl-mode=REQUIRED или аналог в вашем драйвере.
Как исправить «Authentication plugin caching_sha2_password cannot be loaded»?
Обновите клиент или драйвер до того, что поддерживает аутентификацию MySQL 8: клиент mysql версии 8.x или новее, mysql2 вместо mysql в Node, актуальный PHP, актуальный Connector/J. Перевод учётной записи на mysql_native_password сработает, только если на сервере 8.4 этот плагин включён, а по умолчанию он выключен.
Можно ли подключиться, не указывая базу данных?
Да. Не указывайте её в командной строке и выполните USE appdb; после входа или добавляйте имя базы данных перед именами таблиц. Приложения же должны указывать базу данных в подключении, чтобы каждый запрос выполнялся там, где вы ожидаете.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.