在MySQL中创建存储过程或者存储函数,很多时候并不是语法问题,而是权限配置挡住了流程。有一个专门的权限叫 CREATE ROUTINE,很多同学只给 CREATE、INSERT、UPDATE,到了执行 CREATE PROCEDURE 时还是收到权限报错。这个权限与建表的 CREATE 并不等价,它单独控制例程对象的创建。

一、CREATE ROUTINE权限与CREATE权限的关系
MySQL的权限粒度在例程层面设计得比较细。创建表依赖 CREATE 权限,创建视图依赖 CREATE VIEW,创建存储过程和函数则依赖 CREATE ROUTINE。也就是说,即使你拥有某个库的 CREATE 权限,也只代表可以建表,不能推断你可以建存储过程。两者在内部存储到不同的授权列中,比如全局用户表里的 Create_priv 和 Create_routine_priv 就是分开的。
可以通过 SHOW PRIVILEGES 查看MySQL支持的权限列表,里面会列出 Create routine 的上下文为数据库、表或全局。这个权限不允许按某个具体的存储过程授权,只能全局授权,或者授权到某个数据库下,意味着该数据库下所有例程的创建动作都受它控制。
理解这一点后,排查问题就简单了:如果一个账号在测试库建表正常,但创建存储过程报错,优先检查 mysql.db 表中该账号所在库的 Create_routine_priv 列,而不是只看 Create_priv。
-- 查看MySQL支持的所有权限 SHOW PRIVILEGES; -- 查看用户级权限 SELECT User, Host, Create_priv, Create_routine_priv FROM mysql.user WHERE User = 'routine_user';
二、完整授权与撤销实例:从建用户到创建存储过程
下面使用一个独立数据库 routine_demo 和一个最小权限账号 routine_user 进行演示。先创建数据库和用户,再只授予 CREATE ROUTINE 权限,不做全库授权。这样可以观察权限是否真正够用。
执行授权时,MySQL 8.0 的 GRANT 语句要求用户已经存在,所以需要先 CREATE USER。如果是在 MySQL 5.7 上,GRANT 可以隐式建用户,但新版本已经移除了这个行为。授权完成后,用 SHOW GRANTS 确认当前账号拥有的权限,亮出最小权限集合。
-- 以root或具有创建权限的管理账号执行 CREATE DATABASE IF NOT EXISTS routine_demo; CREATE USER IF NOT EXISTS 'routine_user'@'localhost' IDENTIFIED BY 'Str0ngPass!'; GRANT CREATE ROUTINE ON routine_demo.* TO 'routine_user'@'localhost'; SHOW GRANTS FOR 'routine_user'@'localhost';
接着切换到 routine_user 账号登录,并尝试创建一个只返回固定结果的存储过程。这里不需要访问任何业务表,因此可以排除表权限的干扰。只要 CREATE ROUTINE 权限存在,下面的语句就应该执行成功。
-- 使用 routine_user@localhost 登录后执行
USE routine_demo;
DELIMITER $$
CREATE PROCEDURE hello_world()
BEGIN
SELECT 'hello' AS msg;
END$$
DELIMITER ;
创建成功只代表例程对象已经存在。要调用它,还必须有 EXECUTE 权限。很多时候业务账号需要调用存储过程,但不应该拥有创建和修改例程的权限。此时管理账号可以单独追加 EXECUTE 授权。
-- 回到管理账号,追加调用权限 GRANT EXECUTE ON routine_demo.* TO 'routine_user'@'localhost'; -- 切换回 routine_user 账号测试调用 CALL routine_demo.hello_world();
如果希望回收创建能力,只保留调用能力,可以使用 REVOKE。回收后,同一个账号再次执行 CREATE PROCEDURE 会收到类似 ERROR 1370 (42000): create routine command denied to user 的错误。
-- 管理账号执行 REVOKE CREATE ROUTINE ON routine_demo.* FROM 'routine_user'@'localhost'; -- 再使用 routine_user 执行创建会失败,报错信息与CREATE ROUTINE权限缺失有关
三、从授权表和information_schema查看CREATE ROUTINE权限状态
权限信息不只在 SHOW GRANTS 里可以看到,它也会落到系统表中。全局权限保存在 mysql.user,数据库级权限保存在 mysql.db。对于只授权到 routine_demo.* 的场景,mysql.user 中的 Create_routine_priv 应该是 N,而 mysql.db 中的 Create_routine_priv 为 Y。
这种分表存储的方式很容易让人误判,因为只看 mysql.user 会以为用户没有权限,但实际在目标数据库上它是有创建例程能力的。所以授权排错时,建议同时查询两张表,或直接使用 information_schema 中的权限视图。
-- 查询用户级权限 SELECT User, Host, Create_routine_priv FROM mysql.user WHERE User = 'routine_user'; -- 查询数据库级权限 SELECT User, Host, Db, Create_routine_priv FROM mysql.db WHERE User = 'routine_user';
下面的语句通过 information_schema.SCHEMA_PRIVILEGES 查看账号在 routine_demo 库上的权限,结果会把 CREATE ROUTINE 和 EXECUTE 都列出来。与系统表相比,这种查询更直观,也不用担心手工连接用户和主机列时出错。
SELECT GRANTEE, TABLE_SCHEMA, PRIVILEGE_TYPE FROM information_schema.SCHEMA_PRIVILEGES WHERE GRANTEE LIKE 'routine_user%';
四、生产环境中的权限拆分实践与常见列外
生产环境不建议给业务账号直接授予 CREATE ROUTINE。存储过程的创建和变更应交给发布系统或专门的变更账号,业务账号只需要 EXECUTE,这样即使业务代码出现漏洞,攻击者也无法通过创建恶意例程来持久化。常见做法是把权限拆成两类:变更账号拥有 CREATE ROUTINE、ALTER ROUTINE 和 DROP,业务账号只拥有 EXECUTE。
-- 业务账号:只负责读写和调用例程 CREATE USER IF NOT EXISTS 'app_user'@'%' IDENTIFIED BY 'AppPass123!'; GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'%'; GRANT EXECUTE ON app_db.* TO 'app_user'@'%'; -- 发布账号:只负责维护存储过程、函数 CREATE USER IF NOT EXISTS 'deploy_user'@'%' IDENTIFIED BY 'DeployPass456!'; GRANT CREATE ROUTINE, ALTER ROUTINE ON app_db.* TO 'deploy_user'@'%';
另一个容易混淆的权限是 ALTER ROUTINE。它只控制修改或删除例程的属性,例如修改存储过程的注释、SQL SECURITY 等,或者重新定义函数行为。很多同学以为有 CREATE ROUTINE 就能改自己的存储过程,实际执行 ALTER PROCEDURE 时却报错,原因就是缺少 ALTER ROUTINE。
创建存储函数时,还会受到 log_bin_trust_function_creators 参数影响。该参数默认开启情况下不允许创建可能不安全的函数,除非创建者拥有 SUPER 权限。如果业务确实需要在主库创建函数,可以在会话级设置该参数,或者严格审查函数是否会破坏主从一致性。这个限制与 CREATE ROUTINE 是两个不同层面的问题,排查时要分开看。
-- 查看当前是否信任函数创建者 SHOW VARIABLES LIKE 'log_bin_trust_function_creators'; -- 在会话级临时允许创建函数 SET GLOBAL log_bin_trust_function_creators = 1;
总结来说,CREATE ROUTINE 是MySQL例程权限体系中的第一道门槛。授权时先明确账号是用于普通业务调用,还是用于发布变更,再决定授予 EXECUTE 还是 CREATE ROUTINE、ALTER ROUTINE。出现 ERROR 1370 时,先查 SHOW GRANTS 和 information_schema.SCHEMA_PRIVILEGES,一般就能定位到问题。这样既满足最小权限原则,也能减少线上误操作。
CREATE ROUTINE权限MySQL存储过程GRANT授权修改时间:2026-09-27 03:00:29