
UDF函数加载失败:检查plugin_dir是否指向安全路径
MySQL默认只从plugin_dir指定目录加载UDF,而这个路径往往不是你放.so/.dll文件的地方。直接把编译好的xxx_udf.so丢进/tmp或用户家目录,执行CREATE FUNCTION一定会报错ERROR 1126 (HY000): Can't open shared library 'xxx_udf.so'。
查当前有效路径:SELECT @@plugin_dir;,常见值是/usr/lib/mysql/plugin/(Linux)或C:\Program Files\MySQL\MySQL Server X.X\lib\plugin\(Windows)。必须把UDF文件复制到该目录,并确保MySQL进程有读+执行权限(Linux下尤其注意SELinux和文件属主)。
- 别用
symlink绕过路径限制——MySQL 5.7+默认禁用符号链接加载 - Windows上.dll需与MySQL位数严格一致(x64服务不能加载x86 dll)
- 修改
plugin_dir需重启mysqld,且不能设为/tmp或/home这类可写目录,否则等于开放任意代码执行
CREATE FUNCTION被拒绝:确认secure_file_priv和用户权限
即使.so文件放对了位置,CREATE FUNCTION仍可能报ERROR 1142 (42000): CREATE FUNCTION command denied。这不是权限配置漏了,而是MySQL在UDF场景下额外校验两个点:
- 执行用户必须拥有
CREATE ROUTINE+INSERT权限(后者用于写mysql.func系统表) -
secure_file_priv值不能为NULL(表示禁用所有文件操作),但它的值不影响UDF加载——这是常见误解;真正影响的是plugin_dir是否在secure_file_priv允许范围内(其实不校验,放心) - MySQL 8.0.19+要求UDF函数名必须以
udf_或mysql_开头,否则报ERROR 1125
授权命令示例:GRANT CREATE ROUTINE, INSERT ON mysql.func TO 'udf_admin'@'localhost';
UDF崩溃mysqld:避免内存越界和非线程安全调用
UDF本质是动态库,直接运行在mysqld进程空间里。一个malloc没配free、一次strcpy越界、或者调用了printf/time这类非异步信号安全函数,都会导致整个MySQL挂掉(日志里只有mysqld got signal 11)。
- 所有内存操作必须用
my_malloc/my_free(MySQL提供的线程安全封装) - 禁止调用
libc的I/O函数(fopen,printf等),日志只能走my_error()或写入表 - 若函数需初始化(如打开文件句柄),必须用
xxx_init函数注册,且检查initid->ptr是否已分配,防止重复初始化 - 调试时先用
valgrind --tool=memcheck --trace-children=yes mysqld ...跑起来,比看core dump快得多
升级MySQL后UDF失效:ABI兼容性断裂很常见
MySQL小版本升级(比如5.7.33→5.7.40)通常兼容,但跨大版本(5.7→8.0)或启用了LOCKED插件机制后,UDF会直接无法加载,错误信息是ERROR 1126: Can't open shared library 'xxx.so': undefined symbol: my_charset_utf8mb4_0900_as_cs这类符号缺失。
根本原因是MySQL内部结构体(如UDF_INIT)、字符集定义、内存管理接口在8.0中重构过。你必须用目标MySQL版本的头文件(mysql.h, my_sys.h)重新编译UDF,且链接对应版本的libmysqlclient。
- 不要复用5.7编译的.so给8.0用,哪怕只是改个
mysql --version输出 - 生产环境建议在Docker里构建:用
mysql:8.0镜像装build-essential和default-libmysqlclient-dev,保证头文件和链接库完全匹配 - 8.0.23+开始支持
INSTALL COMPONENT方式加载更安全的组件,但UDF本身不在此列,别混淆
最麻烦的从来不是怎么写UDF,而是怎么让它活过一次MySQL重启、一次安全补丁、一次运维手抖的apt upgrade。










