MySQL (Can’t connect … through socket) — решение проблемы

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

Итак проблема следующего характера: есть выделенный web сервер с (в моём случае) операционной системой Linux Ubuntu и всё прилегающее естественно, т.е. Apache, PHP и этот злосчастный MySQL. И в один прекрасный момент, СУБД MySQL выдаёт примерно следующее сообщение об ошибке при запуске: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2). В самом конце, в скобочках, код ошибки, который меняется по ходу решения проблемы. Во всяком случае «вначале пути», как правило, это двойка, позже были и 13-ый код и 111-ый и т.д. То, что это локальный сервер можете не смущаться, так было именно в моём случае т.к. СУБД MySQL была установлена на локальный ПК, там же где и web сервер Apache, естественно, что в сообщении может фигурировать и не локальный хост, это не столь важно. Так вот, что мы имеем? Сообщение об ошибке, которое говорит: «Не могу соединиться с локальным MySQL сервером через сокет '/var/run/mysqld/mysqld.sock' (2)«.

При этом файлы логов СУБД либо пусты, либо ничего дельного не сообщают по сути проблемы. Напомню на всякий случай, что лежат они в следующих местах:

  • /var/log/mysql.log
  • /var/log/mysql.err
  • /var/log/mysql/error.log

но это нам не понадобится.

Значит сейчас можно попробовать посмотреть объяснение системной ошибки через утилиту perror набрав в консоли следующее: perror 2

Кстати так же можно почитать информацию и про другие ошибки, которые могут встретиться на пути. В общем давайте идти дальше. Что же делать? Очевидно, что проблема с сокетом, который располагается по следующему адресу: /var/run/mysqld/mysqld.sock
Т.е. MySQL демон не может подключиться к Unix-сокету. А такое может произойти по 3-ём причинам:

  1. Его просто физически нет (не создан);
  2. Кто-то его уже занял;
  3. MySQL пользователь не может получить доступ к сокету по правам доступа.

Для проверки 2-ого варианта можно попробовать узнать, а не занимает ли кто-то этот файл?

sudo lsof /var/run/mysqld/mysqld.sock

В результате можем получить нечто подобное:

COMMAND PID  USER   FD   TYPE  DEVICE     SIZE/OFF NODE NAME
mysqld  1299 mysql  6u   unix  0xf688b840 0t0      7877 /var/run/mysqld/mysqld.sock

сразу видно, кто занял сокет. Или же получаем следующее:

...
lsof: status error on /run/mysqld/mysqld.sock: No such file or directory
...

— т.е. файл просто отсутствует.

В первом случае всё понятно — отключаем приложение, занявшее нужный сокет, либо лезем в конфиг MySQL и ставим другой сокет.
Во втором случае надо создать сокет и/или сделать его доступным для mysql, для этого выполняем следующие действия (описываю построчно):

  1. Создаём директорию, в которой должен лежать файл сокета:
    sudo mkdir /var/run/mysqld

    Она может быть уже создана, тогда получите такое сообщение: «mkdir: cannot create directory `/var/run/mysqld’: File exists;».

  2. Создаём сам сокет командой mkfifo.
    sudo mkfifo /var/run/mysqld/mysqld.sock

    Хотя сокет является тоже файлом, создавать его можно только специальной программой «mkfifo».

  3. Рекурсивно устанавливаем владельцем папки mysql (и всего её содержимого) пользователя mysql:
    sudo chown -R mysql /var/run/mysqld

После чего пытаемся запустить/рестартануть MySQL сервер.

В моём случае на этом всё не закончилось. Я для гарантии и большей проверки решил ещё и ребутнуть весь физический сервер. После чего увидел, что при попытке старта СУБД MySQL файл сокета почему то не создаётся. Сделал вывод, что у пользователя от которого работает сервер MySQL нет прав, другого быть не может. Пошёл в эту директорию и ещё раз сам вручную создал нужный сокет, заново дал необходимые права на папку. После этого последовала 13-ая ошибка, которая говорит, что у MySQL недостаточно прав на сокет, что бы его использовать в своих целях. Решил явно дать общие свободные права для всех так:

chmod 0777 /var/run/mysqld/mysqld.sock

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

И как вы думаете в чём же оказалось дело? В простой и банальной нехватки места на жёстком диске! Да! Кто бы мог подумать. При помощи команд lsdf и du просматриваем свободное/занятое место на hard drive’e вашего сервера баз данных и делаем соответствующие выводы. Скорее всего причина может крыться именно здесь. Лично у меня файлы логов были очень большие. В общем почистил место и всё завелось как прежде. Проблема решена.

Итак давайте подведём итог. Куда смотреть в первую очередь и на, что обращать внимание.

Смотрим на коды ошибок, расшифровываем при помощи утилиты perror.
Следим за сокетом расположенным по адресу: /var/run/mysqld/mysqld.sock.
Если надо то создаём его вручную командой mkfifo /var/run/mysqld/mysqld.sock (от рута естественно)
Поглядываем за правами mysql пользователя, он должен иметь полные права на этот файл. Если требуется, то так же меняем вручную командами chmod и chown.
Можно так же заглянуть в журналы логов MySQL, они тут:

  • /var/log/mysql.log
  • /var/log/mysql.err
  • /var/log/mysql/error.log

Все файлы сокетов можно найти командой find / -type s
Ну и в крайнем случае может понадобится заглянуть в конфигурационный файл СУБД MySQL, он тут: /etc/mysql/my.cnf. Там есть директивы, которые устанавливают файл сокета. Выглядит примерно так:

[mysqld] 
datadir=/usr/local/mysql/data 
socket=/var/lib/mysql/mysql.sock 

[mysql.server] 
user=mysql 
basedir=/usr/local/mysql

...

[client] 
socket=/var/lib/mysql/mysql.sock

Если потребуется, то сокет для MySQL так же можно установить так: mysql --socket=/var/lib/mysql/mysql.sock, что бы лишний раз не лазить в my.cnf.
Для старта/рестарта сервера баз данных используем следующий синтаксис:

sudo /etc/init.d/mysqld start
sudo /etc/init.d/mysqld stop
sudo /etc/init.d/mysqld restart

а не такой:

service mysql start
service mysql stop
service mysql restart

Т.е. используем скрипт инициализации, а не команду service. Это не одно и тоже! Надёжнее так как я рекомендую.
Можно попробовать создать символьную ссылку таким образом: ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock для исключения проблем с правами доступа. Т.е. файл сокета будет храниться во временной папке, но ссылка на него будет из папки mysql.
У пользователя mysql так же должны быть права на папку /tmp (настройки по умолчанию), если не определена другая временная папка.

Надеюсь у вас получилось. Спасибо за внимание.