MySQL 8.0 不允许在 CREATE USER 中直接调用 RAND() 等函数设密码,因密码字段仅接受字符串字面量或预计算哈希值;正确做法是先创建占位用户,再用 ALTER USER 设置 SUBSTRING(MD5(RAND()),1,16) 等生成的随机字符串,或在应用层用 secrets 等强随机源生成后传入。

MySQL 8.0 创建用户时不能直接用 RAND() 生成密码
MySQL 不允许在 CREATE USER 语句中调用函数(如 RAND()、UUID())作为密码值,直接写 CREATE USER 'u'@'%' IDENTIFIED BY RAND() 会报错 ERROR 1827 (HY000): The password hash doesn't have the expected format。密码字段只接受字符串字面量或预计算的哈希值。
用 PREVIOUS + SET PASSWORD 或 ALTER USER 两步生成随机密码
最稳妥的做法是先创建空密码或占位密码的用户,再用 SET PASSWORD 或 ALTER USER 设置随机字符串。推荐用 ALTER USER(MySQL 8.0 默认禁用 SET PASSWORD,除非 old_passwords=OFF):
CREATE USER 'rnduser'@'localhost' IDENTIFIED BY 'placeholder'; ALTER USER 'rnduser'@'localhost' IDENTIFIED BY SUBSTRING(MD5(RAND()), 1, 16);
说明:
-
SUBSTRING(MD5(RAND()), 1, 16)生成 16 位十六进制随机字符串,长度适中且避开特殊字符引发的引号/转义问题 - 避免用
UUID()—— 它含短横线,某些客户端解析密码时可能出错 - 不要用
SHA2(RAND(), 256)——RAND()返回浮点数,SHA2()需要字符串输入,隐式转换不可靠 - 执行后立即用
SELECT User, Host, authentication_string FROM mysql.user WHERE User = 'rnduser';确认密码已更新
用存储过程封装批量随机用户创建(带错误处理)
如果需批量创建,建议封装为存储过程,避免手动拼接风险:
DELIMITER $$
CREATE PROCEDURE create_random_user(IN u_name VARCHAR(32), IN h_name VARCHAR(60))
BEGIN
DECLARE pwd VARCHAR(32) DEFAULT SUBSTRING(MD5(RAND()), 1, 16);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
CREATE USER IF NOT EXISTS CONCAT(u_name, '_', FLOOR(RAND()*1000))@h_name IDENTIFIED BY pwd;
SELECT CONCAT('User created: ', u_name, '_', FLOOR(RAND()*1000), '@', h_name, ', pwd=', pwd) AS result;
COMMIT;
END$$
DELIMITER ;
调用:CALL create_random_user('api', '10.%.%.%');
注意:
- 过程内不能直接用变量做
CREATE USER ... IDENTIFIED BY @pwd—— MySQL 不支持参数化密码,必须拼成字面量 -
FLOOR(RAND()*1000)防止用户名重复,但高并发下仍需加重试逻辑 - 该过程未校验 host 格式合法性,传入非法 host(如含单引号)会导致语法错误
从应用层生成密码比数据库内生成更可控
真正生产环境强烈建议在应用代码里生成密码(Python/Go/Java 等),再传给 CREATE USER:
例如 Python:import secrets; pwd = secrets.token_urlsafe(12),然后执行 cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user, host, pwd))
原因:
- 数据库内随机函数熵源有限,
RAND()是伪随机且依赖连接种子,多连接并发时易重复 - 应用层可选更强算法(如
secrets模块)、规避 SQL 注入风险、统一密码策略(长度、字符集) - DBA 无法审计或拦截明文密码——若必须日志记录,应用层可控制脱敏时机
MySQL 自身不提供密码强度校验机制,validate_password 插件对动态生成的密码无效,这块得靠外部把关。


















