头图

从 TDengine 存储引擎的变化探讨——为何大家应尽快切换 3.0 版本?

流是一个有方向感的汉字,并且给人轻便迅捷的感觉。TDengine( Time Series Database ,TSDB) 产品最初的灵感之一,便是和“流”字相关:一台物联网设备便是一条数据流,十万台设备便是十万条数据流。它们像溪河汇聚一样,每秒每分源源不断地流向了数据处理平台。

面对这样规模的大数据挑战,TDengine 选择充分地利用时序数据本身的特点(可参考:https://mp.weixin.qq.com/s?__biz=MzIzNzg5MTcxNA==&mid=2247483...),来针对性地设计存储引擎。最终的目标其实就是:高效持续地吞吐、消化这些数据流,让数据能够无延迟地产生价值。

可以说,我们追求的便是“流”一般的产品能力。而关于文章标题的答案,本文将从 TDengine 的存储引擎的变化史说起:

由于认为时间序列拥有天然递增属性,所以在最早期的 1.6 版本中,TDengine 是不支持对乱序数据的处理的,当时乱序数据写入表中后,系统会做报错处理。但经过用户实际场景的磨练后,我们不得不把注意力聚焦在这个“害群之马”的身上。在生产环境中,由于设备损坏,网络延迟等原因,数据乱序到达是难免的。更关键的是,不管数据乱序与否,用户都有权利自行决定是否处理它,这是一个产品应该具有的灵活度。

它让理想中的数据流“乱”了,但我们却又不能放弃它。

于是,我们立刻在 2.0 版本中增加了数据在内存中的排序和硬盘中的数据子块来支持乱序数据的处理,以此维持了数据的完整性和有序性。

但是这对于 2.0 版本的流式计算和订阅来说,仍然有类似的麻烦。

当时的订阅/流计算(连续查询)是基于查询引擎的产物,它依靠连续不断地执行 SQL 查询结果作出实时反馈。以订阅为例,每条符合要求被消费掉的数据的时间戳,都会被记录下来,从而作为下次 SQL 查询中时间范围的起始点。这时,即便是新数据的时间戳只比这个记录早一秒,也不会被 SQL 轮询到。因此可以说, 2.0 的流计算(连续查询)/订阅同 1.6 一样,它只处理了有序部分的数据。本质上来说,由于 SQL 取到的只能是已经入库的数据,所以这个阶段的查询行为是滞后的。

因此,我们决定在 3.0 再次进行优化重构:

虽然数据的时间戳有乱序,但是他们到达数据库的时间永远分有先后,这组序列相当于“数据入库时间”。不过这个“数据入库时间” 不是时间戳,而是一个从 0 开始的整型数字,我们称之为“版本号”,代表的是数据库概念是“第 x 次的数据变更”。熟悉 WAL 概念的伙伴们都知道,TDengine 利用 WAL 技术来提供基本的数据可靠性:每一条 WAL 信息代表的是一次数据库的变更(增删改),所以每一条 WAL 信息都会有唯一的版本号。通过“版本号”,我们可以明确地告诉流计算/订阅该数据是否为新增数据,再通过对 WAL 的这组“版本号”创建索引,我们就可以快速定位要消费的数据了。

以订阅为例:3.0 的订阅引擎为时间驱动,它会解析每一条新写入的 WAL 的消息内容,然后判断是否匹配 topic 的 sql 条件,如果匹配则直接把 WAL 的消息转化成数据返回给用户 。

除了用于订阅,这组版本号还有很多十分重要的作用:

  1. 它本身核心的职责是 raft 日志的编号,用于多副本的数据同步;
  2. 把版本号下发给每行数据,以追加写入的形式完成数据的更新与删除。

比如:某条 wal 消息的内容是写入一行数据:

insert into d1 values ("2022-03-10 08:00:00.000",100);

那么这行数据就也拥有了这条 wal 消息的版本号(假设为 1) ,并且永久性地存储在数据文件中。

解下来的一条 wal 消息的内容是以相同时间戳更新这行数据:

insert into d1 values ("2022-03-10 08:00:00.000",200);

消息的版本号便是 “2”,这条数据会以写入的形式追加存储。查询的时候,在时间戳相同的情况下,通过比对版本号大小来选择最新的数据,因此我们得到的数据便是:”2022-03-10 08:00:00.000″,200。

删除同理,被删除的数据是取版本号较大的空数据,这样便可在不破坏原有数据文件结构的情况下实现高效的删除。以上部分具体实现细节可参考:https://mp.weixin.qq.com/s/imECB9dIFxZKeoaF3uoOgg

3.另外,当 follower 副本的数据落后,但 leader 上的 WAL 日志已经消失的情况下,版本号还可以做数据文件的快照。只把增量的数据传 输过去,大大节约 data recovery 的时间。

通过这些大刀阔斧的重构,TDengine 完美地把上面几大模块缝合了起来:

  1. 解决了流计算、订阅对于乱序数据处理的不支持;
  2. 让删除操作为逻辑删除,解决了删除操作的性能问题;
  3. 基于版本号引入了快照机制,大幅优化了特定场景下的数据恢复的速度。

回到标题上,为何 TDengine 用户应尽快切至 3.0 版本?答案已经都写在上面了。通过下沉到海量用户实际场景中反复迭代优化,从 1.6 到 2.0 再到 3.0, TDengine 从底层结构上解决了最初设计的瑕疵,各个模块之间结构变得更加清晰,衔接也更加自然,这样扎实的底层设计对产品未来的稳定和性能提升所带来的帮助都是巨大的。

开源、高性能、云原生、极简的时序数据处理平台

87 声望
27 粉丝
0 条评论
推荐阅读
TDengine 合作伙伴 +1,这次是「DaoCloud道客」
随着我国数字经济持续快速发展,各行各业都在积极拥抱云技术,上云成为企业加快数字化转型步伐的关键一步。在此过程中,越来越多的企业开始意识到云原生技术的重要性,利用云原生更快地开发和部署应用程序,提高...

TDengine涛思数据

花了几个月时间把 MySQL 重新巩固了一遍,梳理了一篇几万字 “超硬核” 的保姆式学习教程!(持续更新中~)
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。

民工哥14阅读 2.1k

封面图
硬卷完了!MongoDB 打怪升级进阶成神之路( 2023 最新版 )!
前面我们学习:MySQL 打怪升级进阶成神之路、Redis 打怪升级进阶成神之路,然后我们还在继续 NoSQL 的卷王之路。从第一篇文章开始,我们逐步详细介绍了 MogoDB 基础概念、安装和最基本的CURD操作、索引和聚合、工...

民工哥7阅读 749

封面图
初学后端,如何做好表结构设计?
这篇文章介绍了设计数据库表结构应该考虑的4个方面,还有优雅设计的6个原则,举了一个例子分享了我的设计思路,为了提高性能我们也要从多方面考虑缓存问题。

王中阳Go4阅读 1.8k评论 2

封面图
又一款内存数据库横空出世,比 Redis 更强,性能直接飙升一倍!杀疯了
KeyDB是Redis的高性能分支,专注于多线程,内存效率和高吞吐量。除了多线程之外,KeyDB还具有仅在Redis Enterprise中可用的功能,例如Active Replication,FLASH存储支持以及一些根本不可用的功能,例如直接备份...

民工哥4阅读 1.7k评论 2

封面图
MySQL百万数据深度分页优化思路分析
一般在项目开发中会有很多的统计数据需要进行上报分析,一般在分析过后会在后台展示出来给运营和产品进行分页查看,最常见的一种就是根据日期进行筛选。这种统计数据随着时间的推移数据量会慢慢的变大,达到百万...

一个程序员的成长7阅读 968

封面图
深入理解MySQL索引底层数据结构
在日常工作中,我们会遇见一些慢SQL,在分析这些慢SQL时,我们通常会看下SQL的执行计划,验证SQL执行过程中有没有走索引。通常我们会调整一些查询条件,增加必要的索引,SQL执行效率就会提升几个数量级。我们有没...

京东云开发者3阅读 611

封面图

开源、高性能、云原生、极简的时序数据处理平台

87 声望
27 粉丝
宣传栏