ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

MySQL密码修改全攻略:从ALTER USER到root救援与策略配置

2026/9/18 5:42:00 拓冰建站 浏览量
MySQL密码修改全攻略:从ALTER USER到root救援与策略配置 1. 先捋清楚MySQL修改密码有哪些入口不知道你有没有过这种体验在群里看到有人问MySQL密码怎么改底下回复五花八门有说用UPDATE改mysql.user表的有说用SET PASSWORD的有说直接重装的。其实这些说法都沾边但离规范操作都有距离。MySQL改密码这件事从5.7到8.0变化不小不同场景下该用的命令也不同我先按常用程度把入口捋一遍。1.1 最推荐的ALTER USER方式如果你的MySQL账号能正常登录且当前版本在5.7及以上那我强烈建议统一用ALTER USER来改密码这是官方推荐的做法也是语义最清晰的一条路径。ALTER USER rootlocalhost IDENTIFIED BY NewPass$123;执行完这条语句之后当前会话不受影响下一次登录时使用新密码即可。注意这里的rootlocalhost是完整的账号名由用户名加主机名两部分组成两者缺一不可。很多人改完密码没生效就是因为主机名写错了——你想改的是rootlocalhost结果语句里写的是root%那改的压根不是同一个账号。MySQL 8.0里还可以拆成两步走先创建账号再指定密码插件不过日常改密用不着这么麻烦。有一个细节值得提一下8.0对密码插件的默认值做过调整默认使用caching_sha2_password如果你的客户端比如老版本的Navicat、JDBC驱动不支持这个插件改了密码后反而会连不上。这种情况我后面在实战坑里会再展开。1.2 SET PASSWORD与mysqladmin的老牌用法在ALTER USER出现之前大家最常用的其实是SET PASSWORD语句。5.7之前是这么写的SET PASSWORD FOR rootlocalhost PASSWORD(NewPass$123);5.7开始PASSWORD()函数被标记为废弃8.0里直接移除了所以现在更推荐这种写法SET PASSWORD FOR rootlocalhost NewPass$123;还有一类场景是你在Shell脚本里想改密码不想进MySQL交互环境那mysqladmin就是最适合你的工具mysqladmin -uroot -pOldPass$123 password NewPass$456注意mysqladmin的password参数后面跟的是新密码前面的-p后面跟的是旧密码中间用空格分隔。这个工具在自动化运维脚本里非常好用但它有一个缺点——新密码直接出现在命令行参数中可能被系统进程列表ps aux捕捉到。如果你在多人共用的服务器上操作建议还是用mysql -e加SQL的方式代替或者干脆在交互环境里执行。1.3 可视化工具与初始化参数里的隐藏入口用Navicat、MySQL Workbench这类图形工具改密码本质上还是调用了ALTER USER或SET PASSWORD只是帮你省了打命令的功夫。Workbench里在左侧导航栏找到Users and Privileges选中账号直接设置新密码即可适合不习惯命令行的人。用这种方式时同样要注意主机名匹配问题。还有一个容易被忽略的场景初始化安装时的自动生成密码。MySQL从5.7开始在数据目录初始化完成后会自动生成一个临时密码这个密码打印在错误日志里路径一般在/var/log/mysqld.log或/data/mysql/*.err。首次登录必须用这个临时密码登录后系统会强制你修改grep temporary password /var/log/mysqld.log mysql -uroot -p输入临时密码后MySQL会提示ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.——意思是除了改密码其他SQL一律不让你执行。这是MySQL的安全设计不是故障很多人第一次遇到还以为自己装坏了。为了方便对照我把常用改密方式的适用场景整理成了一个表方式适用场景注意事项ALTER USER常规改密、强制改密需指定完整账号名5.7推荐SET PASSWORD老版本兼容、SQL脚本8.0不能用PASSWORD()函数mysqladmin passwordShell脚本、免交互密码会暴露在进程列表中GUI工具日常管理注意主机名匹配临时密码初始化首次登录登录后必须立即改密2. 为什么改密码总是报ERROR 1819密码强度规则拆解新手最容易撞上的报错有两个一个是ERROR 1819 (HY000): Your password does not satisfy the current policy requirements另一个是ERROR 1820 (HY000): You must reset your password ...。前者是我们本节要重点解决的密码强度问题后者就是上一节说的首次登录必须改密。2.1 validate_password组件的触发机制MySQL从5.6开始引入了validate_password插件用于强制密码复杂度。5.7里它是一个插件8.0里官方把它重构为组件component但功能逻辑是一致的。它默认是启用的这也是为什么你执行ALTER USER设置一个弱密码比如123456、password时会直接报1819的原因。从8.0.27开始validate_password的组件名从validate_password.so调整了部分发行版里要通过INSTALL COMPONENT file://component_validate_password;显式安装。如果你用的是MySQL自带的发行包大多数情况是默认装好的可以通过下面的命令确认SHOW VARIABLES LIKE validate_password%;如果这个变量列表是空的说明组件还没加载。在8.0里用下面的语句安装INSTALL COMPONENT file://component_validate_password;在5.7里则是INSTALL PLUGIN validate_password SONAME validate_password.so;2.2 策略参数逐项说明长度、大小写、数字、特殊字符确认组件已启用后执行SHOW VARIABLES LIKE validate_password%会看到一堆参数它们共同决定了一个密码是否合格。我直接用一个实际输出示例来拆解参数名示例值含义validate_password.length8密码最小长度validate_password.number_count1至少要包含的数字个数validate_password.policyMEDIUM密码策略强度validate_password.mixed_case_count1至少要包含的大小写字母个数validate_password.special_char_count1至少要包含的特殊字符个数validate_password.check_user_nameON密码是否不能包含用户名policy参数有三个取值LOW只验证长度MEDIUM验证长度、数字、大小写、特殊字符STRONG在MEDIUM基础上还要对照一个字典文件validate_password.dictionary_file检查密码是否属于常见弱口令。生产环境一般设置MEDIUM就够了STRONG适合等保要求极高的场景。2.3 手动计算一个密码为什么不合格理解了参数我们再来看实际场景。假设当前策略是MEDIUM也就是上面表格里的默认值我在MySQL里执行ALTER USER appuserlocalhost IDENTIFIED BY mypassword;结果报1819。为什么因为mypassword虽然长度有11位但既没有数字、也没有大写字母、也没有特殊字符三条硬性指标全挂了。我换一个ALTER USER appuserlocalhost IDENTIFIED BY Mypss2025;这次能成功。这里的密码规则是长度8位以上数字至少1个大小写至少各1个特殊字符至少1个。如果你用的密码是Mypss长度只有6位再满足其他条件也没用第一关长度就过不了。这里有个特别容易踩的坑validate_password.length的语义并不是必须满8位就行而是在满足其他条件的基础上密码总长度不少于8位。也就是说如果你把其他计数都满足比如大小写混合了、特殊字符有了、数字也有了但最终密码只有7位依然会被拒。有些人以为length8只是最低要求实际7位也能过结果就卡在1819上。2.4 临时放行弱密码的几种做法开发环境里项目急着起服务测试账号密码想用123456这时候你可能会想直接关掉密码校验。可行但分两种情况方法一当前会话临时降低策略不需要改配置文件。执行SET GLOBAL validate_password.policy LOW; SET GLOBAL validate_password.length 6;这里的policy LOW意味着只检查长度不再强制数字、大小写、特殊字符。改完后设置简单密码就不会报1819了。这种方式的缺点是重启MySQL后参数会恢复默认值适合临时用。方法二彻底停用组件配置文件里指定[mysqld] validate_passwordOFF5.7里这样写可以禁用插件8.0里组件方式则是在配置里加validate-passwordOFF。但说实话我很少建议彻底关掉校验尤其是数据库暴露在内网且有多个开发人员共用的场景。密码策略在关键时刻是能拦住一大部分误操作和低级爆破的与其关掉不如把策略调低到符合团队实际使用习惯的水平。3. 忘了root密码的直接救援路径跳过授权表怎么安全操作改密码这事儿最让人头疼的不是不知道命令而是root密码忘了连MySQL都进不去。这时候任何ALTER USER、SET PASSWORD都是空谈。我见过不少人遇到这种情况第一反应是重装数据库其实完全不用一条启动参数就能解决问题。3.1 为什么常规方法救不了rootMySQL的用户认证信息存储在mysql.user系统表里读取这张表需要服务端先完成权限验证。当你忘了root密码意味着无法通过正常认证流程进入服务端所以必须在启动时临时跳过权限验证让MySQL认为所有请求都不需要认证然后你才有机会去改密码表。这就是--skip-grant-tables的作用。需要注意的是--skip-grant-tables会跳过mysql.user表的加载但它不是所有权限都放开而是客户端连接时不需要校验身份进来后具备所有权限。这意味着操作时要极其小心不能在跳过了认证的端口上继续执行危险操作尤其是不能把服务直接暴露给外部网络。3.2 从停机到恢复的全流程操作我用CentOS 7 MySQL 8.0的环境演示一遍完整救援流程。首先停止MySQL服务systemctl stop mysqld然后以跳过授权表的方式启动这里有一个我踩过坑的细节直接执行mysqld --skip-grant-tables在前台启动会占用终端且不方便管理更推荐用初始化脚本或手动指定配置文件的方式。实际生产中我习惯这样操作mysqld --usermysql --skip-grant-tables --skip-networking --skip-networking参数非常关键它让MySQL只监听本地socket连接不监听TCP端口这样外部机器无法趁虚而入。如果你用的是systemd管理也可以用临时修改环境变量的方式但手动起进程最直观。启动完成后另开一个终端直接连接mysql -uroot此时不需要密码就能进入。下一步才是重头戏在没有认证权限的情况下执行ALTER USER其实可能会报错因为权限表没有加载。所以更稳妥的做法是先让权限表生效再改密码FLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY NewAdmin$123;记住FLUSH PRIVILEGES是关键步骤它会重新加载mysql.user表让后续的账号操作恢复到正常语义。如果你跳过这步直接执行ALTER USER在一些版本里会报Table mysql.user doesnt exist或权限不足的错。修改成功后正常关闭这个临时启动的mysqld进程再启动正式服务kill $(pgrep -f mysqld) systemctl start mysqld用新密码登录验证mysql -uroot -pNewAdmin$1233.3 救援模式下的常见翻车点这套流程看着简单实际上容易翻车的地方不少第一FLUSH PRIVILEGES之前不要执行任何依赖权限表的SQL更不要在跳过授权的状态下顺手建库删库。因为此时你拥有的是无授权校验的超级权限任何误操作都没有撤回的余地。第二临时启的mysqld进程最好用--skip-networking锁死TCP端口防止内网其他机器趁着空窗期连进来。等改密完成、正规服务启动后网络监听自然恢复。第三如果你用的是MySQL 5.6或更老的版本mysql.user表里的密码字段叫Password需要走UPDATE mysql.user SET Password PASSWORD(...) WHERE User root;这条老路。5.7开始字段改名为authentication_string且不再建议直接UPDATE系统表而是用ALTER USER。第四部分云厂商的RDS或托管实例会把数据目录和权限表做额外封装--skip-grant-tables这种方式在托管实例里可能根本用不了。如果是在云数据库上忘了密码老老实实找控制台的重置密码按钮别折腾底层文件。4. 密码强度规则在生产环境里到底怎么定实战取舍与踩坑记录理论讲完接下来聊聊更贴近真实业务的场景。很多人以为把validate_password.statusSTRONG开到最高就是安全结果是开发、测试、生产环境的账号全被密码复杂度卡死最后大家约定俗成把密码统一成Admin123456反而比弱密码更危险。密码策略这东西必须结合业务场景来定而不是一味追求最强。4.1 从初始密码到上线一次完整的账号规划以一个常规Web项目为例你花半小时规划账号密码能省掉后续无数个连接失败的排查时间。我的基本流程是这样的第一步安装完MySQL后先做一次初始化安全加固包括设置root密码、删除匿名账号、清理测试库mysql_secure_installation这个交互式脚本会引导你做基础加固建议每一步都走一遍。如果你用的是自动化部署也可以用mysql -e在脚本里执行等价SQL。第二步按职责创建专用账号不用root跑业务。比如一个应用账号负责读写业务库一个只读账号给报表系统用CREATE USER app_rw10.0.0.% IDENTIFIED BY App#Rw2025; CREATE USER report_ro10.0.0.% IDENTIFIED BY Report#Ro2025; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app_rw10.0.0.%; GRANT SELECT ON mydb.* TO report_ro10.0.0.%;第三步设置一个符合业务需求的密码策略。比如内部工具系统不涉及用户隐私数据又有很多同事要连策略可以放宽到LOW级别但生产交易库必须MEDIUM起步且核心账号定期更换。这里要特别提醒validate_password.check_user_name参数默认是ON意思是密码里不能包含用户名。比如你的账号是app_rw你把密码设置成App_rw2025看起来大小写、数字、特殊字符都有了但因为包含app_rw这段字符串会被直接拒绝。这个报错不像1819那么常见出现时容易让人摸不着头脑。4.2 应用连接串里的密码管理一个经常被忽略的细节写到这里我想起一个实际踩过的坑。有一次同事反馈应用偶尔报Access denied for user排查了半天最后发现是代码里配置的连接串密码是两个月前的旧密码而DBA在某个时间点把密码更新了但配置文件的密码没有同步更新。连接池会缓存之前的连接旧的连接还在跑新的连接排到阈值就报错。这类问题的根源不在于MySQL密码怎么改而在于密码变更的联动机制没有建立。我现在的做法是所有涉及MySQL密码变更的操作必须先走审批流程同时在变更单里列出所有依赖该账号的应用配置项逐项确认是否同步更新。对于自研系统最好把数据库密码收口到配置中心或密钥管理系统应用启动时动态拉取而不是写死在配置文件和代码仓库里。4.3 批量修改账号密码的正确姿势生产环境有时需要批量修改一批业务账号的密码比如定期安全巡检后强制轮换。直接在MySQL里写多条ALTER USER当然可以但更容易出错的是漏改或改错账号。我的做法是先在测试环境生成变更SQL再通过脚本在生产端执行mysql -uroot -p -e SELECT CONCAT(ALTER USER , user, , host, IDENTIFIED BY NewPrefix2025;) FROM mysql.user WHERE user NOT IN (root, mysql.sys, mysql.session); 先把上面这条SQL的输出保存成一个change_passwords.sql文件人工检查一遍列表无误后再批量执行mysql -uroot -p change_passwords.sql为什么不直接在一条命令里循环因为批量不安全一旦中间某个账号因为密码策略失败你不知道前面几个已经改成功了。生成SQL文件再人工过一遍至少能在执行前发现明显问题。执行完成后别忘了去应用侧抽查一两个账号的登录连通性不要等到业务侧报警才发现某个重要账号被漏改。4.4 连接串中的特殊字符转义问题我在改密后遇到过一种很有意思的情况密码本身设置成功了mysql -uapp_rw -p手动登录也正常但应用起不来日志里报Access denied。最后发现是密码里的$符号被应用读取配置时当成了变量前缀实际传给MySQL的密码变短了。这个问题在Shell脚本、YAML配置文件、Java properties文件中都可能遇到。比如密码是App#Rw$2025在application.yml里如果没加引号$2025可能被解析成变量。处理办法很简单配置文件里的密码统一加引号Shell脚本里用单引号包住密码连接串使用URL编码转义特殊字符。如果你在改密时顺手把应用配置里的密码也改成了带$、#、的强口令记得回头检查一遍配置文件别让密码在最后一公里被吃掉。5. 从5.7到8.0改密语法的版本差异与兼容策略MySQL版本迭代带来的语法变化是很多人改密码报错的隐藏原因。你可能在网上搜到一篇两年前的教程照着执行却发现自己的MySQL不认账这不是操作问题很可能是版本差异。5.1 5.7与8.0在密码处理上的核心差异最核心的变化有三个第一PASSWORD()函数在8.0已被移除。5.7里写SET PASSWORD PASSWORD(...)还能用8.0里直接报函数不存在。如果你维护的是混合版本环境写脚本时尽量不要依赖这个函数。第二默认认证插件从mysql_native_password变成了caching_sha2_password。老客户端比如MySQL 5.1时代的驱动、旧版Navicat连接8.0时经常报Authentication plugin caching_sha2_password cannot be loaded解决方式有两种要么升级客户端驱动到8.0配套版本要么在创建账号时显式指定认证插件CREATE USER legacy_app% IDENTIFIED WITH mysql_native_password BY Legacy#123;第三8.0里ALTER USER的密码过期策略更灵活可以设置账号密码永不过期或在指定天数后过期ALTER USER app_rw10.0.0.% PASSWORD EXPIRE INTERVAL 90 DAY;对于安全要求比较高的业务这个功能很有用可以让账号密码定期强制轮换。5.7也有类似机制但8.0的表达更清晰。5.2 低版本升级到8.0后的改密注意点如果你是从5.7原地升级到8.0升级成功后再用老密码登录MySQL会自动把认证方式升级到caching_sha2_password吗答案是升级脚本会尽量转换但通过my.cnf配置default_authentication_pluginmysql_native_password的实例新账号仍然可能使用旧插件。这就导致升级后环境里同时存在两种插件的账号排查问题时容易混淆。建议升级后统一执行一次SHOW VARIABLES LIKE default_authentication_plugin;看下当前默认值再决定账号策略。5.3 我处理跨版本改密问题的排查顺序如果你在混合版本环境里改密码遇到诡异问题我建议按下面顺序排查能省下不少时间确认MySQL版本SELECT VERSION();不同版本语法差异很大。确认当前是否强制改密SHOW VARIABLES LIKE validate_password%;和SHOW VARIABLES LIKE default_authentication_plugin;确认客户端是否支持新认证插件用老客户端试连报插件不支持的错就去升级驱动或显式指定mysql_native_password。确认改密的账号主机名是否匹配SELECT user, host, plugin FROM mysql.user;看是否有多条记录。确认密码里没有包含账号名或用户名片段关掉check_user_name或换一个完全无关的密码。这套检查顺序帮我在好几次故障里快速定位到问题成因特别是在别人刚接手数据库、账号权限已经被改得乱七八糟的环境里非常管用。6. 密码忘了、策略卡住了最后再分享几个实操技巧写到这里该讲的命令和原理基本都覆盖了。按我自己的经验MySQL密码管理这类操作十年后回看可能还是那几个核心点账号与主机名的对应关系、密码策略与业务场景的匹配、版本差异导致的行为变化。我再把几个高频实操技巧集中说一下方便你直接参考。6.1 快速测试密码是否符合策略如果你不确定新设置的密码能不能通过强度校验不必等到ALTER USER报错可以直接用validate_password接口测试。在已启用组件的实例上执行SELECT VALIDATE_PASSWORD_STRENGTH(My#Pass2025);这个函数返回一个0到100的整数低于25表示密码不满足当前策略。我经常用它来调试比如团队里有同事设计了几个候选密码我会一条SQL跑出来直接告诉他哪个能用、哪个不行。6.2 修改密码后顺手做的三件事改完密码不是终点我每次改完都会顺手做三件事防止后续踩坑测试连接mysql -u用户名 -p新密码 -e SELECT 1;确认改密后能用新密码正常登录。检查应用侧配置如果有业务依赖该账号确认连接串里的密码已同步更新可以先在测试环境跑一遍连通性。记录变更把改密时间、相关账号、影响范围记录到运维变更单。这一步看似多余但如果你想在一个月后回答这个账号的密码到底是什么时候改的记录就是救命稻草。6.3 一个我个人的小习惯给root设置强密码后使用带sudo的操作我见过很多人在日常工作中一直用root账号连接生产MySQL理由是方便。但这个习惯在遇到密码策略调整、账号迁移、权限回收时会成为巨大隐患。我的建议是root密码设置成强口令后妥善保管日常运维通过一个单独的运维账号连接只授予必要权限确需root操作时临时切换。这不仅是为了安全也是为了避免你哪天手滑改错了root密码把自己锁在门外。最后再说一个容易被忽视的小细节密码里的空格。MySQL的IDENTIFIED BY语法是支持密码中包含空格的比如New Pass 2025但这个密码写入应用配置文件时非常容易因为解析问题导致连不上。所以我个人强烈建议生产环境的数据库密码不要包含空格长度控制在中高强度即可比如App$Rw2025这样既满足规则又不容易在配置解析时出错的组合。