2015年10月16日金曜日

MySQLのrootのパスワードを忘れてしまった…やその類似ケースを、mysqldを停止せずに何とかするメモ

MySQLのrootパスワード忘れた、をググると、--skip-grant-tables を有効にして再起動せよ、というのにぶち当たるのが普通なんですが、カジュアルに再起動する訳にいかないことってあるじゃないですか。

そんなときのTIPS。


まず、ダミーのデータディレクトリをmysql_install_dbで作ります。これはrpmで入れた環境なので、/usrはbasedirです。

$ cd /usr
$ bin/mysql_install_db --no-defaults --datadir=/home/mysql/dummy


ここに、パスワードを変えたいMySQLのmysql.userテーブルをコピーします。少なくともMySQL 5.7.8現在、mysql.userはまだMyISAMなのでコピーできます。
rootが使えないはずなのにmysql.userテーブルがゴリゴリ更新されているような環境があるはずはないと信じているので、取り敢えずはシンプルなcpで大丈夫です。

コピーしたらダミーのデータディレクトリを使ってMySQLを起動します。--socketオプションを忘れると本番のソケットファイルが握りつぶされる可能性があるので注意。。

$ cp -ip /var/lib/mysql/mysql/user.MYD /home/mysql/dummy/mysql/user2.MYD
$ cp -ip /var/lib/mysql/mysql/user.MYI /home/mysql/dummy/mysql/user2.MYI
$ cp -ip /var/lib/mysql/mysql/user.frm /home/mysql/dummy/mysql/user2.frm
$ bin/mysqld_safe --no-defaults --datadir=/home/mysql/dummy --port=23000 --socket=/home/mysql/dummy/mysql.sock &
$ bin/mysql -uroot -S/home/mysql/dummy/mysql.sock

mysql> SELECT user, host, password FROM mysql.user2;
+-------------+-----------------+-------------------------------------------+
| user        | host            | password                                  |
+-------------+-----------------+-------------------------------------------+
| root        | localhost       | *........................................ |
| xxxxxxxxx   | localhost       | *........................................ |
| root        | 127.0.0.1       | *........................................ |
| xxxxxxxx    | xxx.xxx.xxx.%   | *........................................ |
| xxxxxxxxxxx | localhost       | *........................................ |
| xxxx        | localhost       | *........................................ |
| xxxxx       | xxx.xxx.xxx.%   | *........................................ |
| xxxxx       | xxx.xxx.xxx.xxx | *........................................ |
+-------------+-----------------+-------------------------------------------+
8 rows in set (0.00 sec)


この通りコピーできたので、UPDATEステートメントでパスワードを書き換えます。

mysql> UPDATE mysql.user2 SET password= PASSWORD('Do_you_love_Perl6?') WHERE (user, host)= ('root', 'localhost');


更新できたらダミー側のMySQLを落とし、落とせない方のMySQLのmysql.userテーブルを上書きします(といってもバックアップは取っておいた方が良かろうかと)


$ bin/mysqladmin -uroot -S/home/mysql/dummy/mysql.sock shutdown
$ cp -ip /home/mysql/dummy/mysql/user2.MYD /var/lib/mysql/mysql/user.MYD
$ cp -ip /home/mysql/dummy/mysql/user2.MYI /var/lib/mysql/mysql/user.MYI

さてこの状態だと、「mysql.userテーブルにUPDATEはかかっているけどFLUSH PRIVILEGESが必要な状態」であり、「でもFLUSH PRIVILEGESするためのrootに接続できない」状態になるかと思います。
そこでkill -HUPですよ奥さん。


$ pkill -HUP mysqld
$ pkill -HUP mysqld

SIGHUPを受け取ると このへんのコード を通って、"FLUSH PRIVILEGES"や"FLUSH HOSTS", "FLUSH LOGS"その他と同じ処理が流れるので、MySQL上のアカウントにアクセスできなくても無事"FLUSH PRIVILEGES"できる。

…コード上通ってそうなのに、何故か2回SIGHUP送らないと反映されない(5.1でも5.6でも5.7でも…なんだろう。同じif文の中のmysql_print_statusはちゃんと呼ばれてエラーログにデバッグ情報吐いてるのに。。)ので取り敢えず2回やっている。深く考えてはいない。


この手段を一番よく使うのはアレですね。
ソケットファイルが何故か消えてて、--skip-name-resolveでroot@localhostはあるけどroot@127.0.0.1がないから--protocol=tcpで逃げられない時に、root@127.0.0.1を作るのに使います。。


【2015/10/16 15:43】



その通りでした! ありがとうございます!!
https://github.com/mysql/mysql-server/blob/5.6/sql/sql_reload.cc#L56-L69

0 件のコメント :

コメントを投稿