批量创建mysql用户应使用create user+grant组合而非直接insert mysql.user表,注意单引号包裹sql、反引号转义库名、限定主机范围、显式flush privileges,并通过pt-show-grants审计权限。

如何用 shell 脚本批量创建 MySQL 用户并授权
直接写脚本比进 mysql 客户端一条条输快得多,但要注意权限语句必须用单引号包裹,避免 shell 解析特殊字符。常见错误是把 GRANT 里的数据库名写成变量却没加反引号,导致库名含短横线(如 my-app)时语法报错。
实操建议:
- 用
mysql -u root -p<password></password>连接,密码不交互式输入时务必确保脚本权限为600,防止泄露 - 用户主机名别硬写
'%',生产环境优先用具体 IP 段或内网域名,如'10.20.%.%' - 授权后必须显式执行
FLUSH PRIVILEGES;,否则新用户无法登录(MySQL 8.0+ 在部分场景下可省略,但兼容性起见仍建议保留)
#!/bin/bash USER="app_user" PASS="s3cur3_p@ss" DB="myapp_prod" <p>mysql -u root -p'your_root_pass' -e " CREATE USER IF NOT EXISTS '$USER'@'10.20.%.%' IDENTIFIED BY '$PASS'; GRANT SELECT, INSERT, UPDATE ON `$DB`.* TO '$USER'@'10.20.%.%'; FLUSH PRIVILEGES;"
Python 脚本自动同步用户配置到多台 MySQL 实例
当有主从、分片或测试/预发多套环境时,靠人工逐台同步用户极易漏配或权限不一致。用 Python 的 pymysql 或 mysql-connector-python 可读取 YAML 配置文件,再并发连接各实例执行相同 SQL。
关键点:
- 不要在循环里反复调用
subprocess.run(['mysql', ...]),开销大且难统一处理错误;改用数据库驱动原生连接 - 用户密码需加密存储,至少用环境变量传入,避免明文写在 YAML 里
- MySQL 8.0 默认认证插件是
caching_sha2_password,旧版驱动可能连不上,需在连接参数中指定auth_plugin='mysql_native_password'
import yaml
import pymysql
<p>with open("users.yaml") as f:
config = yaml.safe_load(f)</p><p>for host in config["targets"]:
try:
conn = pymysql.connect(
host=host,
user="root",
password=config["root_pass"],
auth_plugin="mysql_native_password"
)
with conn.cursor() as cur:
cur.execute("CREATE USER IF NOT EXISTS 'reporter'@'%' IDENTIFIED BY %s", (config["reporter_pass"],))
cur.execute("GRANT SELECT ON <em>.</em> TO 'reporter'@'%'")
conn.commit()
except Exception as e:
print(f"Failed on {host}: {e}")
finally:
conn.close()为什么 mysql.user 表不能直接 INSERT,而要用 CREATE USER
直接往 mysql.user 表 INSERT 是危险操作:MySQL 8.0+ 的密码哈希格式、插件字段(plugin)、过期策略(password_expired)等字段逻辑复杂,手写容易出错。更严重的是,某些字段(如 account_locked)在老版本不存在,INSERT 会因列数不匹配失败。
正确做法只有两个:
- 始终用
CREATE USER+GRANT组合,这是唯一受支持的接口 - 若真要批量导入,先用
mysqldump --no-create-info mysql user导出结构,再对照生成合规 INSERT,但仅限离线调试,禁止上线使用
用 pt-show-grants 生成可审计的用户权限快照
pt-show-grants(Percona Toolkit 中的工具)能将当前所有用户的权限导出为标准 CREATE USER 和 GRANT 语句,格式清晰、可 diff、可存 Git 版本控制,比人工查 SHOW GRANTS FOR 'u'@'h' 高效太多。
使用注意:
- 它默认只输出非匿名用户,如需包含
''@'localhost',得加--all参数 - 输出不含
FLUSH PRIVILEGES,回放脚本时要自己补上 - MySQL 8.0 的角色(ROLE)权限会被正确还原,但要求 Percona Toolkit ≥ 3.3.0
$ pt-show-grants --user=root --password=xxx > grants-20240520.sql $ grep "app_user" grants-20240520.sql CREATE USER 'app_user'@'10.20.%.%' IDENTIFIED WITH 'caching_sha2_password' AS '$A$...'; GRANT SELECT, INSERT, UPDATE ON `myapp_prod`.* TO 'app_user'@'10.20.%.%';
MySQL 用户管理真正的难点不在语法,而在权限最小化落地、多环境一致性校验、以及密码生命周期管理——这些没法靠一个 CREATE USER 语句解决,得靠脚本约束流程,再靠定期审计兜底。










