关于Mysql中UDF函数的思考(一)

article/2025/9/27 0:35:10

一点背景

从大学二年纪接触编程,几乎我阅读过的所有的编程语言教材都会有那么一章专门讲述数据库编程,而讲述的内容都无非是介绍某个数据库历史,对应的安装过程,最后才会讲解一点SQL语句,像这样的教材我个人认为是完全充字数(特别是嵌入式类别的书籍),这个也是国人出书的一大特色。所以到头来作为研发人员所知道的数据库工程技术也就是创建表删除表,对表的增删改查当然有希望对数据库理解精进的时候,会去查看一些专门的数据库理论的书籍,但是一看到理论中模式和范式的数学语义介绍,必然会望而却步。我也是这样的,让我表达范式介绍,我的确表达不出,但是给我表格组成,我能说明达到哪种范式标准我个人理解作为应用工程师,做到这一步就基本达标了。在最近的项目中,由于需要使用Mysql的UDF(user defined function),这个特性从未使用过,而且个人觉得这个特性以后应该会经常使用,所以写下博文,记录和分享这个特性的用法。

开发环境

操作系统:ubuntu 12.04 Desktop

编译器:gcc 4.6.3

数据库:mysql server 5.5


UDF介绍

官方介绍请点击http://dev.mysql.com/doc/refman/5.5/en/adding-functions.html

UDF是mysql的一个拓展接口,UDF(Userdefined function)可翻译为用户自定义函数,这个是用来拓展Mysql的技术手段


示例业务范围

假设存在这样的业务流程,假设个C/S架构程序如下(不纠结与细节,了解即可):

客户端<----->业务服务器<----->缓存<----->Mysql数据库

正常的升级过程

1.发布新的客户端程序版本

2.版本记录写入Mysql

3.版本记录同步到缓存服务器

4.客户端连接业务服务器

5.业务服务器进行版本升级运算,在运算过程中会向缓存服务器请求最新的版本记录。

6.回复客户端是否存在升级


需要讨论的过程就是在缓存服务器和Mysql数据库的数据同步过程。

在不知道有UDF特性的时候,我们的设计是可以设计一个监控程序,循环监控Mysql的记录是否发生改变,如果发生改变就将缓存发送新的记录,这样的一个监控程序,必然是死循环的方式一直检查记录,这种方式想乃笨重不堪,自然不是上上之策,所以考虑使用其他方式。

对于这种有触发条件的需求,数据库熟手都知道这种记录方式可以使用触发器,但是如何通过触发器将更新的记录同步到缓存(假定缓存采用的是redis)?这个就需要借助UDF特性。

如何使用?

在Mysql的官方文档中有说明如何给Mysql添加新的函数。

添加的函数可分为3种:

1.自行编写函数的源码,将源码编译成动态库,然后使用Mysql的CREATE FUNCTION语句来将函数添加进Mysql。

2.在源码等级上加入自行编写的函数,这样编译之后,新加入的函数就和Native函数一样,永久使用。

3.通过SQL语句脚本来完成函数的功能。

就这3中方式,第三种明显不足以完成我们所需要的业务工作,因为SQL语句不存在连接缓存的功能。第二种方法看似美好,但是对于需要更换缓存方法(从redis更换成memcached),重新编译数据库源码,这种不符合工程化商务化。所以采用第一种方法自行编写函数代码,动态导入函数到mysql之中。

代码如下:文件名test_add.cpp

#include <mysql.h>extern "C" long long testadd(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{int a = *((long long *)args->args[0]);int b = *((long long *)args->args[1]);return a + b;
}extern "C" my_bool testadd_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{return 0;
}


个文件,应该是最小的UDF组成单元了,我们需要测试的就是在Mysql中使用testadd这个函数。

如何将testadd加入mysql之中?

1.生成动态库。

luoqiya@ubuntu:$ g++-shared -fPIC -I /usr/include/mysql-o test_add.sotest_add.cpp

注意:在这个步骤之前确保安装了libmysqlclient-dev.使用gcc生成的共享库文件同样可以使用,这里使用g++是为了体现出extern"C"的必要性

2.将test_add.so加入到Mysql中。

luoqiya@ubuntu:$ cp test_add.so /usr/lib/mysql/plugin

注意:记得使用管理员权限。

3.在Mysql中添加该函数。

以root进入Mysql,执行命令

mysql>CREATE FUNCTION testadd RETURNS INTEGER SONAME 'test_add.so';

添加成功则会显示:Query OK等字样。添加成功之后函数即可使用。

测试函数是否可以使用可以执行命令

mysql>select testadd(1,2);

会出现结果如下图所示:



如此过程,说明mysql已经可以使用我们自定义的函数,结合上面所说的业务流程,创建对应表格的

INSERTDELETE  ,UPDATE触发器在触发器中调用自定义函数将数据传送到缓存中即可满足业务要求。


一点思考


按照上面的步骤创建了自定义函数,已经可以使用UDF函数了,但是这几个步骤可以分析分析,为什么Mysql需要这样设置,这个对于以后设计具备拓展性的程序颇有帮助。如果读者对Mysql的UDF特性还有特殊的功能需求,请参照官方手册,没有什么文章能比手册更加详细。

第一点添加的testadd函数需要采用共享库的方式,这个不难理解,很多模块化的程序都使用dlopen函数族来进行模块的动态加载,比如Apache Web服务器就是用这种方式来动态加载模块,这个是创造可拓展程序的唯一系统级别方案(如果存在其他方式,请告知我,我立即修改)。

第二点,UDF函数对于Mysql来说的表现形式是什么样的?或者换个问法,UDF函数怎样存在与Mysql之中?

这个可以通过查询Msql下的database mysql中的func表格获得。

查询结果如下图:

func表格记录了所有UDF函数的信息,表格中ret代表的是返回值类型,之所以这里记录的是2,是因为我们返回的是整型数据,可以在mysql的源码中sql目录下的mysql_com.h找到类型的枚举定义。

enum Item_result {STRING_RESULT=0, REAL_RESULT, INT_RESULT, ROW_RESULT,DECIMAL_RESULT};

第三点动态加载的安全性问题,在加载的.so文件中的函数存在非法操作(比如free2次指针)会产生什么影响?使用dlopen函数时,可以通过dlsym来获取函数指针,调用.so文件中的方法。这些调用过程是发生在同一个进程之中,所以在dlsym的函数指针调用出现非法操作时,原进程被kill然后核心转储在Mysql之中导入的UDF函数面对同样的问题,是不是Mysql有方法保证Mysql的进程不被kill掉?可以做测试,将上面的test_add.cpp文件改为
#include <mysql.h>
#include <malloc.h>extern "C" long long testadd(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{int a = *((long long *)args->args[0]);int b = *((long long *)args->args[1]);int *p = (int *)malloc(sizeof(int));free(p);free(p);return a + b;
}extern "C" my_bool testadd_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{return 0;
}

按照上面所述步骤,添加函数至数据库,然后调用该函数,结果如下图:

可以发现和 数据库的连接断了,然后到/var/log/mysql/error.log
检查Mysql的错误日志(采用apt-get install安装的方式,my.cnf下没有设置query的路径)
可以发现Mysql
是挂掉,然后又重新启动了(守护进程的存在)
如此可见,Mysql对于动态加入的.so并没有做特殊的保护方式,就和dlopen函数族所提供的一样。之所以不设计特殊
的保护方式,就我的理解而言,要使用外部的.so那么.so的提供者就需要保证安全性,就算.so出现了问题,Mysql

的守护进程也保证了服务快速恢复,对业务影响并不是很大。而且从Mysql在对接口设计上,也有影射需要注意安全性,因为在创建xxx()函数时,至少提供xxx_init()或者xxx_deinit()函数只中一个(Mysql官网的5.1版本中文文档只是说明xxx_init()和xxx_deinit()为可选,而英文版强调至少提供一个,这点看官注意),这两个函数分别对应与调用xxx函数之前的初始化操作和调用完毕xxx函数之后的清理工作。

更加详细的接口介绍,见下一篇。

未完待续$:


http://chatgpt.dhexx.cn/article/HgpVbA36.shtml

相关文章

UDF、UDAF、UDTF之间的区别

1、UDF&#xff1a;用户定义&#xff08;普通&#xff09;函数&#xff0c;只对单行数值产生作用&#xff1b; 继承UDF类&#xff0c;添加方法 evaluate() /*** function 自定义UDF统计最小值**/public class Min extends UDF {public Double evaluate(Double a, Double b) {i…

HIVE udf、udaf、udtf函数定义与用法(最全!!!!!)

一、定义 1、hive udf、udaf、udtf函数定义与用法 &#xff08;1&#xff09;UDF&#xff08;user-defined function&#xff09;作用于单个数据行&#xff0c;产生一个数据行作为输出。&#xff08;数学函数&#xff0c;字符串函数&#xff09; &#xff08;2&#xff09;U…

udf,udaf,udtf之间的区别

1、UDF&#xff1a;用户定义&#xff08;普通&#xff09;函数&#xff0c;只对单行数值产生作用&#xff1b; 继承UDF类&#xff0c;添加方法 evaluate() /*** function 自定义UDF统计最小值* author John**/public class Min extends UDF {public Double evaluate(Double a…

如何编写udf函数(收藏篇)

hive自带了一些函数&#xff0c;比如&#xff1a;max、min 等&#xff0c;但是自带的函数数量有限&#xff0c;所以hive提供给用户自定义函数的功能。 udf 函数可以直接应用于select 语句&#xff0c;对查询结构做格式化处理之后&#xff0c;然后再输出内容。 下面将详细介绍下…

自定义UDF函数

自定义函数案例&#xff1a; 文章目录 自定义UDF函数1.需求2.前期maven工程准备3.编程实现4.导包5.导入hive中 自定义UDTF函数1.需求2.编程实现3.导入hive中 自定义UDF函数 1.需求 自定义一个UDF实现计算给定字符串的长度例如 2.前期maven工程准备 创建一个maven工程&#x…

Hive自定义UDF函数详解

Hive自定义UDF函数详解 一、UDF概述二、UDF种类三、如何自定义UDF四、自定义实现UDF和UDTF4.1 需求4.2 项目pom文件4.3 Hive建表测试及数据4.4 UDF函数编写4.5 UDTF函数编写4.6 UDTF使用 一、UDF概述 UDF全称&#xff1a;User-Defined Functions&#xff0c;即用户自定义函数&…

Hive UDF简单函数

概念 在Hive中&#xff0c;用户可以自定义一些函数&#xff0c;用于扩展HiveQL的功能&#xff0c;而这类函数叫做UDF&#xff08;用户自定义函数)。UDF分为两大类&#xff1a;UDAF&#xff08;用户自定义聚合函数&#xff09;和UDTF&#xff08;用户自定义表生成函数)。  UDF…

大数据Hive篇--UDF函数

什么是UDF: 它是User defined Function的简写&#xff0c;意思是用户自定义方法 为什么要用UDF&#xff1f; hive自带了一些函数&#xff0c;比如&#xff1a;max、min 等&#xff0c;但是自带的函数数量有限&#xff0c;所以hive提供给用户自定义函数的功能。 udf 函数可以…

《C#入门详解》刘铁猛——Lesson20-21事件

其实事件一般就是在界面程序中应用&#xff0c;所以这里讲的不如WPF这种的实用。

《C#入门详解》刘铁猛——Lesson1-2 IDE、各种应用程序

编程学习路径&#xff1a;语言——类库——框架 参考资料&#xff1a;离线MSDN文档&#xff0c;C#语言定义文档&#xff0c;推荐书籍—C# in a nutshell&#xff1b;其中&#xff0c;C#语言定义文档知识点会串的比较多&#xff0c;不建议详读。 使用MSDN&#xff1a;光标选中…

《C#入门详解》刘铁猛——Lesson8-9 方法的定义、调用与调试

方法命名&#xff1a;使用动词或者动词短语&#xff1b;使用PASCAL规则&#xff0c;即所有单词首字母大写。 静态类型的方法不是实例的方法&#xff0c;是类的方法&#xff0c;因此&#xff0c;实例不能调用静态方法&#xff0c;只能用类调用静态方法&#xff0c;示例程序如下&…

《C#入门详解》刘铁猛——Lesson17字段、属性、索引器、常量

属性代码示例&#xff1a; 以上代码演示了字段由get-set方法对演化成为属性的过程。 prop连敲两下Tab键&#xff0c;是属性声明的快捷键。 强调以下&#xff1a; 很少使用索引器。 声明和使用常量&#xff1a;

《C#入门详解》刘铁猛——Lesson27-28类的重写、多态、抽象类、开闭原则

重写、多态——子类对父类的纵向扩展&#xff0c;就是方法的版本升级。 override——重写&#xff0c;子类对父类成员的版本更新。 virtual——override 下面的例子就是多态——多态就是使用一个父类的变量引用一个子类的实例&#xff0c;当调用方法时&#xff0c;会顺着继承链…

《C#入门详解》刘铁猛——Lesson19委托

自定义委托类型&#xff1a; 模板方法实例&#xff1a; 回调方法示例&#xff1a; 多播委托示例&#xff1a; 同步调用&#xff08;串行&#xff0c;单线程&#xff09;示例&#xff1a; 多播委托也是同步调用&#xff1a; 隐式的异步调用示例&#xff1a; 执行结果发生了资源…

《C#入门详解》刘铁猛——Lesson18传值\输出\引用\数组\具名\可选参数、扩展方法

x是101&#xff0c;y是100 在声明函数的时候带有默认值。 静态函数&#xff0c;第一个参数加this修饰符&#xff0c;就是一个扩展方法。调用的时候可以看见向下的小箭头。

刘铁猛C#语言入门详解——学习笔记014、15、16(2)

using System; using System.Collections.Generic; namespace ConsoleApp2 {class Program{static void Main(string[] args){//c#语言对表达式的定义&#xff1a;a sequence of one or more operands and zero or more operators can be evaluated to a single value object m…

《深入浅出WPF》-刘铁猛学习笔记——XAML

这里有个目录 XAML是什么&#xff1f;导人程序集和引用其中的名称空间XAML的树型结构Attribute (特性、标记、属性)Property (属性)属性赋值字符串赋值使用属性元素( Property Element)进行复杂赋值 XAML 注释后记 XAML是什么&#xff1f; XAML是微软公司创造的一种开发语言&a…

《C#入门详解》刘铁猛——Lesson31泛型、partial类、枚举、结构体

泛型类如下&#xff1a; 泛型接口&#xff0c;太常用了&#xff0c;如下&#xff1a; 实现这个泛型接口的类&#xff0c;也是泛型类&#xff0c;如下&#xff1a; 数据结构&#xff0c;基本上都是泛型的。 方法也可以是泛型的&#xff0c;而且泛型方法更常用&#xff0c;泛型方…

《C#入门详解》刘铁猛——Lesson10-11-12 操作符

纵向往下走&#xff0c;优先级依次降低。 []操作符声明数组&#xff1a; typeof操作符的用法&#xff1a; var声明隐式类型变量&#xff1a; new操作符声明实例&#xff1a; delegate——委托&#xff0c;使用该操作符声明一个匿名方法的示例如下&#xff1a; delegate现在已过…