Помню свой первый настоящий проект на Laravel. Всё работало идеально на тестовом сервере, но стоило развернуть приложение на новой машине, как появилось это проклятое сообщение: "ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'". Тогда я потратил почти сутки, перелопачивая форумы и документацию. Сегодня разберу эту ошибку так, как мне самому не хватало тогда понимания.
Суть проблемы в том, что клиент MySQL пытается подключиться к серверу через Unix-сокет, а что-то идёт не так. На Unix-подобных системах локальные соединения работают не через TCP/IP порты, а через специальный файл-сокет. Это быстрее и эффективнее для локальных подключений. Когда вы пишете mysql -u root -p без указания хоста, система автоматически ищет этот файл. И если его нет, повреждён путь или закрыт доступ, получаете ошибку 2002.
Для веб-разработчика это может означать полную остановку работы. PHP-приложение выдаёт PDOException, Node.js скрипты падают с таймаутом, Laravel миграции зависают. Пока не разберёшься с корнем проблемы, никакой код не поможет.
Невидимый сервер: когда процесса просто нет
Первая и самая частая причина банальна до неприличия: MySQL или MariaDB просто не запущен. Никакого демона mysqld в памяти, никакого сокет-файла на диске. Клиент стучится в пустоту и получает "No such file or directory".
Проверяю статус сервиса командой, которая стала для меня второй натурой:
systemctl status mysql
Для MariaDB меняю на systemctl status mariadb. Если вижу "inactive (dead)" или "failed", значит сервер мёртв. Иногда статус показывает "active (exited)", что тоже плохо - процесс попытался запуститься и сразу завершился.
Запускаю сервис:
sudo systemctl start mysql
В половине случаев этого достаточно. Но если запуск проваливается, начинается настоящая детективная работа. Смотрю журнал ошибок:
sudo journalctl -xeu mysql
Или классический лог-файл:
sudo tail -n 100 /var/log/mysql/error.log
Однажды столкнулся с ситуацией, когда сервер не мог запуститься из-за того, что предыдущий процесс некорректно завершился и оставил lock-файл. В логах было сообщение о невозможности получить блокировку. Удалил старый lock, и всё заработало. В другой раз проблема оказалась в нехватке оперативной памяти - система убила процесс через OOM killer, о чём честно написала в системном журнале.
Проверяю дисковое пространство:
df -h
Если раздел с /var заполнен на 100%, MySQL физически не может создать сокет-файл и записывать логи. Очищаю старые файлы, и проблема решается.
После успешного запуска настраиваю автозапуск:
sudo systemctl enable mysql
Теперь после перезагрузки сервера база поднимется сама, без моего участия.
Потерянный адрес: когда клиент и сервер говорят на разных языках
Вторая причина коварнее. Сервер работает, процесс mysqld висит в памяти, но клиент всё равно не может подключиться. Почему? Потому что ищет сокет-файл не там, где его создал сервер.
Типичная ситуация: сервер положил сокет в /var/run/mysqld/mysqld.sock, а клиент ожидает /tmp/mysql.sock. Или наоборот. Такое происходит после обновления системы, миграции между дистрибутивами, установки нескольких версий MySQL/MariaDB одновременно.
Проверяю, существует ли файл:
ls -l /var/run/mysqld/mysqld.sock
Если файла нет, смотрю конфигурацию сервера. Открываю /etc/mysql/my.cnf или /etc/mysql/mysql.conf.d/mysqld.cnf (на Ubuntu) и ищу параметр socket в секции [mysqld]:
[mysqld]
socket = /var/run/mysqld/mysqld.sock
Затем проверяю настройки клиента в секции [client]:
[client]
socket = /var/run/mysqld/mysqld.sock
Эти пути должны совпадать! Если сервер настроен на один путь, а клиент на другой, соединение невозможно. Исправляю несоответствие, сохраняю файл и перезапускаю службу.
На практике встречал ситуацию, когда на одном сервере стояли и MySQL 5.7, и MariaDB 10.5. У каждого свой конфиг, свой путь к сокету. Попытка подключиться приводила к хаосу - система не понимала, к какому серверу обращаться. Решение: удалить одну из СУБД полностью.
Проверить, какие пакеты установлены:
dpkg -l | grep mysql
dpkg -l | grep mariadb
Если вижу конфликтующие версии, удаляю лишнее:
sudo apt-get purge mysql-*
Есть хитрый способ обойти проблему сокета временно - подключиться через TCP/IP:
mysql -h 127.0.0.1 -P 3306 -u root -p
Обратите внимание: использую IP-адрес 127.0.0.1, а не localhost. Когда указываете localhost, MySQL автоматически пытается использовать Unix-сокет. Когда указываете IP - переключается на TCP/IP соединение. Если через IP подключение работает, а через localhost нет, проблема точно в сокете.
Для PHP-приложений явно указываю путь в DSN:
$pdo = new PDO('mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=myapp', 'user', 'pass');
В Node.js с библиотекой mysql2:
const connection = mysql.createConnection({
socketPath: '/var/run/mysqld/mysqld.sock',
user: 'root',
database: 'myapp'
});
Это избавляет от неопределённости и делает поведение предсказуемым.
Закрытые двери: когда права доступа рушат всё
Третья причина касается прав доступа к сокет-файлу и директории, где он лежит. Даже если сервер запущен и путь правильный, без корректных разрешений клиент не сможет прочитать или записать в сокет.
Проверяю владельца и права:
ls -ld /var/run/mysqld/
ls -l /var/run/mysqld/mysqld.sock
Правильный вывод выглядит примерно так:
drwxr-xr-x 2 mysql mysql 60 Oct 28 10:00 /var/run/mysqld/
srwxrwxrwx 1 mysql mysql 0 Oct 28 10:00 /var/run/mysqld/mysqld.sock
Директория и файл должны принадлежать пользователю mysql и группе mysql. Если вижу другого владельца (например, root), это проблема. Исправляю:
sudo chown -R mysql:mysql /var/run/mysqld/
sudo chmod 755 /var/run/mysqld/
После изменения прав перезапускаю службу:
sudo systemctl restart mysql
Столкнулся однажды с ситуацией на Ubuntu Server, где после обновления пакетов AppArmor начал блокировать доступ MySQL к собственному сокету. Проверяю статус:
sudo aa-status
Если профиль mysql активен и есть нарушения, редактирую правила в /etc/apparmor.d/usr.sbin.mysqld. Добавляю разрешение:
/run/mysqld/mysqld.sock rw,
Перезагружаю профиль:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
На CentOS с SELinux проблемы похожие. Восстанавливаю контекст безопасности:
sudo restorecon -Rv /var/run/mysqld
Важный момент: никогда не ставьте права 777 на сокет или директорию, даже если это решает проблему. Это дыра в безопасности размером с ворота. Правильные права - 755 для директории и 660 для сокета.
Продвинутая диагностика: когда базовые шаги не помогли
Бывают случаи, когда стандартный алгоритм не работает. Тогда копаю глубже.
Проверяю, какие процессы слушают сокеты:
netstat -ln | grep mysql
Или с помощью ss:
ss -xl | grep mysql
Если ничего не показывает, значит сервер точно не создал сокет. Если показывает, но подключиться не получается, проблема в правах или конфигурации клиента.
Смотрю, какие конфигурационные файлы загружает MySQL:
mysql --help | grep my.cnf
Система может читать несколько файлов в определённом порядке: /etc/my.cnf, /etc/mysql/my.cnf, ~/.my.cnf. Последний перезаписывает предыдущие. Если где-то в цепочке указан неправильный путь, получаю конфликт.
Проверяю параметр bind-address в конфигурации. Если он установлен как bind-address = ::1 (только IPv6), а система пытается подключиться через IPv4, могут быть проблемы. Меняю на:
bind-address = 127.0.0.1
Иногда помогает создание символической ссылки между путями:
sudo ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock
Но это костыль, а не решение. Правильнее унифицировать конфигурацию.
Контейнеры и виртуализация: отдельный круг проблем
При работе с Docker ошибка 2002 приобретает новые измерения. Внутри контейнера путь к сокету может отличаться от хостовой системы. Если монтируете volume неправильно, клиент снаружи не увидит сокет изнутри.
В docker-compose.yml правильно настраиваю volume:
services:
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
- /var/run/mysqld:/var/run/mysqld
environment:
MYSQL_ROOT_PASSWORD: secret
Для приложений внутри контейнеров всегда использую TCP-соединение, а не сокеты. Это проще и надёжнее:
services:
web:
image: myapp
environment:
DB_HOST: db
DB_PORT: 3306
В облачных окружениях вроде AWS RDS или Google Cloud SQL сокеты вообще не используются - только TCP/IP. Настраиваю подключение соответственно.
Финальный чеклист действий
Когда сталкиваюсь с ERROR 2002, прохожу по этому списку:
- Проверяю статус службы:
systemctl status mysql - Если не запущена, стартую:
sudo systemctl start mysql - Смотрю логи при проблемах запуска:
journalctl -xeu mysql - Проверяю наличие сокета:
ls -l /var/run/mysqld/mysqld.sock - Сверяю пути в конфигах сервера и клиента
- Проверяю права:
ls -ld /var/run/mysqld/ - Исправляю владельца при необходимости:
chown mysql:mysql - Пробую TCP-соединение:
mysql -h 127.0.0.1 - Проверяю AppArmor/SELinux
- Перезапускаю службу после изменений
Этот алгоритм решает проблему в 95% случаев. Оставшиеся 5% требуют глубокого анализа специфики окружения.
За годы работы с базами данных понял главное: ERROR 2002 выглядит страшно только для новичка. Понимание механизма Unix-сокетов превращает диагностику в рутину. Систематический подход, внимание к логам и знание трёх основных причин позволяют решать проблему за минуты, а не часы.