实现高效写入:Schemaless 写入性能优化指南

物联网应用常常需要收集大量的数据,用以支持智能控制、业务分析和设备监控等功能。然而,应用逻辑的更新或硬件的调整可能会导致数据采集项频繁变化,这是时序数据库(Time Series Database,TSDB)面临的一大挑战。

为了适应这种动态变化,TDengine 提供了一种无需预先定义表结构的 Schemaless 写入模式。这种模式允许开发者直接通过写入接口送入数据,系统会自动建立对应的数据存储结构。在数据结构需要调整时,Schemaless 模式也能自动添加新的数据列,确保所有数据都能被准确记录。如果大家想要详细了解 Schemaless 写入的主要处理逻辑、映射规则与变更处理等机制,可以进入 TDengine 官网技术文档直接查看(Schemaless 写入 | TDengine 文档 | 涛思数据)。

TDengine 对 Schemaless 写入功能进行了深入优化,以提高其灵活性和效率。本文将介绍其中部分优化措施的详细过程和技术方法,帮助开发者更好地理解和利用这一功能,从而提升应用性能。同时,我们也希望这些分享能帮助开发人员在性能优化方面迈向新的高度。

性能优化流程

分析性能瓶颈在哪里?

通过分析下文中数据写入的火焰图,我们会发现 parser 部分和 insert 部分占用的耗时都很高,并且主要集中在 parseSmlKey, parseSmlValue, addChildTableDataPointsIntoSql, taos_query_a, buildDataPointSchema 几个函数中。

物联网应用常常需要收集大量的数据,用以支持智能控制、业务分析和设备监控等功能。然而,应用逻辑的更新或硬件的调整可能会导致数据采集项频繁变化,这是时序数据库(Time Series Database,TSDB)面临的一大挑战。

为了适应这种动态变化,TDengine 提供了一种无需预先定义表结构的 Schemaless 写入模式。这种模式允许开发者直接通过写入接口送入数据,系统会自动建立对应的数据存储结构。在数据结构需要调整时,Schemaless 模式也能自动添加新的数据列,确保所有数据都能被准确记录。如果大家想要详细了解 Schemaless 写入的主要处理逻辑、映射规则与变更处理等机制,可以进入 TDengine 官网技术文档直接查看(Schemaless 写入 | TDengine 文档 | 涛思数据)。

TDengine 对 Schemaless 写入功能进行了深入优化,以提高其灵活性和效率。本文将介绍其中部分优化措施的详细过程和技术方法,帮助开发者更好地理解和利用这一功能,从而提升应用性能。同时,我们也希望这些分享能帮助开发人员在性能优化方面迈向新的高度。

性能优化流程

分析性能瓶颈在哪里?

通过分析下文中数据写入的火焰图,我们会发现 parser 部分和 insert 部分占用的耗时都很高,并且主要集中在 parseSmlKey, parseSmlValue, addChildTableDataPointsIntoSql, taos_query_a, buildDataPointSchema 几个函数中。

性能瓶颈如何解决?

针对上面火焰图分析的情景,对于每个函数,我们有两种方法来优化——要么去掉要么缩短。

如何去掉?能否去掉?

首先我们需要先详细了解现有的解析处理框架。下图是现有框架的处理流程图:

从图中我们能够发现存在一些不合理的地方,如下:

  • 行协议解析析(Parser)

在处理数据时,系统会遍历每条记录,提取出测量值(measure)、标签(tag)以及列(col)的键值对,并将这些信息存储于定制的结构体中。此外,系统还会对每条记录中的标签键进行排序,并依照既定规则生成对应的子表名称。然而,此流程中对标签的解析、排序及子表名称生成的重复执行,增加了处理时间和计算复杂度。

  • schema 处理

在获取测量值(measure)的架构元数据(schema meta)之后,系统会对该测量值下的每一条数据执行以下操作:遍历其标签(tag)和列(col),并检查现有架构是否需要更新,更新操作可能包含 create stable, add col, add tag, modify col, modify tag。

  • insert 数据

当数据量小于 10 条时,系统将逐条构造 SQL 语句,并通过调用taos_query函数逐一插入数据。对于超过 10 条的数据集,系统将构建stmt结构体,并利用stmt接口批量插入数据。

  • 一些关键的代码如下图。(主要函数为 tscParseLine/parseSmlKvPairs/tscSmlInsert/buildDataPointSchemas/modifyDBSchemas/applyDataPointsWithSqlInsert 等)

详细代码请见如下链接:

  • https://github.com/taosdata/TDengine/blob/2.6/src/client/src/tscParseLineProtocol.c

  • https://github.com/taosdata/TDengine/blob/2.6/src/client/src/tscParseOpenTSDB.c

针对以上问题,我们首先可以从架构上面进行大的优化。

主要包括如下几个方面:

  • 行协议解析

    • 分析数据可以看出,相同的标签(tag)前缀通常保持一致,所以可以对数据的标签字符串进行预分组,将具有相同前缀的标签归入同一组。在每个分组内,只需对标签进行一次解析,减少重复解析的操作。

    • 检查数据的顺序,如果顺序相同,则可以直接按照顺序进行数据绑定,避免使用哈希查找,从而减少计算量和提高处理速度。

  • schema 处理

    • 提前在解析时判断是否需要变更架构(schema),检查是否存在列的增加或列长度的变更。如果需要变更,再走修改架构(modify schema)的逻辑。

  • insert 数据

    • 数据构造直接在解析每行时实现,直接将数据绑定到最终的 STableDataCxt 结构中。这种做法可以避免在后续阶段再次进行数据绑定和二次拷贝。

    • 改进数据写入方法,采用直接构造 BuildRow 的方式,避免使用 SQL 或 stmt 接口而产生二次解析。

  • 一些关键的代码如下图。(主要函数为smlParseInfluxString/smlParseTagKv/smlParseColKv/smlParseLineBottom/smlModifyDBSchemas/smlInsertData)

详细代码见如下链接:

  • https://github.com/taosdata/TDengine/blob/3.0/source/client/src/clientSml.c

  • https://github.com/taosdata/TDengine/blob/3.0/source/client/src/clientSmlLine.c

  • https://github.com/taosdata/TDengine/blob/3.0/source/client/src/clientSmlTelnet.c

  • https://github.com/taosdata/TDengine/blob/3.0/source/client/src/clientSmlJson.c

在架构优化过程中,我们还发现内存使用也可以进行优化。

在数据解析过程中,将数据转换为无架构(schemaless)格式时,对测量值(measure)、标签(tag)和列(col)进行了大量的内存分配和拷贝操作。这些操作虽然常见,但并非必要,存在优化空间:

  • 在解析数据时,不直接进行数据的拷贝和内存分配。而是记录下每个数据项在原始数据中的指针位置和长度。

  • 在数据需要最终写入数据库前,再根据这些记录的位置和长度信息,从原始数据中直接拷贝相关数据。

如下:

 

st,t1=3,t2=4,t3=t3 c1=3i64,c3="passit",c2=false,c4=4 1626006833639000000

1)t1/t2/t3/c1/c3/c2 全部 calloc,拷贝到新的内存

 

typedef struct { char* key; uint8_t type; uint16_t length; char* value; uint32_t fieldSchemaIdx; } TAOS_SML_KV;

2)直接指针记录位置,避免内存分配拷贝

 

typedef struct { char *measure; char *tags; char *cols; char *timestamp; char *measureTag; int32_t measureLen; int32_t measureTagsLen; int32_t tagsLen; int32_t colsLen; int32_t timestampLen; SArray colArray; } SSmlLineInfo;

如何缩短?

  • 时间精度转换优化

直接寻址代替条件判断,注意溢出的处理。

 

if(fromPrecision == ){} else if( fromPrecision == ){} else {} int64_t smlToMilli[3] = {3600000LL, 60000LL, 1000LL}; int64_t unit = smlToMilli[fromPrecision - TSDB_TIME_PRECISION_HOURS]; if (unit > INT64_MAX / tsInt64) { return -1; } tsInt64 *= unit;

  • Json 解析优化

因为写入的 Json 数据格式基本固定,所以可以提前计算出各个元素(metric, timestamp, value, tags)的偏移位置,直接偏移到相应位置处理。

 

[ { "metric": "meter_current", "timestamp" : 1346846400, "value": 18, "tags": { "location": "上海", "id": "d1001" } }, {} ]

  • 判断逻辑优化

每次判断最大限度的减少信息熵,将概率最大的 if 条件放在前面判断。(i64 / u64 / i8 / u8 / true / L"" )

 

if( str equals "i64"){} else if( str equals "i32"){} else if( str equals "u8"){} ··· if(str[0] equals "i"){} else if(str[0] equals "u"){} ...

其他优化

  • likely / unlikely

根据代码执行的概率,指导编译器优化指令流水线的加载顺序。

 

if(unlikely(len == 0 || (len == 1 && data[0] == '0'))){ return taosGetTimestampNs()/smlFactorNS[toPrecision]; }

  • 程序日志频繁打印,也会导致性能下降。我们在测试中发现,每批的统计信息打印会导致耗时占用上升 10% 左右。

性能优化对比

版本

sql

line协议

telnet协议

json协议

2.6(4ec22e8)

4543622

1458304

2161855

1272000

ver-3.0.0.0

1638498

1650033

1945982

800000

3.0(f6793d5)

3740774

3602947

4328447

5520000

速度单位为 records/second

在 TDengine 3.0 版本与 2.6 版本的对比中,可以看到,line 协议性能提升了 2.5 倍,telnet 提升了 2 倍,而 json 的提升接近 5 倍。

性能分析工具

火焰图

perf 工具

通过 perf top -p 111290 -g 分析,可以看到 CPU 占用率很高

结语

我们在进行性能优化前,首先需全面了解系统的流程和架构,确保对其有深刻认识。性能优化应着重关注以下三个方面:

  1. 架构评估:优秀的性能建立在合理且高效的架构之上。首先检查架构设计的合理性和效率,这是提升性能的基础。

  2. 识别并解决性能瓶颈:在良好的架构基础上,应优先解决主要的性能瓶颈,逐步优化次要问题,以实现持续的性能提升。

  3. 性能分析方法:利用火焰图分析各函数的调用耗时(宽度),对于一些看不到调用栈的地方可以添加日志打印每步骤的耗时,以精确地识别和分析性能瓶颈。

通过综合考虑 CPU、存储、网络、编译器及硬件等多个方面,并结合程序本身的特点,大家就可以实现有效的性能优化了。最后,希望本篇文章能够帮助到有需要的开发者,也欢迎大家体验 TDengine 的 Schemaless 写入性能。

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

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

相关文章

vue中自定义设置多语言,并且运行js脚本自动生成多语言文件

在项目中需要进行多个国家语言的切换时,可以用到下面方法其中一个 一、自定义设置多语言 方法一: 可以自己编写一个设置多语言文件 在项目新建js文件,命名为:language.js,代码如下 // language.js 文档 let languagePage {CN…

红酒与舞蹈:舞动的味觉艺术

在艺术的海洋中,红酒与舞蹈总是能激起人们心中较温柔的涟漪。红酒以其深邃的色泽、馥郁的香气,诠释着味觉的艺术;而舞蹈,则以优雅的姿态、灵动的步伐,演绎着视觉的盛宴。当红酒遇上舞蹈,一场别开生面的艺术…

代码随想录Day66(图论Part03)

101.孤岛的总面积 题目:101. 孤岛的总面积 (kamacoder.com) 思路:无 答案 import java.util.Scanner;class Main {private static int N, M;private static int[][] grid;private static boolean[][] visited;private static boolean touchesEdge;publi…

少见的更优写法,反转字符串中的元音字母

Leetcode 原题链接 解法一 这道题很简单,令双指针 l l l 和 r r r 从两侧相向移动,交换元音字母即可。但大多人的实现是如下这种可简化的嵌套循环。 如果是 Java 等 String 不可变的语言,应先转换为 CharArray,交换完元音字母…

数据库相关学习记录

oracle如何对A-数字-数字类型的字段进行排序 在Oracle中,如果你有一个字段,比如 A-数字-数字 的格式,并且你想要根据这样的格式对数据进行排序,你可以使用 REGEXP_SUBSTR 函数来提取并且转换字符串中的数字部分,然后进…

上海市计算机学会竞赛平台2023年5月月赛丙组滑雪训练

题目描述 小爱最近迷上了滑雪,某滑雪场有 𝑛n 条不同难度的雪道,只有学习并滑了第 𝑖i 条雪道,才能去参加第 𝑖1i1 条雪道的学习与训练。 已知,第一次滑第 𝑖i 条雪道时&#xff0…

【shell】将txt文件拆成n个,文本均匀按行分布

#!/bin/bash # 原始文件名 source_file"/home/part.txt"# 目标文件存放目录 target_dir"/home"# 想要的近似文件数量 desired_files2 # 请将n替换为具体的数字,比如100# 确保目标目录存在 mkdir -p "$target_dir"# 计算原始文件的…

家用洗地机什么牌子好?四款公认品牌好的机型推荐

每个人都希望自己的家里面能够干干净净,就算不是一尘不染,也至少应该是整洁的,但是在这个快节奏的大环境下,做清洁对于人们来说,不是没时间,就是太累了。正当此时,一款造福懒人的神器——家用洗…

Double 4 VR虚拟仿真教学系统在商务英语课堂上的应用

随着科技的飞速发展,VR(虚拟现实)技术逐渐在教育领域展现出其独特的魅力。特别是在商务英语课堂上,Double 4 VR虚拟仿真教学系统正逐渐成为一种新颖而有效的教学工具。 一、Double 4 VR虚拟仿真教学系统的引入 在传统的商务英语…

4D 生物打印技术的挑战:从打印到植入,还有多远?

4D生物打印技术将时间维度融入生物打印,为构建具有动态特性和功能的生物组织结构提供了无限可能。然而,要实现这些目标,选择合适的生物打印技术至关重要。本文将详细介绍几种主要的4D生物打印技术,并分析它们各自的优缺点&#xf…

Linux—— 逻辑运算符,压缩和解压缩

- -a: and 逻辑与 - -o: or 逻辑或 - -not: not 逻辑非 - 优先级:与>或>非 shell [rootserver ~]# find / -size 10k -a -size -50k [rootserver ~]# find /etc -name "e*" -o -name "f*"…

前端初学java二(类、多态、接口、内部类、泛型)

目录 类 种类 Javabean类 测试类 工具类 类的初始化 构照函数 新建对象的内存图 static 继承 This Super 虚方法表 Override 修饰符权限 构造代码块 静态代码块 多态 前提 优点 缺点 示例 抽象方法 抽象类 接口 implements 继承 内部类 成员内部类…

centos7安装mysql教程及免费数据库视图软件

文章所有命令最好在根目录,并以root权限 执行 Mysql安装 1. 添加 MySQL Yum Repository MySQL 官方不再维护 CentOS 7 自带的 MySQL 软件包。你需要先下载并安装 MySQL Yum Repository。 sudo yum install https://dev.mysql.com/get/mysql80-community-release…

期末考试题-通过HTML编程Vue3选项式:简易购物车

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><!-- 引用 element-plus 样式 --><!-- 注意&#xff1a;复…

React+TS 从零开始教程(4):useEffect

上一节传送门&#xff1a;ReactTS 从零开始教程&#xff08;3&#xff09;&#xff1a;useState 源码链接&#xff1a;https://pan.quark.cn/s/c6fbc31dcb02 上一节&#xff0c;我们已经学会了React的第一个Hook&#xff1a;useState。 这一节&#xff0c;我们要学习的是另一…

C语言----文件操作

1.为什么使用文件&#xff1f; 如果没有⽂件&#xff0c;我们写的程序的数据是存储在电脑的内存中&#xff0c;如果程序退出&#xff0c;内存回收&#xff0c;数据就丢失了&#xff0c;等再次运⾏程序&#xff0c;是看不到上次程序的数据的&#xff0c;如果要将数据进⾏持久化…

Java语言开发的一套智慧产科系统源码:产科专科电子病历系统源码

Java语言开发的一套智慧产科系统源码&#xff1a;产科专科电子病历系统源码 系统概述 电子病历系统是以住院病人为中心&#xff0c;面向医生以及护士为主的&#xff0c;涉及临床治疗、护理等业务的临床信息系统&#xff0c;以电子信息技术为手段&#xff0c;实时采集病人在整个…

【每日一练】Python遍历循环

1. 情节描述&#xff1a;上公交车(10个座位)&#xff0c;并且有座位就可以坐下 要求&#xff1a;输入公交卡当前的余额&#xff0c;只要超过2元&#xff0c;就可以上公交车&#xff1b;如果车上有空座位&#xff0c;才可以上。 seat 10 while seat > 0:money int(input(…

cookie/session/token/jwt

Cookie 定义: Cookie 是服务器发送到用户浏览器并存储在本地的小型数据片段&#xff0c;用于在客户端存储会话信息。 Cookie是Web服务器发送给浏览器的一小段数据&#xff0c;浏览器之后在每次请求同一服务器时会将这段数据回传。Cookie的主要作用是维持用户状态&#xff0c;例…

分层解耦----

分层解耦 类聚 软件中各个功能模块内部的功能联系. 例如: 高类聚示例&#xff1a;想象一下餐厅的厨房&#xff0c;每个厨师负责自己的工作站&#xff0c;一个专门做沙拉&#xff0c;一个专门烤肉&#xff0c;另一个专门做甜点。每个工作站内的工作高度类聚&#xff0c;即每个…