ARTICLE DETAIL

资讯详情

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

MySQL ERROR 1045 排查指南:从 Access denied 到 root 密码重置

MySQL ERROR 1045 排查指南:从 Access denied 到 root 密码重置 我第一次在服务器上敲mysql -u root准备登录时等到的不是熟悉的mysql提示符而是一行红字ERROR 1045 (28000): Access denied for user rootlocalhost (using password: NO)当时脑子里的第一反应是我明明还没设过密码怎么就被拒了后来跟 MySQL 打交道久了才明白这条ERROR 1045几乎是每个新手都会撞上的第一道坎但报错背后的真实原因远不止“密码输错”这么简单。这篇不绕弯子直接把这条报错拆开讲清楚。你会看到报错里每个字段到底什么意思、哪些常见场景会触发它、怎么一步步定位 root 用户的真实状态、几种最可靠的密码重置方案以及我见过的一堆翻车操作。无论你是刚装完 MySQL 的初学者还是被生产环境 1045 搞到半夜的运维这篇文章都可以直接照着操作。1. 报错在说什么把 ERROR 1045 的每个字段都拆开看1.1 Access denied 并不可怕先看匹配规则ERROR 1045 (28000)里的 1045 是 MySQL 错误码28000 是 SQLSTATE 值含义就是“访问被拒绝”。MySQL 在处理连接请求时会拿客户端提供的用户名 来源主机去mysql.user表里找对应的账号记录找到之后再校验密码。这里有一个很多新手不知道的点用户名和来源主机是绑定在一起来匹配的。报错里写的user rootlocalhost指的是客户端请求的账号是root来源是localhost。MySQL 会在权限表里找完全匹配rootlocalhost的记录而不是“只要用户名是 root 就行”。如果权限表里只有root127.0.0.1那你连 localhost 的时候同样会被拒绝报错文案也可能是这一条。另外MySQL 在“账号不存在”和“密码错误”这两种情况下返回的报错几乎是同一个并不会明确告诉你到底是哪一种。这是出于安全考虑防止外部探测有效账号。所以不要一看到 Access denied 就认定是密码错账号匹配关系同样值得检查。1.2 using password: NO 才是主线报错末尾的(using password: NO)是解决这个问题的关键信息。它表示客户端在发起认证请求时没有携带任何密码字段。服务器收到了一个空密码就拿它去校验rootlocalhost的认证规则。如果rootlocalhost这个账号本身设置了密码那你没带密码过来自然被拒如果账号密码本来就是空的但又用了某种不允许空密码直连的认证插件同样会被拒。所以这条报错不一定等于“密码忘了”有可能是“你根本没输入密码”。很多人会在这里犯迷糊为什么我明明在可视化工具里填了密码命令行一敲就出using password: NO因为命令行状态下的mysql客户端默认不带密码如果你不写-p参数它默认走无密码认证流程密码字段就是空的。这在后面排查时是很重要的一条线索。1.3 连接路径会影响来源主机再补充一个安全但常见的坑mysql -u root不带-h参数时Linux 下默认走 Unix socket 连接服务端看到的来源主机是localhostWindows 下默认走命名管道也是 localhost。但如果你在工具里写的是127.0.0.1那走的是 TCP 连接来源主机可能是127.0.0.1或::1取决于解析结果。这就是为什么有时候命令行能登录但从 Navicat、Workbench 连却报Access denied。你压根没连到同一个账号上。后面排查时第一步不是急着改密码而是先确认你到底走的是哪个通道、匹配的是哪条user host记录。2. 六个高频现场为什么你会在这些时刻撞上 10452.1 现场一MySQL 5.7/8.0 安装时生成的临时密码没找到MySQL 5.7 之后的版本初始化数据目录时会自动生成一个临时密码而不是让 root 默认为空。这个密码通常写在日志文件里比如grep temporary password /var/log/mysqld.log不同发行版日志路径不一样也可能是/var/log/mysql/error.log。很多人照着老教程执行完mysqld --initialize后直接敲mysql -u root结果就是using password: NO被拒。正确做法是去日志里找临时密码然后执行mysql -u root -p输入临时密码进入后第一件事就是设置新密码。2.2 现场二Ubuntu/Debian 默认 root 走 auth_socket在 Ubuntu 上用apt install mysql-server装完 MySQL默认情况下 root 账号的认证插件是auth_socket。这个插件不看密码而是检查当前操作系统用户是不是 root或特定用户。所以你执行sudo mysql -u root能直接进去但执行mysql -u root -p反而会被拒因为该账号本身就不该用密码登录。很多新手教程没讲这层导致用户以为“MySQL 密码错误”实际上根本不该用密码。要改成传统密码登录得通过sudo mysql -u root进入后执行ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY 你的新密码;2.3 现场三配置文件里藏着一个错误的默认密码客户端工具并不是只看命令行参数还会读取配置文件。常见的有/etc/my.cnf、/etc/mysql/my.cnf、~/.my.cnf。如果配置文件的[client]段里写了[client] userroot password123456那么哪怕你命令行只敲mysql -u root客户端也会自动携带123456去认证。如果实际密码不是这个就会收到Access denied。这种坑最难受的地方在于报错可能显示using password: YES但也可能因为其他覆盖逻辑变成NO。排查时一定要记得看一眼配置文件里有没有“好心”帮它填密码的地方。2.4 现场四客户端太老和 MySQL 8 的新认证插件不兼容MySQL 8.0 默认认证插件是caching_sha2_password而很多老版本的客户端和图形工具用的还是mysql_native_password。两者在握手阶段就会出问题表现常常就是ERROR 1045 (28000): Access denied for user rootlocalhost。尤其是有些破解版 Navicat、老版本 JDBC 驱动都容易卡在这。解决办法有两个方向要么升级客户端驱动到支持caching_sha2_password的版本要么把账号认证方式改回老插件ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的新密码;顺带提醒这属于兼容性妥协生产环境尽量升级客户端而不是长期用老认证插件。2.5 现场五Workbench、Navicat 连的时候 Host 写错导致账号不匹配很多图形工具默认 Host 是127.0.0.1但 MySQL 里只存在rootlocalhost这条账号记录。如果你在mysql.user表里没有root127.0.0.1或root%连接时就会匹配失败。这种情况的判定方法是命令行用mysql -u root -p走 socket匹配 localhost能成功但工具里填127.0.0.1却 1045。解决方式是创建对应 host 的账号或者在工具里用localhost再不行就明确指定 socket 路径。先分清连接路径再决定改哪边。2.6 现场六密码正确仍然被拒因为账号被锁或密码过期mysql.user表里还有两个字段会影响连接account_locked和password_expired。如果账号被锁即使密码完全正确也会被拒如果密码过期服务端会要求你先重置密码连接同样失败。可以用下面这条 SQL 检查SELECT user, host, account_locked, password_expired, plugin FROM mysql.user WHERE userroot;如果发现account_locked Y解锁ALTER USER rootlocalhost ACCOUNT UNLOCK;如果password_expired Y重置一次密码ALTER USER rootlocalhost IDENTIFIED BY 新的密码;3. 定位问题先搞清楚 root 用户和 MySQL 实例的真实状态3.1 确认你连的是不是你以为的那个 MySQL排查 1045 最容易犯的一个错误就是还没确认当前系统里运行着几个 MySQL就直接开始重置密码。结果改的是 A 实例你连的却是 B 实例当然一直报错。先看版本和进程mysql --version ps aux | grep mysqld再确认端口监听情况ss -lntp | grep 3306如果 3306 没监听可能是服务没启动或者启动时用了其他端口。如果你装过多个版本很容易出现新旧进程抢端口、socket 路径不一致的情况。建议把所有和 mysql 相关的进程列出来找到你真正想连的那一个。3.2 用系统级通道先进入 MySQL看一眼 user 表在 Ubuntu/Debian 上最直接的验证方式是用系统管理权限进入 MySQLsudo mysql -u root如果进去之后能看到mysql提示符说明服务本身没问题root 账号状态也还活着。接下来执行SELECT user, host, plugin, authentication_string, password_expired, account_locked FROM mysql.user WHERE userroot;这一眼就能看出 root 有没有设置密码、用的什么认证插件、是否被锁。注意authentication_string里存的是哈希值不是明文密码不要直接拿它去比对“我密码是不是这个”。如果你的系统不是 Debian 系sudo mysql进不去就进入下一步用安全模式来排查。3.3 检查 my.cnf / my.ini 里藏着的连接参数MySQL 的配置加载顺序比较复杂常见路径包括/etc/my.cnf、/etc/mysql/my.cnf、/etc/mysql/mysql.conf.d/、~/.my.cnf。想确认实际生效的配置可以执行mysqld --verbose --help | grep -A 1 Default options重点看以下几个配置项[mysqld]段下的skip-grant-tables如果被开启所有密码校验都会失效此时任何密码都能进去但业务上会有巨大安全隐患。[mysqld]段下的skip-networking如果开启远程 TCP 连接会被禁掉本地工具连不上。[client]段下的user、password客户端默认连接的账号密码。default-authentication-pluginMySQL 8 里可以指定默认认证插件。很多时候 1045 不是密码本身的问题而是某个配置文件悄悄改变了认证行为。全部检查一遍能省去很多后续瞎折腾。3.4 开启通用日志看连接请求到底发到了哪里如果上面的步骤都还定位不了可以临时打开 MySQL 通用日志把连接过程完整记录下来。在 MySQL 里执行SET GLOBAL general_log ON; SET GLOBAL log_output TABLE;然后重新发起一次连不上的操作再查询SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 5;通过日志能看到客户端实际连接的用户名、来源主机、连接方式等信息。排查完记得关掉SET GLOBAL general_log OFF;不然日志会快速增长占满磁盘。4. 可靠重置 root 密码的三条路径和完整命令4.1 路径一skip-grant-tables 模式适用所有发行版这是最通用、也最常被提到的重置方式。思路是让 MySQL 暂时跳过权限校验进去后用 SQL 修改密码。第一步停止 MySQL 服务sudo systemctl stop mysql不同发行版服务名可能是mysqld或mysql按实际来。第二步修改配置文件在[mysqld]段下加一行[mysqld] skip-grant-tables第三步启动服务sudo systemctl start mysql此时执行mysql -u root应该可以直接进去。但注意在这个模式下直接执行ALTER USER会报错提示服务器运行在--skip-grant-tables模式下无法执行该语句。所以要先刷新授权表FLUSH PRIVILEGES;然后再重置密码ALTER USER rootlocalhost IDENTIFIED BY 你的新密码;完事之后删除配置文件里加的skip-grant-tables行再重启 MySQL 服务。最后验证mysql -u root -p这里额外说一句很多版本在启用skip-grant-tables时会同时禁用远程网络连接只允许本地 socket 访问。这其实是保护机制防止你在重置密码期间被外部黑进来。别以为是 bug。4.2 路径二init-file 初始化文件适合不喜欢进交互模式的人如果不想在 skip-grant-tables 模式下手工执行 SQL可以提前写一个初始化文件让 MySQL 启动时自动执行。第一步创建文件/tmp/mysql-reset.sqlALTER USER rootlocalhost IDENTIFIED BY 你的新密码;第二步停止服务sudo systemctl stop mysql第三步用--init-file启动sudo mysqld --usermysql --init-file/tmp/mysql-reset.sql 也可以用配置文件方式在[mysqld]段下加init-file/tmp/mysql-reset.sql然后正常启动服务。MySQL 启动后会自动执行文件里的 SQL把密码重置掉。执行完后立刻停止服务删掉 SQL 文件再正常启动。这个方式的好处是不用进交互式命令行适合写脚本自动化处理。注意文件权限mysqld进程需要能读到这个文件否则会启动报错。建议把文件放到/tmp下并确认 mysql 用户有读取权限。4.3 路径三sudo mysql 直连后修改认证方式Ubuntu 用户最省事前面说过Ubuntu 默认 root 走auth_socket所以用sudo mysql -u root就能进去。如果你只是想重新用密码登录就不用搞什么 skip-grant-tables直接改认证方式ALTER USER rootlocalhost IDENTIFIED WITH caching_sha2_password BY 你的新密码;如果想继续用老式客户端也可以改成ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的新密码;改完之后退出再用mysql -u root -p验证。这个方法只适用于 root 账户本身还能被系统用户免密登录的情况其他发行版不一定适用所以放在方案三。4.4 重置后的收尾动作与验证清单密码重置完别急着关终端。先做一轮验证用mysql -u root -p连接确认新密码生效。执行SELECT CURRENT_USER();看当前登录身份是否是你预期的rootlocalhost。再检查一次plugin字段确保认证插件和客户端兼容。重启一次 MySQL 服务确认配置改动不会影响正常启动。如果之前开了general_log记得确认已关闭。最后把密码明文保存到本地的密码管理器里不要随手写在服务器上的文本文件中。5. 这条报错背后的经典翻车点我见过的一堆脑溢血操作5.1 用 UPDATE 直接改 authentication_string把 root 账号改残MySQL 5.7 之前网上一堆教程教人这样改密码UPDATE mysql.user SET authentication_stringPASSWORD(123456) WHERE Userroot;这个操作在 MySQL 8.0 里会直接报错因为PASSWORD()函数已经被移除在 5.7 里就算能执行也容易把账号的认证信息改得混乱有时还会覆盖掉插件标识。老老实实用ALTER USER才是正路。5.2 skip-grant-tables 模式下没先 FLUSH PRIVILEGES 就执行 ALTER USER这个坑我见过太多次。在skip-grant-tables模式下权限系统其实处于“不加载”的状态。此时执行ALTER USERMySQL 会明确告诉你ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement解决办法就一句话先执行FLUSH PRIVILEGES;让权限表重新加载然后就能执行ALTER USER了。很多人卡在这一步以为重置方案行不通其实只是顺序问题。5.3 手动起了一个 mysqld 进程systemctl 启停全乱套有些人图省事直接命令mysqld_safe --skip-grant-tables 把进程拉到后台。等改完密码后想用systemctl restart mysql恢复服务发现端口被占、socket 文件冲突、服务起不来各种诡异现象。原因很简单手动启动的 mysqld 还在跑systemctl 启动的服务自然抢不过。正确做法是改完密码后先杀掉手动起的进程再让 systemd 接管。不要图一时方便后面收拾起来更麻烦。5.4 密码里有特殊字符在命令行里被 shell 吃了如果新密码里包含$、!、、;之类的字符执行命令时要小心 shell 解析。比如mysql -u root -pPss!2024!在某些 shell 里会触发历史替换密码可能被拆得面目全非然后白白多几次 Access denied。更稳的做法是不带密码回车后交互输入或者在 SQL 里写重置语句时用单引号把密码包好。5.5 连错实例同机装了多版本 MySQL端口和 socket 对不上一台机器装多个 MySQL 版本时最容易出现 1045 假象。你以为在重置 3306 端口那个实例的密码实际上 MySQL 客户端默认连接的可能是另一个实例的 socket 文件。检查方式就是前面提到的ss -lntp和ps aux | grep mysqld确认你改的是不是正在监听的那个实例。5.6 改完密码总不生效其实是客户端在用自己的记忆连库命令行验证已经用新密码成功了但 Navicat、DataGrip 这些工具还是反复报 1045。别急着怀疑 MySQL先看看工具里保存的连接参数密码是不是旧密码Host 走的是 localhost 还是 127.0.0.1端口填对没有有些工具会把密码缓存到本地密钥链里改数据库密码后不会自动同步。删除连接配置重新建一次往往比在设置里翻来翻去找“修改密码”入口更快。6. 让 1045 从此不来找你三个值得长期坚持的习惯6.1 业务和日常操作绝不直接用 root这条听起来像正确的废话但很多 1045 问题都源于 root 账号被过度使用代码里连数据库用 root、脚本里连数据库用 root、运维工具连数据库也用 root。一旦有人改了 root 密码或者认证插件变了全线被波及。更合理的做法是给每个应用创建独立账号权限最小化。比如CREATE USER app_userlocalhost IDENTIFIED BY 强密码; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app_userlocalhost;以后即使某个业务账号泄露也不至于让整个实例裸露在外。6.2 把密码策略和过期机制提前定好MySQL 8 的validate_password组件会强制密码复杂度。很多人嫌麻烦就把强度调低结果设置完密码自己都记不住。不如一次到位设定合理复杂度、定期更换周期记到密码管理工具里。避免“密码过期了连接失败”这类 1045 变种。6.3 保留一条系统级救援通道并定期备份授权信息做运维一定要假设密码会丢。比较好的实践是在服务器上保留一个可以通过系统用户免密进入 MySQL 的账号比如 Debian/Ubuntu 的auth_socket方式同时定期备份mysql.user表或者用mysqldump备份整个实例。这样出现 1045 时至少有一条自己能走通的通道可以进场修复。我个人现在会在每台 MySQL 服务器上放一个 root 密码重置脚本内容用的是 init-file 方案。生产环境出 1045 往往是人最慌的时候有个脚本可以直接跑比临时查文档再一步步敲命令靠谱得多。另一个习惯是装完 MySQL 后第一件事不是急着连进去而是先创建一个专用业务账号把 root 的日常使用频率压到最低。做到这两点之后我基本再没在半夜被 1045 叫醒过。
返回列表