如何实现一个数据库的 UDF?图数据库 NebulaGraph UDF 功能背后的设计与思考

大家好,我是来自 BOSS直聘的赵俊南,主要负责安全方面的图存储相关工作。作为一个从 v1.x 用到 v3.x 版本的忠实用户,在见证 NebulaGraph 发展的同时,也和它一起成长。

BOSS直聘和 NebulaGraph

关于 NebulaGraph 在 BOSS直聘的应用场景,大家可以看看之前文洲老师的文章(图数据库 NebulaGraph 在 BOSS直聘的应用),从那时候文洲老师构建的行为图发展到了安全场景的业务主图、算法推理图、职位相似度图谱等业务,现在更是支持了数仓同学的数据血缘及搜索同学的实时搜索召回场景,单图的规模达到了数千亿。

在图计算方面,BOSS 直聘基于 LPA 和 Louvain 的单度团、多维团,以及基础的离线特征,在安全生产环境中广泛应用图技术。相信未来图在 BOSS直聘还会有更为宽广的舞台。

UDF 的萌生

随着 NebulaGraph 在 BOSS直聘业务上的广泛应用,相对应的对内部技术人员的要求也越来越高。如果技术人员仅仅停留在使用层面,就无法满足从功能到性能很多需求。所以,学习源码成为了必然。

而后迁移 Neo4j->NebulaGraph 过程中,发现业务对 Neo4j 的 UDF 包有所依赖,我本萌生了实现 NebulaGraph UDF 功能的念头。

UDF 设计和实现原理

上图是一条完整 nGQL 语句的执行过程,而 UDF 实现原理同 nGQL 的执行流程相关,大致如下:

graphd 接收到语句 -> Bison 词法解析(切词) -> Flex 语法解析创建 Sentence -> Validator 校验并生成AstContext(抽象语法树) -> toPlan 生成执行计划 Planner -> Optimizer 优化器优化 -> Executor 执行器执行。

在词法语法解析阶段,Function 会被单独解析出来。FunctionManager 作为原生的内置函数管理者,负责函数的定义、加载、调用等操作,从而管理函数的整个生命周期。调用语句通过 FunctionManager 查找到的函数最终会被执行器调用执行。

NebulaGraph 的 UDF 实现基于函数的调用执行流程,增加了 FunctionUdfManager:

static std::unordered_map<std::string, Value::Type> udfFunReturnType_;
static std::unordered_map<std::string, std::vector<std::vector<nebula::Value::Type>>>udfFunInputType_;
std::unordered_map<std::string, FunctionManager::FunctionAttributes> udfFunctions_;class FunctionUdfManager {public:typedef GraphFunction *(create_f)();typedef void(destroy_f)(GraphFunction *);static StatusOr<Value::Type> getUdfReturnType(const std::string functionName,const std::vector<Value::Type> &argsType);static StatusOr<const FunctionManager::FunctionAttributes> loadUdfFunction(std::string functionName, size_t arity);static FunctionUdfManager &instance();FunctionUdfManager();private:static create_f *getGraphFunctionClass(void *func_handle);static destroy_f *deleteGraphFunctionClass(void *func_handle);void addSoUdfFunction(char *funName, const char *soPath, size_t i, size_t i1, bool b);void initAndLoadSoFunction();
};

它主要做以下几件事:

  1. 和 FunctionManager 一起初始化,initAndLoadSoFunction 开启定时扫描,扫描 --udf_path 路径下文件;
  2. loadUdfFunction加载 .so 文件,实例化函数方法,以函数名为 key 保存在 Map 中;
  3. 在启用 UDF 功能的情况下,FunctionManager 未查找函数时,查找并调用 FunctionUdfManager Map 中的函数。

实现比较简单,可以说是取巧了,有需要的话 UDAF 也可用类似方式实现。

UDF 使用方法

下面来讲讲 NebulaGraph UDF 的具体使用,如果你是用 NebulaGraph v3.5.0+ 版本的话,就可以按照以下方式使用 UDF 功能了。如果你是 v3.4.x 及以下版本,UDF 功能是暂不支持的,你也可以 cherry-pick 这个 pr 自行编译使用 UDF 功能。

第一步,在 graphd 配置文件中开启 UDF 功能并指定包目录

# enable udf, c++ only
--enable_udf=true
# set the directory where the .so of udf are stored
--udf_path=/home/foobar/dev/nebula/udf/

第二步,编写自定义函数代码,继承 GraphFunction。GraphFunction 的结构如下:

class GraphFunction;extern "C" GraphFunction *create();
extern "C" void destroy(GraphFunction *function);class GraphFunction {public:virtual ~GraphFunction() = default;virtual char *name() = 0;virtual std::vector<std::vector<nebula::Value::Type>> inputType() = 0;virtual nebula::Value::Type returnType() = 0;virtual size_t minArity() = 0;virtual size_t maxArity() = 0;virtual bool isPure() = 0;virtual nebula::Value body(const std::vector<std::reference_wrapper<const nebula::Value>> &args) = 0;
};
  • create、destroy 是函数的创建销毁方法;
  • name 调用时的函数名;
  • inputType、returnType 输入输出类型;
  • minArity、maxArity 参数数量;
  • isPure 函数是否有状态;
  • body 函数的实现。

第三步,编写好的函数打包成(.so)文件,放到配置文件 --udf_path 配置的对应目录下,graphd 服务会定时(5 分钟)扫描该路径下的包,加载到函数库中。之后,就可以在自己的语句中调用对应的函数了。

⚠️ 注意:由于 graphd 只扫描本地路径下的函数包,想让多个 graphd 都生效,必须都在本地路径下有相应的包。

这里要 cue 下思为老师,感谢他补充的完整使用文档和编译环境:https://github.com/vesoft-inc/nebula/pull/4804 。

UDF 尚未解决的问题

虽然目前 UDF 是能用,但是它还存在部分优化问题。比如:

  1. so 包位置只支持扫描本地;
  2. 函数只在 graphd 层,无法下推到存储;
  3. 使用麻烦,需要用户编码。

当然这些问题和一开始的设计息息相关:开发 UDF 之初,其实是想兼容 C++ 的 so 包和 Java 的 jar 包,但测试了 C++ Jni 调用 Java 的性能,发现基本上无法用于大规模的生产。

下图便是当时的性能测试:

因为实现实在是性能堪忧,于是就放弃了一开始的设计。

当然还有一些未来规划上的事情,主要是希望 NebulaGraph 开发团队一起合作完成:

  1. 个别的大查询语句和深度查询,容易把 storaged 的内存打满影响集群整体性能。是否可以考虑通过查询时间超时或内存监控自动 kill 对应的查询,释放掉内存。其实对于类似的语句,基本上已经很难拿到结果了,更多的可能是想降低语句带来的影响
  2. 集群的容错性,多副本情况下某个节点的非正常下线会影响整体集群,由于环境的复杂性具体定位分析也比较困难,盼望尽可能增强集群健壮性。

开发 UDF 的意外收获

前面说过,UDF 其实是阅读 NebulaGraph 源码的产物。这里我想谈谈我对源码阅读感受:整体的 NebulaGraph 源码给我最直观的感受就是层次、结构清晰,代码优雅。在配合官方博客提供的内核讲解系列文章,对我这种跨语言学习的选手难度都大大降低了。

希望 UDF 能帮你解决一些问题,以及我的分享能给你带来一丝启发。


谢谢你读完本文 (///▽///)

如果你想尝鲜图数据库 NebulaGraph,记得去 GitHub 下载、使用、(з)-☆ star 它 -> GitHub;和其他的 NebulaGraph 用户一起交流图数据库技术和应用技能,留下「你的名片」一起玩耍呀~

2023 年 NebulaGraph 技术社区年度征文活动正在进行中,来这里领取华为 Meta 60 Pro、Switch 游戏机、小米扫地机器人等等礼品哟~ 活动链接:https://discuss.nebula-graph.com.cn/t/topic/13970

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/77564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

9.13 | day 6 |day 45| to 完全平方数

● 70. 爬楼梯 &#xff08;进阶&#xff09; class Solution {public int climbStairs(int n) {int[] dp new int[n1];//设置背包容量&#xff1a;n个int m 2;//有两个物品&#xff0c;注意这是一个完全背包问题dp[0] 1;//initialize ​for(int i 1;i<n;i){//遍历背包f…

快速幂 c++

一般大家写都是 int ans 1; for (int i 1; i < a; i )ans * x;时间复杂度 但是这对于我们还不够&#xff0c;我们要 首先我们得知道一个数学知识 那么求 就有以下递归式 a 能被2整除 a 不能被2整除 (这里a/2是整除) 所以每次都调用 不就是么 最后补充一个东西…

.net core 上传文件大小限制

微软官网文档中给的解释是.net core 默认上传文件大小限制是30M&#xff0c;所以即便你项目里没有限制&#xff0c;这里也有个默认限制。 官网链接地址 总结了一下解决办法&#xff1a; 1.首先项目里添加一个web.config自定义配置文件 在配置文件中加上这段配置 <!--//…

Anomalib实战之一:自定义数据集

Anomalib支持多种格式的数据集&#xff0c;包括MVTec AD和BeanTech等最先进的异常检测基准数据集。对于希望在自定义数据集上使用该库的用户&#xff0c;anomalib还提供了一个Folder datamodule&#xff0c;可以从文件系统的文件夹中加载数据集。本文的目的是使用Folder datamo…

SQL 注入漏洞攻击

文章目录 1. 介绍2. 无密码登录3. 无用户名无密码登录4. 合并表获取用户名密码 1. 介绍 假设你用自己的用户名和密码登录了一个付费网站&#xff0c;网站服务器就会查询一下你是不是 VIP 用户&#xff0c;而用户数据都是放在数据库中的&#xff0c;服务器通常都会向数据库进行查…

SprimgMVC增删改查·

1.项目创建 在IDEA创建一个maven项目 2.项目依赖 pom.xml <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.ap…

二维多孔介质图像的粒度分布研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

(echarts)解决echarts图表适配窗口的大小

(echarts)解决echarts图表适配窗口的大小 解决方法&#xff1a; // 为窗口加上宽度变化事件&#xff0c;当宽高发生改变时&#xff0c;调用echarts的resize()方法&#xff0c;调整图表尺寸 window.addEventListener("resize", function () {myChart.resize(); });示…

什么是“path”环境变量?path的作用是什么?

必应回答&#xff1a; path的作用是指定系统在执行命令或程序时&#xff0c;可以在哪些文件夹中查找。path是一种环境变量&#xff0c;它的值是由多个文件夹路径组成的&#xff0c;用分号分隔。例如&#xff0c;如果path的值是C:\Windows;C:\Python;C:\Program Files\Java\bin…

Gartner 2023API管理市场指南重磅发布,得帆云iPaaS标杆入榜

中国API管理-市场指南 Market Guide for API Management, China 是由全球最具权威的IT咨询与研究机构Gartner发布、聚焦中国API管理市场领域的专业研究报告&#xff0c;通过对市场概况以及代表厂商的分析&#xff0c;为企业决策者提供重要依据与参考。 得帆云iPaaS融合集成平台…

FPGA-结合协议时序实现UART收发器(四):串口驱动模块uart_drive、例化uart_rx、uart_tx

FPGA-结合协议时序实现UART收发器&#xff08;四&#xff09;&#xff1a;串口驱动模块uart_drive、例化uart_rx、uart_tx 串口驱动模块uart_drive、例化uart_rx、uart_tx&#xff0c;功能实现 文章目录 FPGA-结合协议时序实现UART收发器&#xff08;四&#xff09;&#xff1…

蓝桥杯官网练习题(灌溉)

题目描述 小蓝负责花园的灌溉工作。 花园可以看成一个 n 行 m 列的方格图形。中间有一部分位置上安装有出水管。 小蓝可以控制一个按钮同时打开所有的出水管&#xff0c;打开时&#xff0c;有出水管的位置可以被认为已经灌溉好。 每经过一分钟&#xff0c;水就会向四面扩展…

【MySQL系列】MySQL的用户管理

「前言」文章内容大致是MySQL的用户管理。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、用户管理1.1 用户信息1.2 创建新用户1.3 删除用户1.4 修改用户密码 二、数据库的权限2.1 给用户授权2.2 回收用户权限 一、用户管理 MySQL与Linux类似&#x…

NAT(网络地址转换)

文章目录 一、产生背景二、公有地址和私有地址三、定义四、分类五、常用命令 首先可以看下思维导图&#xff0c;以便更好的理解接下来的内容。 一、产生背景 IPv4公网地址资源耗尽&#xff1a; 由于IPv4地址空间有限&#xff0c;公网IPv4地址资源逐渐耗尽&#xff0c;导致难以分…

基于Android的生鲜农产品商城交易设计与实现

摘 要 人们生活水平随着发展不断的提升&#xff0c;人们对生鲜产品消费比越来越依赖&#xff0c;都希望吃到新鲜的食品。消费的加大给生鲜了全新的供应链及销售模式&#xff0c;那种传统的生鲜配送模式也在发生着变化。生鲜系统电商平台在我国目前是属于盛行的电商行业&#x…

分布式锁的3种实现!附代码

分布式锁是一种用于保证分布式系统中多个进程或线程同步访问共享资源的技术。同时它又是面试中的常见问题&#xff0c;所以我们本文就重点来看分布式锁的具体实现&#xff08;含实现代码&#xff09;。 在分布式系统中&#xff0c;由于各个节点之间的网络通信延迟、故障等原因…

如何将pdf文件变小?三招学会pdf文件压缩

在日常工作和生活中&#xff0c;我们常常需要处理大量的PDF文件&#xff0c;然而&#xff0c;有时候这些文件的大小会成为问题&#xff0c;比如文件太大无法通过邮件发送、在线上传&#xff0c;或者在手机上打开时读取缓慢等&#xff0c;为了解决这些问题&#xff0c;我们需要将…

PN结解释

基本原理 PN结由P和N组成 硅掺杂硼&#xff0c;缺少电子&#xff0c;显正电&#xff0c;就是P&#xff08;Positive&#xff09; 硅掺杂磷&#xff0c;多出电子&#xff0c;显负电&#xff0c;就是N&#xff08;Negative&#xff09; 将P和N拼接 左边代表游离的电子&#xf…

flex布局学习笔记

flex布局 推荐网址&#xff1a;弹性框完整指南 |CSS-Tricks - CSS-Tricks 基础知识和术语 由于flexbox是一个完整的模块&#xff0c;而不是一个单一的属性&#xff0c;它涉及很多事情&#xff0c;包括它的整套属性。其中一些应该在容器&#xff08;父元素&#xff0c;称为“…

这些嵌入式系统安全性的知识你需要了解

这可能是 工程师在面对嵌入式系统应用程序的安全性时可能提出的第一个问题。 不幸的是&#xff0c;有大量的“安全软件包”可用&#xff0c;并且对安全性不熟悉的嵌入式工程师可能只将安全性称为加密或病毒防护。尽管加密是安全性的一种工具&#xff0c;而病毒扫描程序从技术上…