ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MySQL修改密码四种方法:ALTER USER与忘记root重置

MySQL修改密码四种方法:ALTER USER与忘记root重置 1. 被密码挡在门外的那些时刻刚装完 MySQL兴冲冲打开命令行准备大干一场结果Access denied for user rootlocalhost一行红字直接把你拍在门外——这个场景我见过太多次了。新手装 MySQL 最容易卡住的不是 SQL 语法恰恰是我密码到底是什么当初设的密码忘了怎么办怎么把密码换成好记的。这一篇就专门聊透一件事修改 MySQL 密码的四种方法从命令行到图形化工具全部走一遍并对每种方法的适用场景、坑点和风险说清楚。MySQL 密码管理这件事看似简单实际暗藏不少门道。你用的是 Windows 还是 Linux装的是 5.7 还是 8.0有没有 root 权限MySQL 服务还能不能连上这些条件不同能用的方法就完全不一样。有人照着网上教程一顿操作结果ALTER USER报语法错有人改了密码却发现服务起不来还有人忘了 root 密码直接重装数据库——这几种翻车我都亲身经历过。四种方法分别是ALTER USER语句法、SET PASSWORD语句法、mysqladmin命令行工具法、以及忘记密码时的--skip-grant-tables跳过权限表法。前两种是能登进去时的正规改法第三种适合脚本运维第四种是彻底进不去时的救急手段。每一节我都会交代清楚命令怎么敲、为什么这么敲、以及敲完之后怎么验证适合完全没有数据库基础的人一步一步跟着做。注意全文默认你具备基本的命令行操作能力知道怎么打开终端或 CMD能用cd切换目录不涉及任何数据备份之外的高阶概念。如果你连 MySQL 都还没装好建议先把安装配置跑通再回来改密码不然容易把两个问题搅在一起排查起来更痛苦。2. 动手之前必须搞清楚的几件事2.1 先确认你的 MySQL 版本和登录状态版本不同命令写法差异极大这是新手最容易忽略的前置条件。查版本的方式很简单能登进去的时候执行SELECT VERSION();登不进去的话直接用命令行mysql --version输出类似mysql Ver 8.0.35 for Linux on x86_64就说明是 8.0 系列。为什么要在意版本因为 MySQL 8.0 已经彻底移除了PASSWORD()函数SET PASSWORD PASSWORD(xxx)这种老写法在 8.0 上会直接报语法错误或者返回 NULL而网上大量老教程还在用这种写法照抄必翻车。登录状态同样关键。能用当前密码登进去走语句法最省事登不进去但记得密码只是记错了用户名那要先确认用户是否存在连用户名都忘了那基本只能走最后的跳过权限表方案。我一般建议先用mysql -u root -p试一下能进就直接在会话里改进不去再往下看。2.2 密码策略这道坎为什么你设的简单密码被拒绝MySQL 8.0 默认带了一个validate_password组件密码强度不够会被直接拒绝报错信息类似ERROR 1819 (HY000): Your password does not satisfy the current policy requirements这是很多人第一次改密码时最懵的地方——明明命令写对了为什么就是不让改因为默认策略要求密码至少 8 位且包含大小写字母、数字和特殊字符。想看当前策略执行SHOW VARIABLES LIKE validate_password%;如果只是本地学习环境完全没必要被这个策略折磨可以临时降到最低强度SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;注意validate_password.policy这个变量名在 MySQL 8.0.3 之后才改成带下划线的形式之前的版本是validate_password_policy写错一个下划线就会报Unknown system variable。生产环境不要图省事降策略这是安全底线。2.3 修改密码会不会影响正在运行的业务这是运维场景下最需要提前想清楚的问题。MySQL 的密码修改是即时生效的改完之后所有用旧密码建立的连接不会立刻断开但新发起的连接必须用新密码。如果你的应用配置文件里写的是旧密码那么连接池里的旧连接耗尽之后应用就会开始大面积报连接失败。正确做法是按顺序来先改密码立刻更新应用的连接配置并重启应用或者提前把新旧密码都配置好再灰度切换。绝对不要在大流量时段裸改 root 密码。我自己踩过一次——半夜改完密码忘了同步改 Spring Boot 的application.yml第二天早上服务直接连不上数据库排查了半小时才想起来是密码的锅。2.4 改密码之前先做一次备份这句话不是废话修改密码本身不涉及数据变更理论上不会损坏数据。但跳过权限表那种救急方案会要求重启 MySQL 服务重启过程中如果遇到意外比如磁盘满、配置文件被锁有备份就多一层保障。备份命令mysqldump -u root -p --all-databases backup_$(date %F).sql对于学习环境这条可以跳过但只要数据库里有你还想留着的用户数据养成改配置前备份的习惯绝对值得。改密码这种小操作都翻车的案例我在论坛上见过不只一次。3. 方法一ALTER USER 语句法首选方案3.1 命令怎么写、为什么这么写这是 MySQL 官方推荐的密码修改方式从 5.7 开始就是主流写法语法干净、意图明确ALTER USER rootlocalhost IDENTIFIED BY NewPass_2024!;拆开看这段命令的几个部分。rootlocalhost是完整的用户标识MySQL 里用户是由用户名 主机共同确定的rootlocalhost和root%是两个完全不同的用户只写用户名会报语法错或者改错对象。IDENTIFIED BY后面接新密码密码必须用单引号包起来。语句以分号结尾别忘了。执行成功后输出是Query OK, 0 rows affected看到这个就说明改好了。想立刻验证可以退出当前会话重新登录mysql -u root -p输入新密码能进就说明修改生效。不需要重启 MySQL 服务这是语句法最舒服的一点。3.2 指定用户改密码与改自己的密码如果当前登录的是 root想改普通用户app_user的密码直接写完整标识ALTER USER app_user% IDENTIFIED BY App#2024pass;如果只想改自己当前登录账户的密码可以用USER()函数简化ALTER USER USER() IDENTIFIED BY MyNewPass!;这里的USER()返回的是当前会话的完整用户标识省去了手写主机名可能写错的麻烦。不过我更推荐显式写全用户名主机名因为可读性强回头查操作记录一眼就能看懂改了谁的密码。MySQL 8.0 还支持用ALTER USER ... IDENTIFIED WITH指定认证插件比如把认证方式从默认的caching_sha2_password改回老版本兼容的mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY NewPass_2024!;这招在遇到老客户端连不上 8.0 数据库时特别有用因为 8.0 默认的caching_sha2_password插件很多旧版客户端和驱动不支持报错信息通常是Authentication plugin caching_sha2_password cannot be loaded。把认证插件换成mysql_native_password就能解决但我得提醒一句这是兼容性妥协不是安全最佳实践。3.3 密码里的特殊字符和引号处理密码里包含!、$、#、%这类字符时在 Linux 命令行下敲命令要格外小心因为 shell 可能先把这些字符解析掉。最稳妥的方式是进入 MySQL 交互式会话之后再执行ALTER USER语句这样字符解析由 MySQL 负责不会被 shell 干扰。如果确实要在命令行里一次执行用双引号包语句、单引号包密码mysql -u root -p -e ALTER USER rootlocalhost IDENTIFIED BY Pss#123;密码本身包含单引号怎么办用反斜杠转义ALTER USER rootlocalhost IDENTIFIED BY It\s_Pass123;实在嫌麻烦建议密码里避开单引号这个字符。我个人的经验是密码用大小写字母加数字加!#%^*这些符号已经足够强没必要非用引号省得给自己找麻烦。3.4 一个容易忽略的细节改完要刷新权限吗ALTER USER改密码不需要手动执行FLUSH PRIVILEGES这个命令只在使用INSERT、UPDATE直接改mysql.user表时才需要。很多老教程里到处写FLUSH PRIVILEGES其实在正规语句法下完全是多余的。反过来说如果你发现改完密码之后旧密码还能登录那要检查是不是改错了用户。最常见的原因是数据库里同时存在rootlocalhost和root127.0.0.1两条记录你用localhost登录走的是前者但改密码时不小心改了后者。用这条语句查一下所有 root 记录SELECT user, host FROM mysql.user WHERE user root;看到多条记录不要慌逐个确认哪条是你实际登录用的主机来源再针对性地改。这种改了一个不对的 root的情况新手非常容易中招。4. 方法二SET PASSWORD 语句法老项目里还常见4.1 标准写法和适用场景SET PASSWORD是比ALTER USER更早的写法在 MySQL 5.7 及之前的版本里广泛使用。标准语法SET PASSWORD FOR rootlocalhost NewPass_2024!;不写FOR子句时默认修改当前登录用户的密码SET PASSWORD NewPass_2024!;这条语句在 5.7 上是完全可用的在 8.0 上也仍然支持但官方已经在文档里标注为逐步弃用推荐用ALTER USER替代。你能用不代表应该一直用新项目直接上ALTER USER老项目维护时按现状来就行。4.2 千万避开 PASSWORD() 函数的老写法网上大量教程还在写这种形式SET PASSWORD FOR rootlocalhost PASSWORD(NewPass_2024!);在 MySQL 5.7 上这么写能跑通但在 8.0 上会直接失败因为PASSWORD()函数已经被移除。就算在 5.7 上官方也早就警告不要用这种写法因为它会生成旧格式的密码哈希可能引发兼容问题。判断要不要用PASSWORD()函数有一个简单标准你的 MySQL 是 8.0 及以上就绝对不要用。看到教程里带PASSWORD()直接跳过找用ALTER USER的那篇。这是新手排查为什么命令报错时最高频的一个原因几乎每个刚学 MySQL 的人都在这里卡过。4.3 ALTER USER 与 SET PASSWORD 的对比对比项ALTER USERSET PASSWORD引入版本5.7 正式推荐早期版本就有8.0 支持状态官方主推仍支持但标记弃用是否支持指定认证插件支持不支持语法可读性高意图明确中等是否支持 PASSWORD()不需要旧写法会失败推荐使用场景所有新项目老代码维护从表格能看出除非你在维护一个必须兼容 5.6 的老系统否则没有理由用SET PASSWORD。我自己的习惯是只要连接的是 5.7 以上版本一律用ALTER USER命令统一了出问题也好查。4.4 批量修改多个用户密码的思路有些场景下需要一次性修改多个账户的密码比如每次上线前重置一批测试账号。这时候单个敲命令效率太低可以借助存储过程或者脚本拼接语句。一个简单的思路是用 SQL 生成改密码语句SELECT CONCAT(ALTER USER , user, , host, IDENTIFIED BY Temp#2024;) FROM mysql.user WHERE user IN (app_user, test_user, report_user);把输出的几行语句复制出来依次执行。为什么不直接写存储过程自动执行因为动态 SQL 在存储过程里需要PREPARE和EXECUTE牵扯的语法更多对初学者反而是负担。生成语句再手工执行直观、可控、出错了容易回退。提示这种批量操作改完记得逐一验证登录别一次全改完才发现某个账号主机名写错。稳妥的做法是先改一个验证通过再批量执行。5. 方法三mysqladmin 命令行工具法适合脚本运维5.1 命令结构和参数含义mysqladmin是 MySQL 自带的命令行管理工具不用进入交互式会话就能改密码特别适合写进 shell 脚本mysqladmin -u root -pOldPass password NewPass_2024!拆解一下这条命令-u root指定用户名-pOldPass提供旧密码注意-p和密码之间不能有空格password是子命令最后的NewPass_2024!是新密码。整条命令的意思就是用旧密码登录然后把密码改成新密码。这里有个关键点-p后面直接跟密码虽然能跑但密码会出现在 shell 历史记录里而且进程列表ps aux也能看到存在泄露风险。更安全的写法是只写-p让命令提示你输入mysqladmin -u root -p password NewPass_2024!回车后会提示Enter password:此时输入旧密码不会回显也不会进历史记录。生产环境强烈推荐这种方式。5.2 为什么脚本里更偏好这个方法写自动化运维脚本时进交互式会话执行 SQL 需要处理管道和重定向代码更啰嗦。用mysqladmin一行就能搞定退出码也清晰成功返回 0失败返回非 0配合 shell 的if判断非常好用#!/bin/bash if mysqladmin -u root -p$OLD_PASS password $NEW_PASS 2/dev/null; then echo 密码修改成功 else echo 密码修改失败请检查旧密码或连接配置 exit 1 fi注意旧密码是从环境变量读的脚本里不硬编码密码避免脚本文件泄露导致密码泄露。这是运维脚本的基本素养我见过把 root 密码明文写在.sh文件里的操作文件权限一松等于把数据库钥匙挂在门上。5.3 主机参数不匹配导致的连接失败mysqladmin默认连的是localhost也就是走 socket 连接。如果你的 MySQL 只监听了 TCP 端口而没开 socket或者反过来就会报连接失败。明确指定连接方式可以避免这类问题mysqladmin -h 127.0.0.1 -P 3306 -u root -p password NewPass_2024!-h 127.0.0.1强制走 TCP-P 3306指定端口。为什么要用127.0.0.1而不是localhost因为在 MySQL 客户端里localhost通常会被解释为走 Unix socket而127.0.0.1明确走 TCP。这两个路径走的是不同的认证记录rootlocalhost和root127.0.0.1在权限表里是两行。搞清楚这一点很多密码明明是对的却说拒绝访问的问题就迎刃而解了。5.4 常见报错对照与处理报错信息可能原因处理方式Access denied for user旧密码填错确认旧密码注意特殊字符转义Cant connect to local MySQL server服务未启动或 socket 路径不对检查服务状态改用-h 127.0.0.1Unknown MySQL server host主机名拼写错误用 IP 替代主机名command not found: mysqladmin未加入 PATH用绝对路径或配置环境变量这张表里的第二行是我遇到最多的情况。尤其是在 Docker 里跑的 MySQL宿主机上执行mysqladmin默认找不到容器里的 socket必须指定-h 容器IP -P 映射端口或者docker exec进容器内部执行。这个细节后面会专门讲。6. 方法四忘记密码时的跳过权限表方案6.1 这个方案到底在干什么忘了 root 密码前面的方法一个都用不了因为全都要先登录。这时候只能让 MySQL 在启动时临时忽略权限验证进去把密码改掉再恢复正常启动。核心参数是--skip-grant-tables它的作用是跳过加载权限表任何用户都能免密登录。必须理解的一点是这个状态下 MySQL 的权限系统是瘫掉的所有账号都没有权限概念包括远程连接也是无限制的。所以操作期间一定要确保 MySQL 没有对公网开放否则等于数据库大门敞开。学习环境在本地操作没问题生产服务器操作前务必确认防火墙规则或者干脆先把网络断开。6.2 Linux 下的完整操作步骤第一步停止 MySQL 服务。用 systemd 的系统执行sudo systemctl stop mysqld第二步用跳过权限表的方式启动。推荐在启动时同时加上--skip-networking彻底禁止网络连接只允许本地 socket 访问更安全sudo mysqld_safe --skip-grant-tables --skip-networking 第三步免密登录mysql -u root第四步执行改密码。这里有个坑在--skip-grant-tables模式下不能直接用ALTER USER会报错。必须先刷新权限表让权限系统重新加载FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY NewPass_2024!;FLUSH PRIVILEGES在这里是必须的不是可选项这跟前面语句法的场景正好相反。执行完FLUSH PRIVILEGES之后ALTER USER就能正常工作了。第五步重启服务恢复正常模式sudo systemctl stop mysqld sudo systemctl start mysqld第六步用新密码登录验证mysql -u root -p6.3 Windows 下的对应操作Windows 上操作逻辑一样只是命令形式不同。先停服务net stop mysql80然后用跳过权限表的模式启动。要在命令行窗口里执行mysqld --skip-grant-tables --skip-networking --console--console参数让日志输出到当前窗口方便观察启动过程。这个窗口要保持打开不要关。另开一个 CMD 窗口执行mysql -u root免密登录然后重复上面第四步的FLUSH PRIVILEGES和ALTER USER语句。改完之后在第一个窗口按CtrlC停止 mysqld 进程再用net start mysql80正常启动服务。Windows 上服务名不一定是mysql80装了不同版本可能是mysql或者mysql57用sc query | findstr mysql可以查。6.4 跳过权限表方案的三个高风险点第一个风险是网络暴露。--skip-grant-tables下的 MySQL 对任何能连上的人都敞开所以务必加上--skip-networking或者确保本机防火墙拦截了 3306 端口。这一步省了可能在改密码的几分钟里被人趁虚而入。第二个风险是 8.0 的语法变化。有些 8.0 版本在跳过权限表模式下ALTER USER仍然会报ERROR 1290提示需要先FLUSH PRIVILEGES。前面已经强调过这里就是原因所在。如果FLUSH PRIVILEGES之后还报错可以退而用SET PASSWORD试试。第三个风险是启动参数找不到。Ubuntu 系统上mysqld_safe可能不在默认 PATH 里需要用绝对路径/usr/sbin/mysqld_safe或者用service mysql start --skip-grant-tables。不同发行版的服务管理工具不一样CentOS 用systemctlUbuntu 传统上用service和mysqld_safe选错了命令会直接报找不到命令。6.5 一个隐藏手段读取初始化日志里的临时密码如果你不是忘了密码而是新装完 MySQL 根本不知道初始密码是什么那不需要走跳过权限表这条路。MySQL 5.7 和 8.0 在首次初始化时会生成一个随机临时密码写在错误日志里。Linux 下通常在sudo grep temporary password /var/log/mysqld.log或者sudo grep temporary password /var/log/mysql/error.log日志路径取决于安装方式和发行版。CentOS 的 yum 安装一般是/var/log/mysqld.logUbuntu 的 apt 安装是/var/log/mysql/error.log。拿到临时密码后第一次登录会强制要求立即改密码ALTER USER rootlocalhost IDENTIFIED BY NewPass_2024!;这招适用于初次登录找不到密码而不是密码被改忘了两者要区分开。很多人一上来就用跳过权限表方案其实先翻一下日志可能几秒钟就解决了。7. 四种方法的横向对比与选型建议7.1 一张表看清四种方法的适用边界方法是否需要登录是否需要重启适用版本推荐场景ALTER USER是否5.7日常改密码的首选SET PASSWORD是否5.7 及更早为主维护老项目mysqladmin是否全版本脚本自动化跳过权限表否是全版本忘记密码救急选型逻辑其实很清楚能登录就用第一种写脚本用第三种登录不了才用第四种。第二种基本只在接触老代码库时才会遇到。把这张表存下来下次遇到改密码的需求直接对号入座不用再翻教程。7.2 不同版本版本的实际差异MySQL 5.6 时期的改密码主流写法是SET PASSWORD PASSWORD(xxx)因为那时还没有ALTER USER这个语句。5.7 引入了ALTER USER但PASSWORD()函数还在所以两种写法共存。到了 8.0PASSWORD()函数被移除官方推荐只剩ALTER USER。另外 8.0 默认认证插件从mysql_native_password换成了caching_sha2_password这是很多人在 8.0 上遇到的另一个高频问题。如果你用的客户端版本较老连 8.0 会报认证插件错误解决办法就是前面提过的改密码时顺带指定mysql_native_password插件。经验之谈接手一个老项目第一件事就是SELECT VERSION()看版本然后决定用哪套语法。不要凭记忆敲命令版本差异能让你在同事面前尴尬一整天。7.3 Docker 环境下的特殊处理现在用 Docker 跑 MySQL 很普遍容器环境改密码的逻辑跟宿主机不一样。如果容器还在运行且你知道旧密码直接进容器执行docker exec -it mysql_container mysql -u root -p进去之后用ALTER USER改跟平时没区别。如果忘了密码就得用环境变量重建容器或者修改容器启动参数。Docker 启动时可以用MYSQL_ROOT_PASSWORD环境变量设置初始密码docker run -d --name mysql_container \ -e MYSQL_ROOT_PASSWORDNewPass_2024! \ -p 3306:3306 mysql:8.0注意这个环境变量只在首次初始化数据卷时生效。如果数据卷已经存在改这个变量不会改变实际密码。想重置得先删掉数据卷但那样数据就没了。所以容器环境下的密码管理最好的办法还是记牢或者用配置文件管理别指望环境变量随时能改。7.4 CentOS 7 与麒麟系统的差异提醒搜索热词里提到centos7虚拟机修改密码和麒麟v10server 修改密码说明国产化环境下这类操作也很常见。这两类系统在 MySQL 服务管理上有个共同点都可能使用systemctl管理 MySQL 服务也可能用service命令。麒麟系统基于 Linux 内核命令层面跟 CentOS 大同小异差别主要在于包管理器和默认目录。麒麟 V10 上安装的 MySQL 如果来自系统源配置文件和日志路径可能跟标准发行版不同遇到权限问题时先确认datadir和log-error的实际位置SHOW VARIABLES LIKE datadir; SHOW VARIABLES LIKE log_error;拿到路径再去看日志比盲目猜测有效得多。国产化环境的文档相对少遇到问题多靠这两个变量定位这是我处理这类系统的实用习惯。8. 我踩过的那些密码相关的坑第一次改 MySQL 密码我用的是网上抄来的SET PASSWORD PASSWORD(123456)在 8.0 上报错之后我以为是权限不够折腾了半天才发现是函数被移除了。那次之后我学乖了改密码前先SELECT VERSION()这个习惯帮我省了无数排查时间。第二个坑是密码策略。有次给测试环境设了个 6 位纯数字密码被validate_password拦下来报错信息一眼看不懂查了半天才知道是策略问题。现在的做法是本地学习环境先把策略调到 LOW 再改密码省去和策略较劲的功夫。第三个坑最深刻——在 Windows 上用net stop mysql停服务结果提示服务名无效因为实际服务名是mysql80。服务名这个东西跟安装时选的名称有关不是固定的。查服务名的命令是sc query state all | findstr /i mysql第四个坑是 Docker 数据卷。我以为改MYSQL_ROOT_PASSWORD环境变量就能重置密码重启容器后发现密码根本没变因为数据卷里已经存了旧密码。最后只能进容器用ALTER USER改把数据卷和环境变量的关系搞明白了才算彻底理解容器里 MySQL 的初始化机制。这些坑说到底都指向同一件事改密码之前先确认环境和版本。版本决定语法环境决定命令形式把这两个前提摸清楚四种方法随便挑一种都能顺利跑通。数据库操作里没有看起来差不多这回事差一个下划线、差一个主机名结果就完全不一样。最后分享一个我个人的小习惯所有项目的 MySQL 密码我都会在改完后立刻做两件事——一是用新密码实际登录一次确认无误二是把新密码同步到项目的配置管理里。改完不验证、改完不同步是造成改了等于没改和服务半夜挂掉的两大元凶。密码管理的本质不是记住密码本身而是管理密码的变更流程把流程固定下来才是真正省心的做法。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进