蜘蛛抓取网站模块原理,网络营销策划书5000字,淘宝搜索框去什么网站做,石家庄做公司网站简介#xff1a; 这是一篇无法一口气读完的、文字过万[正文字数14390]的长文#xff0c;这是一个无法中途不上厕所就看完的、关于时序数据库的视频[时长111分钟]分享的文字整理..
大家好#xff0c;很开心能够和大家一起交流时序数据库的相关的内容 首先还是简单自我介绍一…简介 这是一篇无法一口气读完的、文字过万[正文字数14390]的长文这是一个无法中途不上厕所就看完的、关于时序数据库的视频[时长111分钟]分享的文字整理..
大家好很开心能够和大家一起交流时序数据库的相关的内容 首先还是简单自我介绍一下我是 孙金城花名 金竹。我是2011年加入阿里在2016年之前一直做公司内部的研发工作包括阿里郎Blink等平台。 从2016年到现在我一直重心在开源建设上面包括ApacheFlink/ApacheBeam/ApacheIoTDB在这个过程中也得到了开源的一些肯定目前是BeamCommitterApacheFlink和ApacheIoTDB的PMC也是Apache Member目前全球华人大概有30的ApacheMember当然随着开源的越来越热国内每年参与开源建设的同学也在逐渐的在增加。 那么2020之后会有怎样的规划呢本着但做好事莫问前程的心态会多多在订阅号中记录我在流计算和IoT方面的认知。最终努力做到走进阿里/踏入开源成为最好的自己. 那么为什么一直做流计算会慢慢选择了解IoT相关领域呢因为在马老师看来“5G时代加速的不仅仅是通讯行业而是更多的促进物联网IOT领域的发展。IoT将是一个新的浪潮。那么我参与IOT领域的切入点是什么呢就是从了解时序数据库 进行着陆的... 如图这不是一篇经验分享而是一个学习过程分享 了解时序数据库最先想到的是: 了解一下时序数据库的现状及发展趋势那么这个数据的权威性大家应该有所共识在db-engines网站的排名应该很客观的。如图从2018年开始TimeSeriesDBMS的关注度就持续迅猛的增长。其实不仅仅db-engines网站有这样的数据Gartner也给出了预见性的判断。我们一起看一下... 在2019年赋权的边缘 就成为了十大战略性技术趋势之一边缘由物联网驱动需要使处理计算更接近端而不是集中式的云服务器。当然早在2018年 云向边缘计算挺进 的趋势就已经非常明显。边缘设备的信息的收集/存储和计算需求与日俱增一直到目前2020年更多的计算和存储能力都逐渐下沉到设备端云边端一体将是今后几年持续的焦点。设备数据具备时序性而作为物联网领域具备存储和计算能力的产品就是时序数据库。那么目前业界时序数据库的阵营是怎样的状况呢我们继续看一下... 同样出自DB-Engines的排名信息目前有几十种大概34种时序数据库大家熟知的InfluxDB自2016年以来稳稳的位居榜首随着2018年IoT领域的崛起InfluxDB的热度也持续飙升稳稳地龙头位置那么InfluxDB为啥如此受到时序数据存储技术的青睐呢我们接下来就会和大家细致进行分析... 其实时序数据库早在1999年就已经有RRDtool,全称RoundRobin Database。新数据会自动覆盖老数据也就是考虑了时序数据的时效性在2008年又出现了GraphiteGraphite是一个用于采集网站实时信息并进行统计的开源项目可用于采集多种网站服务运行状态信息。随后就是大家熟知的OpenTSDBInfluxDB还有feacebook的内存版时序数据库再有就是非常著名的用于监控的Prometueus2017年2018年的时候时序数据库已经发展的非常迅速来这个在刚才的DB-engines网站数据和Gartner给出的战略技术趋势信息是非常吻合的。那么在2020年Apache开源社区又出现了时序数据库的黑马 ApacheIoTDB将来还会有哪些时序数据库产品呢我们让时间来揭晓。 那么在这里抛出一个问题就是“时序数据库难道不能直接存储到关系数据库吗”为啥要造出很多时序数据库时序数据库和关系数据库又有怎样的本质区别呢 其实数据存到哪里合适还是要看数据本身的特点以及数据处理的需求。面对IoT领域时序数据有很多的数据来源比如汽车火车飞机等交通工具以及我们越来越被大家认可的智能家居产品等。 当然最大的数据量来源还是工业领域的各种设备传感器数据这些设备的工况数据收集和处理将给存储和计算带来巨大的挑战。 我们以一个具体的案例来说这是GoldWind发电数据采集GoldWind有超过2w个风机一个风机有120-510个传感器采集频率高达50Hz就是每个传感器1秒50个数据点采集峰值。这要算下来就是每秒5亿个时序指标点的数据。 这个数据量让数据采集/存储/计算面临很大的挑战。同时还有我们业务中的一些非常常见的采集/查询需求20万/妙的吞肚需求是非常常见的但是这样的需求单机关系数据库也是很难搞定的我们接下来会分析一下具体原因。 好的那么还是简单梳理一下时序数据的特点首先时序数据有特定的一些概念。如图Metric就是我们要采集的指标类似一张表还有Tag就是metric的属性特点比如指标属于哪个设备哪个区域等等维度信息。再有Field就是真实要采集的指标名称。那么非常重要的时序数据一定有数据产生的时间就是Timestamp时间戳信息指标数据的时效性也是非常重要的所以要有时间信息。那么Point就是类似表的一行数据。我们以风力 指标 为例 简单图示一下这些概念。
Wind-force就是Metric其中 cityregion就是Tagspeed和direction就是Field。当然Timestamp不用多说就是指标数据产生的时间了。 那么这种关系设计的方式大家不难发现tag的数据存储会有很多的冗余。这是关系数据库存储时序数据的一个涉及到存储成本的问题。当然问题不仅如此我们继续往下看。 现在我们简单总结一下时序数据场景的特点或者说是需求核心会涉及到写入/读取/存储成本三个维度。写入要支撑上千万甚至上亿的吞吐能力读取就需要根据不同的业务有不同的读取需求后面我们会慢慢分析到。当然面对成本问题海量数据的存储成本无疑是时序数据库设计的重中之重。所以面对这样的时序数据场景的特点时序数据存储到关系数据库就会有很多弊端比如刚才说的数据冗余造成的成本问题还有写入的吞吐问题等那么我们接下来就要讨论这些问题存在的本质原因... 好,回过头来我们在来看看目前的时序数据库从架构的角度有哪几种 第一种就是在关系数据库基础上进行改进的时序数据库比如基于PG开发的Timescale。 第二种就是在KV数据库的基础之上进行改进的时序数据库比如基于HBase开发的OpenTSDB。 第三种就是为时序数据量身定制的时序数据库那么目前的领头羊就是InfluxDB。当然我前面说过我也很看好2020新晋的Apache顶级项目ApacheIoTDB。 这三类时序数据库又怎样的特点呢我们接下来逐一进行讨论分析... 我们先来看看基于关系数据库的时序数据库既然基于关系数据库那么我就要聊聊和关系数据库密切相关的存储结构。 首先我们看一个简单的二叉树,我们知道二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式而且二叉树的存储结构及其算法都较为简单二叉树特点是每个结点最多只能有两棵子树且有左右之分。那么我们看看怎样向二叉树里面插入一个数据点呢 如图我们插入节点10插入的过程是二分法即使从整个节点的数值的中间开始如果要插入的值比这个数小就从左子树继续二分查找如果要插入的数据比当前数大就从右子树继续查找那么按照这个算法二叉树的写入和查询的复杂度都是logN。 我们再举一个小例子可能目前我们讲解的内容很简单大家都很熟悉不过为了为后面的内容做铺垫大家还是稍微耐心听我多啰嗦几句。这个例子就是假设我们有如图8个数据我们如果想要查找数据2那么我们先看中间数据第4个点13比较一下13比2大所以要查询的2一定在13的左边我们再从左边的数据进行二分查找左边的中间数据5发现5还是比2大那么就需要查找左子树目前只剩下了2当然也就是我们要找的数据了。那么logN其实就是log2N对吧集合8个数据查找一条数据就是log82的3次方等于8所以log8就是3次这是二叉树的查询复杂度的一个简单示例说明。OK这个二叉树介绍呢有点浪费大家时间废话有点多大家见谅那么接下来我们就介绍关系数据库中使用的存储数据结构。 在实际的关系数据库中有2种数据存储结构一个是BTree一个是BTree那么这两种数据结构格局特色都有使用的场景。那么这两种数据结构的查询复杂度是怎样的呢有什么本质区别。 BTree的写入成本是LogBNB就是树的阶数如图就是3阶BTree每个节点的黄色方框表示的指针个数就是树的阶数蓝色部分就是具体构建BTree节点的数值如果是索引树的话蓝色部分就是构建索引的key的值比如订单号之类的关系数据库的索引字段。 BTree的写入成本是LogBN如果都是LogBN那么Btree和TTree有啥本质区别呢这要看看BTree和BTree从数据的结构上有啥区别 最本质的区别是BTree在每个树节点是有数据存放的BTree只有叶子节点才会存放数据我们说的数据就是关系数据库中一行数据包括Key和普通字段比如订单号是key订单号以及订单对应的产品名称数量金额等就是数据。同时还有一个很大的区别就是BTree结构在树叶节点是有指针指向相邻的叶子节点的是一个链表。这特点大家想想有什么利好对这个特点是有助于区间查询的。大家花几秒钟时间思考一下相对于BTree是不是有这样的优势:) 这里大家可能发现一个问题就是这个算法复杂度应该是LogN为啥是LogBN。本质上在存储角度我们考虑的是DAM模型也就是计算磁盘访问次数的成本。 我们继续思考另外一个有意思的问题Btree的成本和BTree都一样BTree又有友好支持区间查询的优势那么两个共存的意义是什么呢好我们还需要继续搞清楚这件事情...其实对于不太了解这些数据结构的同学是有点烧脑的对于熟悉的同学可以放松一下我们继续探究BTree存在的意义抛开算法复杂度之外还有哪些其他存储领域的实际因素导致BTree更有优势。 上面是BTree从算法角度的复杂度推导下面是BTree的... 这里有一个地方要说明一下这里推理的复杂度是logN但是前面我们说的是logBN这个差别是什么其实本质前面是磁盘访问复杂度不包括数据块内的key值查找。这个大家如过感兴趣可以思考一下如果有问题欢迎留言:) OK放松3秒钟然后继续分析 B Tree 我们继续和这个问题死磕... 要想清楚上面的问题我们还需要一些算法之外的辅助内容虽然是辅助但却是和存储系统密切相关的部分。那就是磁盘。在存储领域选择BTree还是BTree和磁盘IO有着直接的关系。那么什么是磁盘IO呢 我们知道计算机的CPU就如同人的大脑大脑根据外部输入进行思考并为我们下达行为命令那么CUP同样要依托于外部输入进行计算然后将计算的结果再向外输出。那么我们存储里面的磁盘IO可以简单理解成从磁盘读取数据和向磁盘写数据的过程。 这个图应该是我们上学课本或者数据库理论方面书籍里的图片处理器数据总线主存磁盘等等我们再熟悉不过的名词当然内部也是非常复杂的我们今天挑与我们要讨论的问题相关的内容进行分析。 那就聊聊磁盘首先磁盘内有很多的盘片每个盘片又由若干扇区组成扇区是磁盘读写的最小单元每个扇区多大呢一般是512个字节。好那么这个和数据库又有啥关系呢 数据库的数据就存储在磁盘上但是从数据库系统角度如果每次读取一个扇区的数据就太慢了所以数据库一般会有数据块的抽象设计数据都是一个数据块读取一次磁盘一般数据块大小是464KB那么重点来了通常读取一个数据块的磁盘IO需要消耗大概10ms左右的时间这是非常耗时的操作了所以我们在数据库设计的时候一定要尽量减少磁盘的访问次数。那么MySQL的InnoDB默认数据块的大小是16KB当然这是可以配置的。不同数据库默认值不一样比如HBase存储的数据块大小应该默认64M。好的说到这里不知道大家是不是知道我接下来要说什么了有这方面经验的估计秒懂但是第一次思考这个问题的还是需要些解释的我们往下继续进行... OK,我们还是举一个例子来讨论假设以16K为块大小也就是每16K数据需要访问一次磁盘我们的目标是减少磁盘I/0,那么数据相同的情况下B-Tree和BTree哪个磁盘I/O更少呢假设我们有一个查询select* from device where deviceId38deviceID是一个bigint的8字节类型作为索引key。我们分别分析一下Btree和BTree哪个访问磁盘更少 主键deviceId为bigint类型长度为8字节而指针大小在InnoDB源码中设置为6字节这样一共14字节也即是如图一个蓝色框数据和一个橙色框指针需要14个字节存储。那么我们一个数据块中能存放多少这样的单元就代表一次可以在内存加载多少数据keydeviceID和对应的地址指针即16384(16K)/14bytes1170。也即是每1170个设备id读取一次磁盘那么也就是内存构建一个1170阶的BTree。 那么大家想如果是BTree结构一行数据除了设备Id还有他设备信息那么Btree在没有节点不但存储key和指针还要存储数据所以同样的数据块大小是不是一次加载到内存的Key数量比Btree要少很多那么在查询时候必然比BTree访问磁盘IO要多大家思考2秒钟是不是这个道理:) 那么如果是BTree相同的块大小树的阶数就比较小相对于BTree来说相同条件BTree的阶数更大也就是复杂度上的logBN的B比较较大B越大相同的查询开销越小。 那么既然Btree很优秀我们看看一个BTree能支持怎样的数据规模呢好我们想想BTree只有叶子节点要数据比如我们一行数据是1K一个数块是16K那么也即是一个数据块可以存放16行数据。那么一个3层的BTree叶子节点有多少行记录呢三层的树的话第二层有多少数据指针指向第三层叶子节点就有多少数据块数据的数据每个叶子是一个数据块每块有16行数据就能计算数据规模了。那么底层有多少指针呢还是按照刚才MySQLinnoDB的例子算会构建一个1170阶的BTree大家还记得“阶”的概念吧就是一个节点指针的数量那么1170阶的Btree第二层首先有1170个节点每个节点又有1170个指针刚才我们计算过那么我们可以计算第三层的记录行数了那就是1170*1170*162000w。也即是3层的1170阶Btree可以完美的支持2000w的数据量那么这2000w数据量查询一条数据的成本是多少呢通常磁盘IO的代价是最大的刚才我们说的了一次磁盘大概10ms那么3层我们最多要3次磁盘IO就可以查询到了。也就是毫秒级的查询延时。 我们再看一个例子分析为啥3次就够了假设这棵树我们要查找key为30的数据那么过程如下。 根据根结点指针找到文件目录的根磁盘块1将其中的信息导入内存。[1次磁盘IO] 此时内存中有三个key(5,28,65)和三个存储其他磁盘块的地址的数据。我们发现283065因此我们找到指针p2。根据p2指针我们定位到磁盘块3并将其中的信息导入内存。 [2次磁盘IO] 此时内存中有3个key(28,35,56)和三个存储其他磁盘块的地址的数据。我们发现283035因此我们找到指针p1。根据p1指针我们定位到磁盘块8并将其中的信息导入内存。 [3次磁盘IO] 此时内存中有3个key(28,30,33)和对应的数据完成查找。OK查询的逻辑和成本消耗先介绍到这里。下面更重要的内容来了也就是内容会涉及到了为什么关系数据库不适合作为IoT场景的存储大家要多花精力理解我给大家演示一下BTree的写入的过程。。。 好接下来假设我们有数据按照如图的顺序到来如何一步一步的构建一颗BTree索引树呢 好首先来的数据是33590我们构建一个树的形态如图35是root左右各有一个孩子。 然后又来了17和26二分的方式17和26都小于35所以都在35的左子树但是这里有个问题 就是我们是一个3阶Btree指针有3个节点只能有2个所以31726已经超了我们要进行节点的分裂后面存储层面就涉及到磁盘块的分裂。 好我们进行节点分裂之后树的样子变成root节点有2个key3个指针。我们继续看后面的变化。。。 我们看看后面来了哪些数据。。。树的变化如图这里提醒一点中序遍历的不同的树形状可以得到相同的结果。我们这个例子核心是想说明数据的构建过程有节点的分裂也意味着存储有磁盘块的分裂会产生大量的随机磁盘IO。我们继续看变化。。。 接下来再后面的数据。。好3687296575到来后树的形状如图。。。(这棵树是故意弄的很平衡哈:) 最后我们看看有哪些数据到来最终树的形成。基于B-Tree/BTree的存储数据写入造成很多的随机IO。我简单说一下大家思考如果一个节点已经写入磁盘了后面树形发生变化之后节点分裂原先存储到一个磁盘块的数据就会分开存储到2个新的磁盘块那么原先的磁盘块就要删除。这样反复的操作那就造成了Btree/Btree很多的随机IO了。 OK聊到随机IO对存储系统的影响我们也要顺便说一下相对于随机I/O就是顺序I/O。 顺序IO - 是本次 I/O 给出的初始扇区地址和上一次 I/O 的结束扇区地址是完全连续或者相隔不多的。在顺序I/O访问中磁盘所需的磁道搜索时间较少读/写磁头可以以最小的移动访问下一个块。随机IO - 是指读写操作时间连续但访问扇区地址不连续随机分布在磁盘LUN的地址空间中。随机I/O可能是因为磁盘碎片导致磁盘空间不连续或者当前block空间小于文件大小导致的。连续 I/O 比随机 I/O 效率高的原因是在做连续 I/O 的时候磁头几乎不用换道或者换道的时间很短而对于随机 I/O如果I/O 很频繁的话会导致磁头不停地换道造成效率的极大降低。如图我们看到不同磁盘 顺序写和随机写的性能差异差不多都是数量级的差距。即便SSD的硬盘希捷公司也有明确的说明大家可以参考链接查看测试数据。 所以随机IO和顺序IO对存储系统的写入性能有很大的影响。那么这些差异和时序数据场景有什么关系呢我们继续聊。。 首先我们还是要切回时序数据场景的特点IoT场景是写多/读少的场景写入性能至关重要。所以基于关系数库也就是Btree的存储结构的时序数据块在存储时序数据时候都存在IO效率低下写入速度很慢的现象。那么需要如何解决呢 解决BTree的写入问题一般有两种方式一种是是COLACache-Oblivious Look ahead Array- tokuDB。另一类就是我们今天要重点分析的LSM tree(Log-structured merge Tree)结构。这个数据结构的插入复杂度是O1这个比BTree的logN要优秀很多了。 目前基于LSM Tree数据结构的存储有很多如著名的KV存储HBase还有LEVELDBRocksDB等等。 那么为了解决时序数据写入问题时序数据库阵营出现了一些基于KV存储的时序数据库比如大家熟知的基于HBase的OpenTSDB。 LSM Tree全称是 Log-structured merge TreeMerge就是合并Log structured就是像写日志一样进行顺序写入append only的方式。 LSM树的核心思想就是放弃部分读能力换取写入能力的最大化。核心思路其实非常简单就是假定内存足够大因此不需要每次有数据更新就必须将数据写入到磁盘中而可以先将最新的数据驻留在内存中等到积累到一定程度后再使用归并排序的方式将内存中的数据合并追加到磁盘队尾。我们来看一下这个基于LSM Tree的写入过程
首先我们要解决如何做到顺序IO就是一个数据写入到来之后先进行WAL的落盘。那么如何解决乱序那就是写WAL是为了恢复真正的有序写入要将数据写入内存也就是Mem-Table然后对Mem-Table进行排序数据写入到内存之后就表示写入成功了。那么写到内存之后会怎样操作呢就是要解决落盘问题。当内存数据到达一定规模就需要写入磁盘LSM Tree的做法是将要刷磁盘的Mem-Table变成immutable刷磁盘同时不影响写入请求在创建一个新的Mem-table。这样的持久化在数据持续变化和查询的时候会有什么问题吗当然有比如key更新/删除了怎么办要查询的key在多个文件怎么办所以LSM Tree结构还需要有一个Compactions的阶段以及对数据创建索引的过程。我们以HBase的HFile为例存储结构如图文件中除了数据还需要有索引和Boolmfilter的机制进行加速查询。 上面我们了解了LSM Tree的写入逻辑那么查询逻辑是怎么样的呢查询逻辑最核心的是要查询索引,首先在内存Mem-table里面查询然后在immutable Mem-table里面进行查找然后是磁盘Flie里面进行查找。当然这里有Bloom filter辅助查询。Bloom filter本质就是一个bitmap每个key数据用k个独立的hash就行计算填充bitmap数据查询时候Bloomfilter说没有一定没有Bloomfilter说有不一定有还要继续索引查找。 LSMTree虽然以牺牲查询为代价来解决Btree写入问题但是会利用一些辅助手段比如索引Bloomfilter基数估计等手段加速查询同时各种数据库还会有不同的Cache机制加速查询。这样听起来基于LSMTree结构的KV存储应该有很不错的读写表现。那么基于KV存储的时序数据库应该怎样设计呢 我们还是以大家熟知的基于HBase的OpenTSDB为例来进行分析看看KV存储在设计时序数据存储时候的设计技巧和存在的问题。 以HBase为例我们聊聊KV存储对LSMTree的应用如图大家应该很熟悉HBaseClient向HBase的RegionServer发起读写请求当然HLog就是LSMTree中的WAL部分MemStore就相当于LSM里面的Mem-table部分HFile就是持久化文件。 简化一下就是HBase的HRegionServer包括HLog和HRegion一个HRegion包括很多的HStore又分为MemStore和HFile两个核心部分。 其中按照Rowkey进行region划分每个region对应一个ColumnFamily一个MemStore。 每个ColumnFamily里面又有若干Qualifiler。当然任何一个KV数据又会包含一个时间戳Timestamp。 那么按照这个架构一条完整的数据是怎样的呢 如图这是一条完整的KV数据开头KeyLength和ValueLength两个固定的长度分别代表Key和Value的长度。
在Key部分RowLength是固定长度的数值表示RowKey的长度Row 就是RowKey的值。
Column Family Length是固定长度的数值表示ColumnFamily的长度
接着就是ColumnFamily再接着是Qualifier然后是两个固定长度的数值表示TimeStamp和KeyTypePut/Delete
Value部分没有什么复杂的结构就是纯粹的二进制数据这个也是很大的成本问题后面我们会介绍。 那么这里有两个常见问题一是按Rowkey进行Region的划分热点问题怎么处理二是按ColumnFamily进行Store存储如何设计ColumnFamily包含的Qualifier这个磁盘块磁盘IO有什么关系 我们知道HBase的Rowkey都是字典序的一般Rowkey的典型组成会是业务KEY时间倒序Long.MAX_VALUE -timestamp/加盐/Hash组成那么时间倒序有利于最新的数据在最前面加盐或者Hash可以缓解热点问题。关于ColumnFamily的Qualifier的设计,简单讲一个原则就是经常一起查询的数据要作为同一个ColumFamily的Qualifier。 除了这两个问题我们还有2个问题需要考虑那就是数据查询时候如何定位一条数据属于哪个RegionHFile里面的KV数据如何快速读取 回答Region寻址和HFile快速读取的问题也要分析HBase对Region的管理方式Region信息属于HBase的内部抽象对Region的管理HBase也建立了一颗B树我们前面聊过如果一个数据块事16K那么一个3阶的BTree能容纳2000w的数据量一次查询只需要3次IO那么HBase数据块默认大小事64M可以配置更大那么这些Region一般会加载在内存中所以在BTree的数据机构上HBase对region的寻址非常快速。 同样在HFile里面定位rowkey的位置也是基于二分查找的如图查询Rowkey为fb的过程图中红色箭头部分第一层f在a和m之间所以查询a的子树第二层f在d和h之间所以查询d所指向的子树第三层我就找到了f这个值然后在第四层在查找fb数据最终返回数据。 这里提醒一下HBase存储是LSMTree数据结构但是为了加速查询内部对Rowkey的索引建立和Region的管理都是采用了BTree的数据结构。 好了解了HBase的基本内容之后我们思考一下我们如何基于HBase设计一个时序数据库呢首先要思考的问题就是如何在HBase中对时序数据的核心概念进行抽象映射。也就是时序数据核心概念是Metric相关内容Tag相关内容和Timestamp时间戳。HBase现有的概念是ColumnFamilyColumnQualifier当然也具备Timestamp信息。 那么我们无法改变HBase的KV数据结构只能在表达时间序列数据的同时最充分的利用HBase现有的数据结构抽象最核心的就是Rowkey的设计和ColumnFamily和ColumnQualifier的利用技巧。我们逐一看一下。 那么基于HBase的时序数据库OpenTSDB是如何设计的呢
我们先看最核心的OpenTSDB的RowKey Schema的设计其组成是salt] metric_uidtimestamptagk1tagv1[...tagkNtagvN] 那么按照HBase的思维这里有一个很奇怪的问题就是【salt】设计竟然放在了rowkey的最前面这个Salt解决了什么问题同时有带来了什么问题呢 当然Salt部分和前面我说的HBaseRowkey里面也可以加Salt的作用是一样的解决热点问题。但是这里问题是在查询的时候用户不知道salt的值如果进行查询呢这肯定是带来了数据查询的性能问题。其实OpenTSDB里面Salt是分桶的抽象默认20个桶用户可以配置那么查询时候同一个业务rowkey就要查询20次然后在进行merge这个会极大的影响get查询。 好那么通常Rowkey设计不携带salt部分同时OpenTSDB在Rowkey设计上也下了不少功夫首先大家看到Rowkey的开始是一个metric的uid。 如图OpenTSDB里面的一个rowkey示例包括 metric部分时间戳部分tagk和tagv的组成。 我们以前面我们风力数据采集的信息为例Rowkey就应该是speedtscityhangzhouregionxihu。 那么这里面我们还没有解释uid的作用后面我们慢慢进行说明。现在要考虑的问题是MetricTimeTag这个在rowkey中的顺序可以变化吗或者说如何设计最合理呢 HBase是按照rowkey的字典序顺序排列的为了减少查询IO,最好将相同的Metric写到相同的磁盘块所以OpenTSDB需要将Metric作为Rowkey的最前面。
Timestamp没有业务语义不适合放到最前面如何放到前面可能出现多个Metric会混杂在一个磁盘块不利于查询,写入也会造成热点问题
Tags放在最前面会造成大量的Scan操作比如用户指定多个Tags中的某个查询而且不是前缀Tag的话就会在HBase里面将会变成大范围的扫描过滤查询当然Tags放到Timestamp前面也存在Scan的问题。 好思考了这些现实问题OpenTSDB对rowkey的设计就是根据用户的metrictstag信息的方式组成。那么目前看这个rowkey的设计还是很合理的那么是否也在海量的时序数据场景有一些明显弊端呢 很明显这个key组成里面根据业务的不同会变得复杂首先就是HBase的key里面有timestamp是一定要有的然后业务的rowkey里面也是有时间戳信息的。 为了套用先有的HBase结构存储中有很多无用的信息比如comumnFamily部分KeyType部分等。同时Rowkey里面的metric是一个业务字符串这些数据在实际存储过程中很多冗余造成存储成本的浪费。 还有HBase是弱类型问题不能对Value部分根据业务的不同字段类型进行专门的压缩。同时Rowkey部分包含很多tag信息没有对Rowkey和tag进行倒排索引同样使得查询受限。大量scan操作性能很差。所以基于KV的时序数据库存在客观的设计弊端。 面对HBase设计时序数据存储的弊端OpenTSDB做了哪些比较核心的优化呢 首先是Rowkey里面的Timestamp时间粒度不是毫秒而是小时这样极大的减小了scan的部分一小时的数据可以一个get进行获取。另外我们一直提到的metric_uid其实也是对存储的优化将业务字符串映射成uid可以极大简化存储。还有在Compactions部分会将很多Qualifier数据合并成一个Qualifier里面提高压缩效率。 我们细致的看一下OpenTSDB的data Scheam和uid mapping的Schema。 最核心要了解的就是RowKey部分我们看到很多的编码我们看到编码后的rowkey和上面编码前的字符串有极大的改进。 关于UIDOpenTSDB里面采用3个字节存储约1600万个值足够满足大部分业务场景同时我们也可以根据业务规模改变UID的生产策略。这样极大的节省存储和key排序成本。 同时我们看到TSDB-UID的映射表是双向的既可以同UID找到key也可以通过key找到UID。大大提高搜索效率。 OK坦白讲不论OpenTSDB怎么优化首先都需要先填补HBase设计在时序数据库场景适配的坑再进行优势发挥那么这样的客观事实势必会催生为IoT时序数据而生的原生时序数据库。 一个是大家熟知的时序数据库领域的领头羊Influxdb另一个是2020年Apache 新晋的顶级项目ApacheIoTDB。我们分别探讨一下。 InfluxDB在时序数据库排名第一这个成绩不是浪得虚名而是实至名归有句话叫天才出于勤奋其实InfluxDB从诞生伊始一直到现在都在不停的努力不停的为解决现实问题而优化改进。我们接下来都是基于InfluxDB1.8版本进行描述的。 我们了解一下Influxdb 引擎升级的历程最初为避免BTree带来的写入问题InfluxDB采用了LSMTree的存储设计底层引擎采用LevelDB但是后面发现LevelDB存储不能热备份问题于是底层引擎又采用RocksDB解决但是RocksDB当时又存在时序数据场景冷数据的高效删除问题我们知道海量的数据通过api的方式逐条删除是相当耗时的会死人的所以InfuxDB又进行改进引入Shard每个shard存储一片时间连续的数据冷数据都是时间较老的数据一个shard对应一个底层LevelDB数据库要删除的时候只需要关库删文件即可。但是这个设计又带来了新的问题就是系统的文件句柄问题后面又有BoltdDB来解决但是BoltDB里面利用了mmap和BTree的数据结构BTree的随机写问题又糙成了IOPS限制问题所谓痛苦造就成就种种投产问题推升了目前InfulxDB全新的存储和索引架构TSM架构。 接下来我们看看目前InfluxDB的TSM架构细节尝试 尽量 知其然知其所以然。Lets go... 首先不可避免的我们要了解InfuxDB针对时序数据场景的核心概念的抽象我们看InfluxDB新增了一些抽象一个是database一个是retentionpolicy尤其retentionpolich是专门针对时序数据的冷数据设计的原生的时序数据库的优势就是在设计之初就会考虑时序场景的种种典型问题。那么这些概念的层次结构是怎样的呢 首先Database是一个最上层的抽象或者说是管理单元然后数据也是要按时间进行Sharding的每个shard都归属于一个ShardGroup然后Shard里面就是具体时序数据了当然包括时间戳Series和Field信息其中Series就包含了metric和tag信息。上面的RP就是retentionpolicy。 同时Point相当于一行数据如代码片段所示包括被排序的keykey的组成是measurement和tags的组合后面我们还会大量的涉及大key的介绍。好简单了解了这个层次逻辑我们再简单看看这些概念的代码抽象。 首先是StoreStore是为Database管理shards和index的抽象。 其中InfluxDB的索引机制有两种一是基于内存的版本一是基于文件的版本后面我们逐步详细介绍。 我继续向下进行钻取那就是文件存储的分区管理Shard的代码抽象Shard里面会包含时序数据和Tag到SeriesKey的倒排索引。大家会发现有了tag的倒排索引基于HBase的OpenTSDB里面根据一部分tag查询的scan操作就可以避免了。 那么Shard里面还有一个EngineEngine负责进行合并多shard的查询结果等工作。Engine代表了一个存储引擎这里面包含了TSM架构最核心的组成部分WAL/CACHE/TSMFile/Compaction/Compression组件。我们后面一一介绍。在往下就是FileStore部分了FileStore里面包括很多的TSMFile。那么一个TSMFile是怎样的结构呢如图包括4个部分。 其中Blocks和index部分尤为重要我们先看看数据是如何写入的... 首先一个写请求到来之后的写入流程如下
第1步进行AppendOnly的WAL写入。第2步然后就要更新索引这个是加速查询的本质包含了tag和serieskey的倒排索引内容。第3步就是数据更新到缓存相当于LSMTree里面的Mem-table角色。第4步就是返回客户端成功应答。当然数据不能一直保留在内存我们需要某种机制进行落盘处理。同时落盘的时候最好不要影响写入请求。 第5步落盘的过程在InfluxDB里面叫做Snapshotting。进行快照的时机有2个一个是内存使用达到预定的阈值大小一个是给定时间间隔没有数据写入。cache-snapshot-memory-size ”25m”cache-snapshot-write-cold-duration “10m”落盘之后我们就变成了Level的文件InfluxDB设计4层的TSMFlile每个TSMFile内部又有精巧的结构设计。 这里再特别注意一下内存中Cache结构是一个SortedMap结构。 同时说明一下在目前InfluxDB1.8版本我没有看到做快照时候将Cache变成只读快照影响写请求然后写完清空Cache。 第6步我想正是因为这个Cache没有只读动作在进行Compaction时候也考虑了低层级采用低CPU消耗的算法缓解对写入的影响。而高层级的文件合并就要考虑压缩成本。在回到索引部分我们刚才提到InfluxDB有两种类型的索引可以选择一个是基于内存的一个是基于文件的。在索引的构建中也绞尽脑汁利用了可用的一切优化手段BTreeBloomFilterHashIndex等等来加速查询。 那么我们来哦细究一下最核心的部分内存Cache里面的数据结构是如何设计的有怎样的优势呢 首先设计的初衷一定是在SeriesKey有序的前提下高性能写入。 如图Cache内部提供了一个ring结构来对数据进行分桶/分区管理理论上分区的数量最多水256个为啥呢因为InfluxDB采用 SeriesKey的前8个bits进行Hash8个bit最多256个值。而每个Partiton的数据存储是一个sortedmap包括SeriesField和时间戳信息。 OK,这里YY一下按照代码里面的注释这个取余操作是可以优化的。大家感兴趣可以看看源代码怎样优化最好。:) 欢迎线下讨论... 我们根据代码的的分析可以得到Cache的数据结构是这样的内存数据存储结构是partitonmap的层级管理那么这样的数据结构有什么好处呢 环结构可以Split数据结构降低读写时候锁的竞争取前8个bit进行hash确保连续数据存储在一个磁盘块时间连续Map是有序的确保在Snapshoting时候可以快速有序落盘
接下来我们再来看看TSMFile细节内容。 好是时候聊聊TSMFile的数据结构和对读/写性能和存储成本的设计考虑了。 首先TSMFile的四核部分中Header 是5个字节存放Magic和版本Footer是8个字节记录TSMFile中索引数据在TSMFile的偏移量。Blocks里面存储很多数据块索引部分的Key是SeriesKeyField。 其中IndexEntry对应一个Block进行索引的构建。包括数据块包含数据的最小最大时间以及数据块在TSMFile中的位置。 Block部分除了CRC的数据校验部分还有数据类型时间戳信息和数据值。 InfluxDB目前支持Integer/Float/Boolean/String等数据类型每种数据类型都有专门的压缩算法。 对于不可或缺的Timestamps信息InfluxDB采用Facebook的Delta-delta算法极大压缩有序时间戳的存储。 同时在Index结构设计上也考虑了时间要素和区间查询。索引部分利用minTime为每个block的indexEntry构建基于BTree索引树以便加速查询。 这里我们提出一个问题在Footer中记录整体Index在TSMFile的偏移量如果我想获得所有Key需要怎样操作我们看这个数据结构会发现很明显是需要根据offset定位到Index的区域然后读取所有的index数据。这样其实这是一个很耗时耗空间的操作需要将整体index进行读取那么是否可以优化一下呢 当然InfluxDB对索引进行了优化为每一个Key的索引信息都添加了offset信息维护了一张offset表格。 在代码的抽象上在IndirectIndex的数据结构中有offsets的数据维护这样想要查询所有Kyes的时候我们读取offsets信息根据偏移直接获取对应key的信息。 那么还有什么跟多的设计考虑吗 当然除了刚才我们提到的Level文件合并算法考虑还有索引优化考虑以及定期的FullCompaction。除此之外在索引设计上面增加BloomFilter辅助判断key在索引树里面的存在性还利用HashIndex加速key的查找。总体看来InfluxDB在查询上面花了很多的细节优化。 在继续拷问还有什么其他优化吗 回答还是肯定的在Measurement索引区域进行tag和SeriesKey的倒排索引结构。这在很大程度增加了查询的灵活性和查询性能。 同时我们发现在tagkey到SeriesKey的映射中我们还有seriesID和SeriesKey的映射维护这又是出于怎样的考虑呢对这里是对存储成本也进行了考虑。 这是索引文件TSI的结构设计包括4个部分 首先是Trailer层面这是索引的入口记录Measurement/Tag/Series三个Block在TSI的偏移量offset和大小然后是Measurement block记录所有的指标信息也就是measurement表的Meta信息。然后是 tag信息部分这个部分核心维护了面介绍的倒排数据结构tagkey, tagvalue, listseriesID最后series Block索引区域记录了database中所有的SeriesKey信息当有了SeriesKey和时间戳之后我们就可以进一步在Cache/File中进行查询找到对应的分桶和对应value的时间序列值。 找到对饮的TSMFile对应的数据块之后加载数据块到内存根据TSFMFile中的BTree树索引二分法找到具体的查询值。 具体的查询逻辑如图标注。 对应TSI的每个部分内部都有精细的设计和优化。这里简单提两点 一个是每个部分都有一个Trailer部分保存该部分的组成的偏移量。另一个是每个部分都利用hashindex加速对应的key查询。OK总之InfluxDB利用TSM和TSI架构完美解决了高吞吐热备份冷删除高性能的查询,和基于tag的倒排索引索引所支撑了灵活的业务查询。
这里特别提醒InfluxDB自动根据Tag进行倒排索引的建立在实际业务中要充分利用。 当然InfluxDB的龙头地位除了存储查询设计的优势还有很多实用的功能比如ContinuousQueries。这个的实现本质就是定时调度我们可以根据语法很清楚的了解设计的语义。其中 EVERY是计算频度FOR 是计算区间GroupBy的时间区间就是每次计算的数据窗口。 我们简单看一个示例EVERY1小时For90分钟groupby30分钟。的业务含有是每1小时调度一下计算每次计算的数据处理区间是90分钟业务聚合计算的窗口数据30分钟的数据。 我们假设8点中触发了计算那么计算数据区间是6:308:0090分钟计算窗口是30分钟也就是90分钟的数据计算三次有三个计算结果。 当然1小时触发一次到9点还会再次出发计算逻辑。 还有丰富的生态也是InfluxDB亲民的资本。既后数据的采集又有基于订阅的实时计算还有方便的界面查看。OK这里可以为InfluxDB点个赞了很优秀。:) OK,再来看看另外一个Apache开源社区优秀的时序数据库ApacheIoTDB。 首先是够新ApacheIoTDB是2020年9月从Apache 孵化器毕业成本Apache顶级项目的。 第二是ApacheIoTDB足够强大左边的写入和查询的性能测试已经超越了目前的领头羊InfluxDB。 那么为什么IoTDB会有这样的优秀表现呢 首先ApacheIoTDB也是在LSMTree的基础进行了架构优化提出了tLSM的存储架构在tLSM的架构中优化了LSM中的Mem-table部分提出了为乱序数据提供独立的处理逻辑这在写入上有很大的优势。同时IoTDB更加注重从SQL的角度进行查询优化让用户的查询逻辑智能的获取最优的查询性能。OK我们再看看目前IoTDB整体组件栈是一个怎样的情况呢一起来看一下... 图中棕色部分是现在发布的版本已经具备的橙色部分是后续发布版本中陆续规划的当然还有一些有待进一步讨论和社区交流的部分。那么就IoTDB的现有功能来说已经得到了一些工业企业的认可我们一起看一下几个投产的案例。 这是IoTDB的一部分工业企业用户包括上海的地铁项目这个场景1台IoTDB实例就支持了每天4000多亿的海量时序数据采集和存储。关于IoTDB的分享还有太多的内容可以交流。时间原因我们将内容留在下次。
原文链接
本文为阿里云原创内容未经允许不得转载。