一直以来, 对于 MySQL root 密码的忘记, 以为只有一种解法 - skip-grant-tables.
问了下群里的大咖, 第一反应也是 skip-grant-tables. 通过搜索引擎简单搜索了下, 无论是百度, 抑或 Google, 只要是用中文搜索, 首页都是这种解法. 可见这种解法在某种程度上已经占据了使用者的心智. 下面具体来看看.
skip-grant-tables 的解法
首先, 关闭实例
这里, 只能通过 kill mysqld 进程的方式.
注意: 不是 mysqld_safe 进程, 也切忌使用 kill -9.
- # ps -ef |grep mysqld
- root 6220 6171 0 08:14 pts/0 00:00:00 /bin/sh bin/mysqld_safe --defaults-file=my.cnf
- mysql 6347 6220 0 08:14 pts/0 00:00:01 /usr/local/mysql57/bin/mysqld --defaults-file=my.cnf --basedir=/usr/local/mysql57 --datadir=/usr/local/mysql57/data --plugin-dir=/usr/local/mysql57/lib/plugin --user=mysql --log-error=slowtech.err --pid-file=slowtech.pid --socket=/usr/local/mysql57/data/mysql.sock --port=3307
- root 6418 6171 0 08:17 pts/0 00:00:00 grep --color=auto mysqld
- # kill 6347
使用 --skip-grant-tables 参数, 重启实例
# bin/mysqld_safe --defaults-file=my.cnf --skip-grant-tables --skip-networking &
设置了该参数, 则实例在启动过程中会跳过权限表的加载, 这就意味着任何用户都能登录进来, 并进行任何操作, 相当不安全.
建议同时添加 --skip-networking 参数. 其会让实例关闭监听端口, 自然也就无法建立 TCP 连接, 而只能通过本地 socket 进行连接.
MySQL8.0 就是这么做的, 在设置了 --skip-grant-tables 参数的同时会自动开启 --skip-networking.
修改密码
- # mysql -S /usr/local/mysql57/data/mysql.sock
- mysql> update mysql.user set authentication_string=password('123456') where host='localhost' and user='root';
- Query OK, 0 rows affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 1
- mysql> flush privileges;
- Query OK, 0 rows affected (0.00 sec)
注意:
这里的 update 语句针对的是 MySQL 5.7 的操作, 如果是在 5.6 版本, 修改的应该是 password 字段, 而不是 authentication_string.
update mysql.user set password=password('123456') where host='localhost' and user='root';
而在 MySQL 8.0.11 版本中, 这种方式基本不可行, 因为其已移除了 PASSWORD() 函数及不再支持 SET PASSWORD ... = PASSWORD ('auth_string') 语法.
不难发现, 这种方式的可移植性实在太差, 三个不同的版本, 就先后经历了列名的改变, 及命令的不可用.
下面, 介绍另外一种更通用的做法, 还是在 skip-grant-tables 的基础上.
与上面不同的是, 其会先通过 flush privileges 操作触发权限表的加载, 再使用 alter user 语句修改 root 用户的密码, 如:
- # bin/mysql -S /usr/local/mysql57/data/mysql.sock
- mysql> alter user 'root'@'localhost' identified by '123';
- ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement
- mysql> flush privileges;
- Query OK, 0 rows affected (0.00 sec)
- mysql> alter user 'root'@'localhost' identified by '123';
- Query OK, 0 rows affected (0.00 sec)
免密码登录进来后, 直接执行 alter user 操作是不行的, 因为此时的权限表还没加载. 可先通过 flush privileges 操作触发权限表的加载, 再执行 alter user 操作.
需要注意的是, 通过 alter user 修改密码只适用于 MySQL5.7 和 8.0, 如果是 MySQL 5.6, 此处可写成
update mysql.user set password=password('123456') where host='localhost' and user='root';
最后重启实例
- mysql> shutdown;
- # bin/mysqld_safe --defaults-file=my.cnf &
需要注意的是, 如果在启动的过程中没有指定 --skip-networking 参数, 无需重启实例. 但在网上看到的绝大多数方案, 都是没有指定该参数, 但重启了实例, 实在没有必要.
下面对这个方案做个总结:
1. 如果只添加了 --skip-grant-tables, 修改完密码后, 其实无需重启, 执行 flush privileges 即可.
2. 从安全角度出发, 建议加上 --skip-networking. 但因其是静态参数, 将其剔除掉需要重启实例.
3. 加上 --skip-networking, 虽然可以屏蔽掉 TCP 连接, 但对于本地其它用户, 只要有 socket 文件的可读权限, 都能无密码登录. 还是存在安全隐患.
4. 不建议通过 update 的方式修改密码, 更通用的其实是 alter user.
更优雅的解法
相对于 skip-grant-tables 方案, 我们来看看另外一种更优雅的解法, 其只会重启一次, 且基本上不存在安全隐患.
首先, 依旧是关闭实例
其次, 创建一个 sql 文件
写上密码修改语句
- # vim init.sql
- alter user 'root'@'localhost' identified by '123456';
最后, 使用 --init-file 参数, 启动实例
# bin/mysqld_safe --defaults-file=my.cnf --init-file=/usr/local/mysql57/init.sql &
实例启动成功后, 密码即修改完毕~
如果 mysql 实例是通过服务脚本来管理的, 除了创建 sql 文件, 整个操作可简化为一步.
# service mysqld restart --init-file=/usr/local/mysql57/init.sql
注意: 该操作只适用于 / etc/init.d/mysqld 这种服务管理方式, 不适用于 RHEL 7 新推出的 systemd.
Linux 公社的 RSS 地址: https://www.linuxidc.com/rssFeed.aspx
来源: http://www.linuxidc.com/Linux/2018-07/153093.htm