ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL root密码忘了?5.7/8.0重置方法与坑点详解

MySQL root密码忘了?5.7/8.0重置方法与坑点详解 MySQL的root密码忘了这事儿搁谁都糟心。尤其正在线上跑业务突然收到一行ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)第一反应多半是“完了要不要重装数据库”。我干数据库运维这些年处理过几十次类似的场景可以负责任地说MySQL 5.7和8.0都有稳妥的恢复路径不丢数据、不改业务十来分钟就能把root密码重置拿回来。这篇内容不挑基础从原理到每一步命令都会讲清楚新手能照着抄老手也能补充一些平时不太容易遇到的细节。核心就一个词冷静。先别急着动手。很多事故扩大化都是因为没确认环境就敲命令。1. 搞清楚两件事你忘的是哪种密码5.7和8.0差在哪1.1 先确认版本和登录场景重置密码之前第一步一定是确认MySQL版本。最直接的方式mysql --version如果还能用某个普通账号登录哪怕权限很低也可以查SELECT VERSION();为什么版本这么关键因为5.7和8.0在密码存储、认证插件、SQL语法上差异很明显。网上大量教程混着写照抄特别容易踩坑。举个最常见的例子UPDATE mysql.user SET authentication_stringPASSWORD(xxx)这套老操作在5.7里勉强能用虽然官方已经废弃在8.0里PASSWORD()函数直接被移除了执行就是报错。不是你操作不对是版本不对路。还得确认登录场景。你到底是“完全进不去”还是“能进但不知道密码”比如Ubuntu/Debian系统通过apt装的MySQLroot用户默认不是密码认证而是走auth_socket插件直接sudo mysql就能登进去根本没有“忘记密码”这回事。这类细节非常容易让人白忙活后面我会单独讲。1.2 5.7和8.0在密码机制上的三个关键差异我先把最影响操作的三个差异列出来后面所有步骤都围绕它们展开认证插件不同5.7默认是mysql_native_password兼容老客户端8.0默认改成caching_sha2_password安全性更高但老驱动和旧版Navicat等客户端可能不支持。密码函数命运不同PASSWORD()函数在5.7里标记废弃在8.0里彻底删除。所以重置密码要统一走ALTER USER语法别再用旧函数。权限表的host匹配rootlocalhost和root%是两条独立记录。你改了其中一个host另一个照样登录不上还会报1045。很多人就是栽在这里。还有一个容易被忽略的点密码复杂度策略。8.0把validate_password从插件升级成了组件变量名都变了——5.7是validate_password_policy8.0是validate_password.policy少个点都会报错。后面在1819报错里我会细说。2. 重置密码的两条路线skip-grant-tables和init-file2.1 路线A跳过授权表启动这是最广为人知的方案。原理一句话mysqld启动时跳过授权表的加载整个服务以“不校验权限”的模式运行。你不需要密码就能mysql -uroot直接进。听起来方便风险也大。因为在这个模式下任何能连上数据库的人都是“超级管理员”所以必须搭配--skip-networking禁用远程TCP连接只留本机socket把风险压到最低。操作完必须立刻恢复配置并重启不能留着过夜。这条路线的优点是通用5.7和8.0通吃也是很多线上环境唯一能用上的方案。缺点是步骤多一点而且8.0里有个微妙问题跳过授权表模式下直接执行ALTER USER可能会失败需要先执行FLUSH PRIVILEGES把授权表重新加载回内存再改密码。2.2 路线Binit-file启动MySQL官方其实更推荐用init-file方式尤其8.0。原理是MySQL启动时可以指定一个SQL文件服务启动过程中自动执行里面的语句。我们要做的就是把ALTER USER ...写进这个文件让密码在启动瞬间被修改。mysqld --init-file/tmp/mysql-init.txt或者在/etc/my.cnf的[mysqld]段加一行init_file /tmp/mysql-init.txt这个方案最大的好处是全程不会进入“无授权表”的危险状态改动范围极小安全性更高。缺点是文件里的SQL必须写得绝对正确否则服务启动了但密码没改成你还得看日志排查。2.3 为什么非要多写一个FLUSH PRIVILEGES这个点是很多教程没讲透的。我用生活化的方式解释一下正常启动MySQL授权表会加载到内存里权限校验随时生效。但skip-grant-tables模式下MySQL是“跳过”加载授权表的此时你虽然能自由操作但权限系统处于半瘫痪状态。这时候直接执行ALTER USER有些版本会报权限不足有些版本会“假成功”然后重启后密码没变。解决办法就是先执行FLUSH PRIVILEGES;这条命令会强制MySQL把授权表重新加载回内存让权限校验恢复。然后再执行ALTER USER基本就能一次成功。我处理过不少环境凡是“按教程操作但改不成功”的绝大多数是少了这一句。先flush再alter能省非常多时间。3. MySQL 5.7 重置root密码完整操作3.1 标准步骤五步解决5.7的流程很成熟按顺序走基本不会出问题。第一步停掉数据库服务systemctl stop mysqld如果服务名是mysql就执行systemctl stop mysql。老一点的init.d环境用service mysql stop第二步在配置文件里加启动参数编辑/etc/my.cnf或/etc/mysql/my.cnf在[mysqld]段下面加两行[mysqld] skip-grant-tables skip-networking这里有个经验之谈很多人只加skip-grant-tables不加skip-networking结果MySQL在0.0.0.0:3306上监听任何能ping到这台机器的客户端都能无密码登录。这在生产环境是重大事故不是小事。两个参数一起加数据库只接受本地socket连接风险直接压制到本机。第三步启动并免密登录systemctl start mysqld mysql -uroot正常情况下看到mysql提示符就说明已经进去了。第四步刷新授权表再改密码FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY 你的新密码;这是最推荐的方式。如果你用的是比较老的5.7版本或者ALTER USER报奇怪的错也可以用老写法UPDATE mysql.user SET authentication_stringPASSWORD(你的新密码) WHERE Userroot; FLUSH PRIVILEGES;注意5.7的mysql.user表里已经没有password字段了用的是authentication_string。这一点和早期的MySQL 5.1、5.5不一样写SQL时别按老印象来。第五步恢复配置并重启验证改完密码后把/etc/my.cnf里刚加的skip-grant-tables和skip-networking两行注释掉或删掉然后systemctl restart mysqld mysql -uroot -p输入新密码能进去就收工了。如果不放心再执行SELECT user,host,plugin FROM mysql.user WHERE userroot;看下当前root的认证方式确认是自己预期的状态。3.2 特殊场景系统root用户用的是auth_socket插件装过Ubuntu官方MySQL的人应该遇到过这种情况明明没设置过root密码但mysql -uroot -p怎么都进不去报1045。其实数据库根本没密码是认证插件不是密码认证而是auth_socket。这时候不需要走上面的整套步骤直接sudo mysql进入后执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的新密码;这条SQL的意思是把认证方式从auth_socket改成mysql_native_password并把密码设成指定值。改完之后以后就能用密码登录了不再依赖系统用户身份。这个场景确认起来也简单如果sudo mysql能进但mysql -uroot -p提示access denied十有八九就是auth_socket。别急着去改配置文件先确认插件能省一大圈事。4. MySQL 8.0 重置root密码完整操作4.1 方法一skip-grant-tables FLUSH ALTER USER8.0没有5.7那么好说话坑主要在三个地方PASSWORD()函数不能用了密码复杂度组件默认可能挡你的新密码ALTER USER报1396的概率比5.7高。完整步骤第一步停服务、加配置systemctl stop mysqld编辑/etc/my.cnf加上[mysqld] skip-grant-tables skip-networking然后启动systemctl start mysqld mysql -uroot第二步先flush再ALTER USER进入后按顺序执行FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY 强密码;这里强烈建议直接用一个满足复杂度要求的密码比如大小写字母数字特殊字符至少8位。如果你非要设简单密码就会碰到ERROR 1819 (HY000): Your password does not satisfy the current policy requirements报这个错不用慌两种处理方式。想临时降低策略的话SET GLOBAL validate_password.policy LOW;改完密码后再把策略恢复成原来的MEDIUM或STRONG。注意8.0里变量名是validate_password.policy带点不是5.7的validate_password_policy。第三步清理配置并重启注释掉skip-grant-tables和skip-networking重启systemctl restart mysqld mysql -uroot -p这个方法在8.0上我用过很多次核心就是先FLUSH再ALTER。如果还是报1396别怀疑去查一下mysql.user表里root的host到底是什么后面章节我会详细说。4.2 方法二init-file一次搞定这个方法更干净。先创建一个SQL文件比如/tmp/mysql-init.txtALTER USER rootlocalhost IDENTIFIED BY 你的新密码;然后停掉MySQL临时指定init_file启动systemctl stop mysqld mysqld --init-file/tmp/mysql-init.txt 如果是systemd管理也可以临时在/etc/my.cnf里加[mysqld] init_file /tmp/mysql-init.txt再启动服务。启动完成后MySQL会自动执行文件里的SQL把密码改掉。然后立刻做两件事删除SQL文件、删除或注释掉配置文件里的init_file行。文件本身的权限也值得注意。我习惯chmod 600 /tmp/mysql-init.txt因为里面是明文密码权限太松容易泄露。另外SQL书写要严格一条语句一行不要有额外空格、注释或者BOM头。文件里语句执行失败时MySQL不会回滚服务会正常启动只是密码没改成功这时候去查error log才有线索。4.3 重置后如果老客户端连不上怎么办8.0默认认证插件是caching_sha2_password你就算密码改对了用老版本的Navicat、PHP 5.x的mysqli扩展连接可能直接报Authentication plugin caching_sha2_password cannot be loaded这不是密码错是客户端不支持新插件。临时解决方案是把root的认证方式改回老插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的新密码;说完这个我在生产环境里的建议是除非兼容老旧业务否则尽量升级客户端而不是降级认证插件。毕竟caching_sha2_password更安全没必要为了省事牺牲安全性。但应急恢复时这条命令能救命。5. 常见报错速查表与救急经验5.1 高频报错对照表我把实际操作中高频出现的报错整理成一张表排查的时候直接对着看能少走很多弯路。报错信息原因解决思路ERROR 1045 (28000): Access denied for user rootlocalhost (using password: YES)密码错误、host不匹配或用户不存在确认mysql.user中的host列用skip-grant-tables重置查SELECT user,host FROM mysql.userERROR 1396: Operation ALTER USER failed for rootlocalhost指定的host与表中记录不一致或用户不存在查mysql.user表按真实host写成root%或root127.0.0.1ERROR 1819: Your password does not satisfy the current policy requirementsvalidate_password组件启用了复杂度策略用更复杂的密码或SET GLOBAL validate_password.policyLOW改完再恢复ERROR 2002 (HY000): Cant connect to local MySQL server through socket服务没启动或socket文件路径不对检查systemctl status mysqld确认socket文件位置/tmp/mysql.sock或/var/run/mysqld/mysqld.sockYour password has expired密码过期策略生效登录后不能执行任何命令执行ALTER USER rootlocalhost IDENTIFIED BY 新密码;或SET PASSWORD 新密码Authentication plugin caching_sha2_password cannot be loaded老客户端不识别8.0新认证插件临时改为mysql_native_password或升级客户端驱动这表格里有几个被问烂的问题我再单独拿出来说两句。关于1396我在帮人排查时发现绝大多数情况不是权限问题是host写错了。你登录时系统默认用rootlocalhost但mysql.user里存的可能是root%也可能是root127.0.0.1。执行ALTER USER时一定要先查SELECT user, host, plugin FROM mysql.user WHERE userroot;看到什么host就写什么host。比如表里是root%那就执行ALTER USER root% IDENTIFIED BY ...。关于2002还有一个隐蔽场景skip-grant-tables模式下mysqld可能生成一个非默认路径的socket文件导致mysql -uroot找不到。这时候可以手动指定socket路径mysql -uroot --socket/var/run/mysqld/mysqld.sock或者干脆在my.cnf里检查socket配置。5.2 三条救急经验第一操作前把数据目录做个文件级备份。重置密码不动业务数据但万一执行了糊涂SQL比如误删了mysql.user整张表那才叫麻烦。稳妥做法是停服状态下备份整个数据目录cp -a /var/lib/mysql /var/lib/mysql_bak_$(date %F)备份完再去操作心理负担小很多。如果没有停机窗口可以只备份mysql库目录但要注意一致性。第二改完密码后清理shell历史。很多人习惯直接在命令行写mysql -uroot -p你的新密码这样bash history里就留下了明文密码生产环境这是忌讳。我的习惯是export HISTCONTROLignorespace mysql -uroot -p命令前加一个空格配合HISTCONTROLignorespace这条命令就不会进历史记录。改完密码后用history | tail检查一下确认没有泄露。第三重置后检查my.cnf里有没有残留配置。我遇到过一个诡异现象密码明明改成功了重启后还是免密登录。排查半天才发现之前手动加过skip-grant-tables但注释不干净重启又生效了。所以收尾时执行grep -n skip\|init-file\|init_file /etc/my.cnf确认两行危险配置都清理干净再收工。Windows环境也有类似问题如果是用命令行mysqld --skip-grant-tables手动启动的记得先正常关闭窗口再重新启动服务否则配置会一直带在内存里。这个活儿说白了就是四句话认清版本、二选一路线、先flush再alter、完事立刻恢复配置。把这四句话记牢绝大多数环境都能拖回来。我自己的习惯是每次处理完这类事故都会顺手把rootlocalhost的认证方式统一成自己可控的状态然后用mysql_config_editor把登录凭据存好避免下次再手忙脚乱。你第一次操作也别慌按上面的顺序一步步来先在一台测试机或者虚拟机里练一遍再动生产环境。毕竟生产环境每多敲一个没把握的命令都是给自己挖坑。
返回列表