Apache Doris 原生C++ UDF之Coding(2)

简介: Apache Doris 原生C++ UDF之Coding(2)

一、环境信息

1.1 硬件信息

  1. CPU :4C
  2. CPU型号:x64(AVX2)
  3. 内存 :10GB
  4. 硬盘 :66GB SSD

1.2 软件信息

  1. Linux版本 :CentOS-7
  2. Apahce Doris版本 :0.15-release
  3. CodeBlocks版本:20.03mingw

二、自定义TIME_TO_SEC函数

实现传入一个时间参数,将其时间部分转换成秒的UDF。

2.1 源码开发 & 实现一

2.1.1 测试主函数

//time_to_sec 的语法格式
//  TIME_TO_SEC(time)
//语法格式说明
//time:传入时间,如果传入了日期部分,也不会管,只将时间部分转换成秒
//重点:是指将传入的时间转换成距离当天00:00:00的秒数,00:00:00为基数,等于 0 秒
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int time_to_sec(string text)
{
    // clear other str
    regex r("^((?![0-9]{2}:[0-9]{2}:[0-9]{2}).)*");
    string time = regex_replace(text, r, "");
    cout << time << endl;
    // handle abnormal
    if(time.length() != 8)
        return NULL;
    // get hh mm ss
    int HH = atoi(time.substr(0,2).c_str());
    int MM = atoi(time.substr(3,2).c_str());
    int SS = atoi(time.substr(6,2).c_str());
    // return sum sec
    return HH*3600 + MM*60 + SS;
}
int main()
{
    cout<<time_to_sec("1987-01-01 00:39:38")<<endl;
    return 0;
}

2.1.2 UDF头文件

C++
#pragma once
#include "udf.h"
#include <bits/stdc++.h>
namespace doris_udf {
IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time);
/// --- Prepare / Close Functions ---
/// ---------------------------------
/// The UDF can optionally include a prepare function. The prepare function is called
/// before any calls to the UDF to evaluate values.
void AddUdfPrepare(FunctionContext* context, FunctionContext::FunctionStateScope scope);
/// The UDF can also optionally include a close function. The close function is called
/// after all calls to the UDF have completed.
void AddUdfClose(FunctionContext* context, FunctionContext::FunctionStateScope scope);
}

2.1.3 UDF源文件

C++
#include "time_to_sec.h"
namespace doris_udf {
IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time) {
    // handle null
    if (time.is_null) {
        return IntVal::null();
    }
    // clear other str
    using namespace std;
    const string timestr((char *)time.ptr);
    const regex r("^((?![0-9]{2}:[0-9]{2}:[0-9]{2}).)*");
    const string replace_str = "";
    string hms_time = regex_replace(timestr, r, replace_str);
    // handle str abnormal
    if(hms_time.length() != 8) {
        return IntVal::null();
    }
    // get hh mm ss
    int HH = atoi(hms_time.substr(0,2).c_str());
    int MM = atoi(hms_time.substr(3,2).c_str());
    int SS = atoi(hms_time.substr(6,2).c_str());
    // return sum sec
    return HH*3600 + MM*60 + SS;
}
/// --- Prepare / Close Functions ---
/// ---------------------------------
void AddUdfPrepare(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
void AddUdfClose(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
}

2.1.4 实现方式一小结

不建议使用,doris对其中的regex相关函数并不友好,会直接导致be所有节点crash。

2.2 源码开发 & 实现二

2.2.1 测试主函数

C++
//time_to_sec 的语法格式
//  TIME_TO_SEC(time)
//语法格式说明
//time:传入时间,如果传入了日期部分,也不会管,只将时间部分转换成秒
//重点:是指将传入的时间转换成距离当天00:00:00的秒数,00:00:00为基数,等于 0 秒
#include <iostream>
#include <string>
#include <regex>
using namespace std;
int time_to_sec(string text)
{
   // clear other str
   string segSign = ":";
   string::size_type pos1 = text.find(segSign);
   if(pos1 == string::npos)
      cout << "没找到!" << endl;
   else
      cout << "找到了!下标:" << pos1<<endl;
    string time = text.substr(pos1-2,8);
    cout << time << endl;
    // handle abnormal
    if(time.length() != 8)
       return NULL;
    // get hh mm ss
    int HH = atoi(time.substr(0,2).c_str());
    int MM = atoi(time.substr(3,2).c_str());
    int SS = atoi(time.substr(6,2).c_str());
    // return sum sec
    return HH*3600 + MM*60 + SS;
}
int main()
{
    cout<<time_to_sec("1987-01-01 00:39:38")<<endl;
    return 0;
}

2.2.2 UDF头文件

C++
#pragma once
#include "udf.h"
#include <bits/stdc++.h>
namespace doris_udf {
IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time);
/// --- Prepare / Close Functions ---
/// ---------------------------------
/// The UDF can optionally include a prepare function. The prepare function is called
/// before any calls to the UDF to evaluate values.
void AddUdfPrepare(FunctionContext* context, FunctionContext::FunctionStateScope scope);
/// The UDF can also optionally include a close function. The close function is called
/// after all calls to the UDF have completed.
void AddUdfClose(FunctionContext* context, FunctionContext::FunctionStateScope scope);
}

2.2.3 UDF源文件

C++
#include "time_to_sec.h"
namespace doris_udf {
IntVal TIME_TO_SEC(FunctionContext* context, const StringVal& time) {
    // handle null
    if (time.is_null) {
        return IntVal::null();
    }
    // clear other str
    using namespace std;
    string timestr((char *)time.ptr);
    string segSign = ":";
    string::size_type pos = timestr.find(segSign);
    string hms_time;
    if(pos == string::npos)
        return IntVal::null();
     else
        hms_time = timestr.substr(pos-2,8);
    // handle str abnormal
    if(hms_time.length() != 8) {
        return IntVal::null();
    }
    // get hh mm ss
    int HH = atoi(hms_time.substr(0,2).c_str());
    int MM = atoi(hms_time.substr(3,2).c_str());
    int SS = atoi(hms_time.substr(6,2).c_str());
    // return sum sec
    IntVal result;
    result.val = HH*3600 + MM*60 + SS;
    return {result.val};
}
/// --- Prepare / Close Functions ---
/// ---------------------------------
void AddUdfPrepare(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
void AddUdfClose(FunctionContext* context, FunctionContext::FunctionStateScope scope) {}
}

2.2.4 实现方式二小结

基本完全使用字符串的API实现,简单高效并且兼容性较好,最终选定实现二。

三、编译结果

四、函数使用

4.1 创建 UDF 函数

CREATE FUNCTION 
TIME_TO_SEC(String) 
RETURNS INT PROPERTIES ( 
"symbol" = "_ZN9doris_udf11TIME_TO_SECEPNS_15FunctionContextERKNS_9StringValE",
"object_file" = "http://10.192.119.68:8088/udf/udf_samples/build/src/udf_samples/libtime_to_sec.so" );

4.2 使用UDF 函数

原先不兼容TIME_TO_SEC的Tableau固化SQL,现在可以正常运行。

五、总结

  • 自定义C++ UDF 的使用与普通的函数方式一致,唯一的区别在于,内置函数的作用域是全局的,而 UDF 的作用域是 DB 内部
  • 1.2后的新版本不建议使用原生C++ UDF,因为兼容性较差、GLIBC一升级就没法用了;建议使用JAVA UDF

Apache Doris 自定义C++ UDF的Coding至此结束,查阅过程中若遇到问题欢迎留言交流

目录
打赏
0
0
0
0
3
分享
相关文章
天翼云:Apache Doris + Iceberg 超大规模湖仓一体实践
天翼云基于 Apache Doris 成功落地项目已超 20 个,整体集群规模超 50 套,部署节点超 3000 个,存储容量超 15PB
天翼云:Apache Doris + Iceberg 超大规模湖仓一体实践
云原生时代的架构革新,Apache Doris 存算分离如何实现弹性与性能双重提升
随着云基础设施的成熟,Apache Doris 3.0 正式支持了存算分离全新模式。基于这一架构,能够实现更低成本、极致弹性以及负载隔离。本文将介绍存算分离架构及其优势,并通过导入性能、查询性能、资源成本的测试,直观展现存算分离架构下的性能表现,为读者提供具体场景下的使用参考。
云原生时代的架构革新,Apache Doris 存算分离如何实现弹性与性能双重提升
数据无界、湖仓无界,Apache Doris 湖仓一体典型场景实战指南(下篇)
Apache Doris 提出“数据无界”和“湖仓无界”理念,提供高效的数据管理方案。本文聚焦三个典型应用场景:湖仓分析加速、多源联邦分析、湖仓数据处理,深入介绍 Apache Doris 的最佳实践,帮助企业快速响应业务需求,提升数据处理和分析效率
数据无界、湖仓无界,Apache Doris 湖仓一体典型场景实战指南(下篇)
数据无界、湖仓无界, Apache Doris 湖仓一体解决方案全面解读(上篇)
湖仓一体架构融合了数据湖的低成本、高扩展性,以及数据仓库的高性能、强数据治理能力,高效应对大数据时代的挑战。为助力企业实现湖仓一体的建设,Apache Doris 提出了数据无界和湖仓无界核心理念,并结合自身特性,助力企业加速从 0 到 1 构建湖仓体系,降低转型过程中的风险和成本。本文将对湖仓一体演进及 Apache Doris 湖仓一体方案进行介绍。
数据无界、湖仓无界, Apache Doris 湖仓一体解决方案全面解读(上篇)
从 ClickHouse 到 Apache Doris:在网易云音乐日增万亿日志数据场景下的落地
日志数据已成为企业洞察系统状态、监控网络安全及分析业务动态的宝贵资源。网易云音乐引入 Apache Doris 作为日志库新方案,替换了 ClickHouse。解决了 ClickHouse 运维复杂、不支持倒排索引的问题。目前已经稳定运行 3 个季度,规模达到 50 台服务器, 倒排索引将全文检索性能提升7倍,2PB 数据,每天新增日志量超过万亿条,峰值写入吞吐 6GB/s 。
从 ClickHouse 到 Apache Doris:在网易云音乐日增万亿日志数据场景下的落地
金融场景 PB 级大规模日志平台:中信银行信用卡中心从 Elasticsearch 到 Apache Doris 的先进实践
中信银行信用卡中心每日新增日志数据 140 亿条(80TB),全量归档日志量超 40PB,早期基于 Elasticsearch 构建的日志云平台,面临存储成本高、实时写入性能差、文本检索慢以及日志分析能力不足等问题。因此使用 Apache Doris 替换 Elasticsearch,实现资源投入降低 50%、查询速度提升 2~4 倍,同时显著提高了运维效率。
金融场景 PB 级大规模日志平台:中信银行信用卡中心从 Elasticsearch 到 Apache Doris 的先进实践
Apache Doris 3.0.4 版本正式发布
该版本持续在存算分离、湖仓一体、异步物化视图等方面进行改进提升与问题修复
Apache Doris 2.1.8 版本正式发布
该版本持续在湖仓一体、异步物化视图、查询优化器与执行引擎、存储管理等方面进行改进提升与问题修复,进一步加强系统的性能和稳定性,欢迎大家下载体验。
Apache Doris 创始人:何为“现代化”的数据仓库?
3.0 版本是 Apache Doris 研发路程中的重要里程碑,他将这一进展总结为“实时之路”、“统一之路”和“弹性之路”,详细介绍了所对应的核心特性的设计思考与应用价值,揭晓了 2025 年社区发展蓝图
Apache Doris 创始人:何为“现代化”的数据仓库?
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!
183 1
别让你的CPU打盹儿:Apache Doris并行执行原理大揭秘!

推荐镜像

更多