首页 文章 精选 留言 我的

精选列表

搜索[时序预测],共10000篇文章
优秀的个人博客,低调大师

Apache IoTDB 0.11.2 发布,物联网时序数据库

Apache IoTDB 0.11.2现已发布。Apache IoTDB 是一个集成数据专为时间序列数据设计的管理引擎。它为用户提供以下服务:数据收集、存储和分析。由于其轻巧的结构,高性能和丰富的功能集,以及与 Apache Hadoop、Spark 和 Flink 的深度集成,IoTDB 可以满足海量需求物联网中的数据存储、高速数据提取和复杂数据分析等工业领域。 这是一个错误修复版本,主要包括: IOTDB-1049修复 Nullpointer 异常和 Last 查询中的删除错误 IOTDB-1060 支持对不带 where 子句的 delete 语句的完全删除 IOTDB-1068 修复时间序列元数据缓存错误 IOTDB-1069 限制刷新 memtable 数,以避免在禁用 mem_control 时发生 OOM IOTDB-1077 在 java 会话中添加 insertOneDeviceRecords API IOTDB-1106 Delete timeseries 语句将错误地删除其他时间序列 修复了 grafana 值时间位置错误 GetObject 返回了所有字符串数据类型 修复 Sync 失败:Socket被 peer 关闭 DeleteData 方法在 Session 中存在,但在 SessionPool 中不存在。 在 iotdb-engine.properties 中添加 thrift_max_frame_size 删除所有数据后修复最后的错误结果 在客户端中 print 更多insert error message 将 enablePartition 参数公开到 iotdb-engines.properpties 中 详情可查看发布说明。

优秀的个人博客,低调大师

干货丨时序数据库DolphinDB插件开发教程

DolphinDB支持动态加载外部插件,以扩展系统功能。插件用C++编写,需要编译成".so"或".dll"共享库文件。本文着重介绍开发插件的方法和注意事项,并详细介绍以下几个具体场景的插件开发流程: 如何开发支持时间序列数据处理的插件函数 如何开发用于处理分布式SQL的聚合函数 如何开发支持新的分布式算法的插件函数 如何开发支持流数据处理的插件函数 如何开发支持外部数据源的插件函数 1. 如何开发插件 1.1 基本概念 DolphinDB的插件实现了能在脚本中调用的函数。一个插件函数可能是运算符函数(Operator function),也可能是系统函数(System function),它们的区别在于,前者接受的参数个数小于等于2,而后者的函数可以接受任意个参数,并支持会话的访问操作。 开发一个运算符函数,需要编写一个原型为ConstantSP (const ConstantSP& a, const ConstantSP& b)的C++函数。当函数参数个数为2时,a和b分别为插件函数的第一和第二个参数;当参数个数为1时,b是一个占位符,没有实际用途;当没有参数时,a和b均为占位符。 开发一个系统函数,需要编写一个原型为ConstantSP (Heap* heap, vector<ConstantSP>& args)的C++函数。用户在DolphinDB中调用插件函数时传入的参数,都按顺序保存在C++的向量args中。heap参数不需要用户传入。 函数原型中的ConstantSP可以表示绝大多数DolphinDB对象(标量、向量、矩阵、表,等等)。其他常用的派生自它的变量类型有VectorSP(向量)、TableSP(表)等。 1.2 创建变量 创建标量,可以直接用new语句创建头文件ScalarImp.h中声明的类型对象,并将它赋值给一个ConstantSP。ConstantSP是一个经过封装的智能指针,会在变量的引用计数为0时自动释放内存,因此,用户不需要手动delete已经创建的变量: ConstantSP i = new Int(1); // 相当于1i ConstantSP d = new Date(2019, 3, 14); // 相当于2019.03.14 ConstantSP s = new String("DolphinDB"); // 相当于"DolphinDB" ConstantSP voidConstant = new Void(); // 创建一个void类型变量,常用于表示空的函数参数 头文件Util.h声明了一系列函数,用于快速创建某个类型和格式的变量: VectorSP v = Util::createVector(DT_INT, 10); // 创建一个初始长度为10的int类型向量 v->setInt(0, 60); // 相当于v[0] = 60 VectorSP t = Util::createVector(DT_ANY, 0); // 创建一个初始长度为0的any类型向量(元组) t->append(new Int(3)); // 相当于t.append!(3) t->get(0)->setInt(4); // 相当于t[0] = 4 // 这里不能用t->setInt(0, 4),因为t是一个元组,setInt(0, 4)只对int类型的向量有效 ConstantSP seq = Util::createIndexVector(5, 10); // 相当于5..14 int seq0 = seq->getInt(0); // 相当于seq[0] ConstantSP mat = Util::createDoubleMatrix(5, 10);// 创建一个10行5列的double类型矩阵 mat->setColumn(3, seq); // 相当于mat[3] = seq 1.3 异常处理和参数校验 1.3.1 异常处理 插件开发时的异常抛出和处理,和一般C++开发中一样,都通过throw关键字抛出异常,try语句块处理异常。DolphinDB在头文件Exceptions.h中声明了异常类型。 插件函数若遇到运行时错误,一般抛出RuntimeException。 在插件开发时,通常会校验函数参数,如果参数不符合要求,抛出一个IllegalArgumentException。常用的参数校验函数有: ConstantSP->getType():返回变量的类型(int, char, date等等),DolphinDB的类型定义在头文件Types.h中。 ConstantSP->getCategory():返回变量的类别,常用的类别有INTEGRAL(整数类型,包括int, char, short, long等)、FLOATING(浮点数类型,包括float, double等)、TEMPORAL(时间类型,包括time, date, datetime等)、LITERAL(字符串类型,包括string, symbol等),都定义在头文件Types.h中。 ConstantSP->getForm():返回变量的格式(标量、向量、表等等),DolphinDB的格式定义在头文件Types.h中。 ConstantSP->isVector():判断变量是否为向量。 ConstantSP->isScalar():判断变量是否为标量。 ConstantSP->isTable():判断变量是否为表。 ConstantSP->isNumber():判断变量是否为数字类型。 ConstantSP->isNull():判断变量是否为空值。 ConstantSP->getInt():获得变量对应的整数值,常用于判断边界。 ConstantSP->getString():获得变量对应的字符串。 ConstantSP->size():获得变量的长度。 更多参数校验函数一般在头文件CoreConcept.h的Constant类方法中。 1.3.2 参数校验的范例 本节将开发一个插件函数用于求非负整数的阶乘,返回一个long类型变量。 DolphinDB中long类型的最大值为2^63 - 1,能表示的阶乘最大为25!,因此只有0~25范围内的参数是合法的。 #include "CoreConcept.h" #include "Exceptions.h" #include "ScalarImp.h" ConstantSP factorial(const ConstantSP &n, const ConstantSP &placeholder) { string syntax = "Usage: factorial(n). "; if (!n->isScalar() || n->getCategory() != INTEGRAL) throw IllegalArgumentException("factorial", syntax + "n must be an integral scalar."); int nValue = n->getInt(); if (nValue < 0 || nValue > 25) throw IllegalArgumentException("factorial", syntax + "n must be a non-negative integer less than 26."); long long fact = 1; for (int i = nValue; i > 0; i--) fact *= i; return new Long(fact); } 1.4 调用DolphinDB内置函数 有时会需要调用DolphinDB的内置函数对数据进行处理。有些类已经定义了一些常用的内置函数作为方法: VectorSP v = Util::createIndexVector(1, 100); ConstantSP avg = v->avg(); // 相当于avg(v) ConstantSP sum2 = v->sum2(); // 相当于sum2(v) v->sort(false); // 相当于sort(v, false) 如果需要调用其它内置函数,插件函数的类型必须是系统函数。通过heap->currentSession()->getFunctionDef函数获得一个内置函数,然后用call方法调用它。如果该内置函数是运算符函数,应调用原型call(Heap, const ConstantSP&, const ConstantSP&);如果是系统函数,应调用原型call(Heap, vector<ConstantSP>&)。以下是调用内置函数cumsum的一个例子: ConstantSP v = Util::createIndexVector(1, 100); v->setTemporary(false); // v的值可能在内置函数调用时被修改。如果不希望它被修改,应先调用setTemporary(false) FunctionDefSP cumsum = heap->currentSession()->getFunctionDef("cumsum"); ConstantSP result = cumsum->call(heap, v, new Void()); // 相当于cumsum(v),这里的new Void()是一个占位符,没有实际用途 2. 如何开发支持时间序列数据处理的插件函数 DolphinDB的特色之一在于它对时间序列有良好支持。 本章以编写一个msum函数的插件为例,介绍如何开发插件函数支持时间序列数据处理。 时间序列处理函数通常接受向量作为参数,并对向量中的每个元素进行计算处理。在本例中,msum函数接受两个参数:一个向量和一个窗口大小。它的原型是: ConstantSP msum(const ConstantSP &X, const ConstantSP &window); msum函数的返回值是一个和输入向量同样长度的向量。本例为简便起见,假定返回值是一个double类型的向量。可以通过Util::createVector函数预先为返回值分配空间: int size = X->size(); int windowSize = window->getInt(); ConstantSP result = Util::createVector(DT_DOUBLE, size); 在DolphinDB插件编写时处理向量,可以循环使用getDoubleConst,getIntConst等函数,批量获得一定长度的只读数据,保存在相应类型的缓冲区中,从缓冲区中取得数据进行计算。这样做的效率比循环使用getDouble,getInt等函数要高。本例为简便起见,统一使用getDoubleConst,每次获得长度为Util::BUF_SIZE的数据。这个函数返回一个const double*,指向缓冲区头部: double buf[Util::BUF_SIZE]; INDEX start = 0; while (start < size) { int len = std::min(Util::BUF_SIZE, size - start); const double *p = X->getDoubleConst(start, len, buf); for (int i = 0; i < len; i++) { double val = p[i]; // ... } start += len; } 在本例中,msum将计算X中长度为windowSize的窗口中所有数据的和。可以用一个临时变量tmpSum记录当前窗口的和,每当窗口移动时,只要给tmpSum增加新窗口尾部的值,减去旧窗口头部的值,就能计算得到当前窗口中数据的和。为了将计算值写入result,可以循环用result->getDoubleBuffer获取一个可读写的缓冲区,写完后使用result->setDouble函数将缓冲区写回数组。setDouble函数会检查给定的缓冲区地址和变量底层储存的地址是否一致,如果一致就不会发生数据拷贝。在多数情况下,用getDoubleBuffer获得的缓冲区就是变量实际的存储区域,这样能减少数据拷贝,提高性能。 需要注意的是,DolphinDB用double类型的最小值(已经定义为宏DBL_NMIN)表示double类型的NULL值,要专门判断。 返回值的前windowSize - 1个元素为NULL。可以对X中的前windowSize个元素和之后的元素用两个循环分别处理,前一个循环只计算累加,后一个循环执行加和减的操作。最终的实现如下: ConstantSP msum(const ConstantSP &X, const ConstantSP &window) { INDEX size = X->size(); int windowSize = window->getInt(); ConstantSP result = Util::createVector(DT_DOUBLE, size); double buf[Util::BUF_SIZE]; double windowHeadBuf[Util::BUF_SIZE]; double resultBuf[Util::BUF_SIZE]; double tmpSum = 0.0; INDEX start = 0; while (start < windowSize) { int len = std::min(Util::BUF_SIZE, windowSize - start); const double *p = X->getDoubleConst(start, len, buf); double *r = result->getDoubleBuffer(start, len, resultBuf); for (int i = 0; i < len; i++) { if (p[i] != DBL_NMIN) // p[i] is not NULL tmpSum += p[i]; r[i] = DBL_NMIN; } result->setDouble(start, len, r); start += len; } result->setDouble(windowSize - 1, tmpSum); // 上一个循环多设置了一个NULL,填充为tmpSum while (start < size) { int len = std::min(Util::BUF_SIZE, size - start); const double *p = X->getDoubleConst(start, len, buf); const double *q = X->getDoubleConst(start - windowSize, len, windowHeadBuf); double *r = result->getDoubleBuffer(start, len, resultBuf); for (int i = 0; i < len; i++) { if (p[i] != DBL_NMIN) tmpSum += p[i]; if (q[i] != DBL_NMIN) tmpSum -= q[i]; r[i] = tmpSum; } result->setDouble(start, len, r); start += len; } return result; } 3. 如何开发用于处理分布式SQL的聚合函数 在DolphinDB中,SQL的聚合函数通常接受一个或多个向量作为参数,最终返回一个标量。在开发聚合函数的插件时,需要了解如何访问向量中的元素。 DolphinDB中的向量有两种存储方式。一种是常规数组,数据在内存中连续存储;另一种是大数组,其中的数据分块存储。 本章将以编写一个求几何平均数的函数为例,介绍如何开发聚合函数,重点关注数组中元素的访问。 3.1 聚合函数范例 几何平均数geometricMean函数接受一个向量作为参数。为了防止溢出,一般采用其对数形式计算,即 geometricMean([x1, x2, ..., xn]) = exp((log(x1) + log(x2) + log(x3) + ... + log(xn))/n) 为了实现这个函数的分布式版本,可以先开发聚合函数插件logSum,用以计算某个分区上的数据的对数和,然后用defg关键字定义一个Reduce函数,用mapr关键字定义一个MapReduce函数。 在DolphinDB插件开发中,对数组的操作通常要考虑它是常规数组还是大数组。可以用isFastMode函数判断: ConstantSP logSum(const ConstantSP &x, const ConstantSP &placeholder) { if (((VectorSP) x)->isFastMode()) { // ... } else { // ... } } 如果数组是常规数组,它在内存中连续存储。可以用getDataArray函数获得它数据的指针。假定数据是以double类型存储的: if (((VectorSP) x)->isFastMode()) { int size = x->size(); double *data = (double *) x->getDataArray(); double logSum = 0; for (int i = 0; i < size; i++) { if (data[i] != DBL_NMIN) // is not NULL logSum += std::log(data[i]); } return new Double(logSum); } 如果数据是大数组,它在内存中分块存储。可以用getSegmentSize获得每个块的大小,用getDataSegment获得首个块的地址。它返回一个二级指针,指向一个指针数组,这个数组中的每个元素指向每个块的数据数组: // ... else { int size = x->size(); int segmentSize = x->getSegmentSize(); double **segments = (double **) x->getDataSegment(); INDEX start = 0; int segmentId = 0; double logSum = 0; while (start < size) { double *block = segments[segmentId]; int blockSize = std::min(segmentSize, size - start); for (int i = 0; i < blockSize; i++) { if (block[i] != DBL_NMIN) // is not NULL logSum += std::log(block[i]); } start += blockSize; segmentId++; } return new Double(logSum); } 在实际开发中,数组的底层存储不一定是double类型。用户需要考虑具体类型。本例采用了泛型编程统一处理不同类型,具体代码参见附件。 3.2 在DolphinDB中调用函数 通常需要实现一个聚合函数的非分布式版本和分布式版本,系统会基于哪个版本更高效来选择调用这个版本。 在DolphinDB中定义非分布式的geometricMean函数: def geometricMean(x) { return exp(logSum::logSum(x) \ count(x)) } 然后通过定义Map和Reduce函数,最终用mapr定义分布式的版本: def geometricMeanMap(x) { return logSum::logSum(x) } defg geometricMeanReduce(myLogSum, myCount) { return exp(sum(myLogSum) \ sum(myCount)) } mapr geometricMean(x) { geometricMeanMap(x), count(x) -> geometricMeanReduce } 这样就实现了geometricMean函数。 如果是在单机环境中执行这个函数,只需要在执行的节点上加载插件。如果有数据位于远程节点,需要在每一个远程节点加载插件。可以手动在每个节点执行loadPlugin函数,也可以用以下脚本快速在每个节点上加载插件: each(rpc{, loadPlugin, pathToPlugin}, getDataNodes()) 通过以下脚本创建一个分区表,验证函数: db = database("", VALUE, 1 2 3 4) t = table(take(1..4, 100) as id, rand(1.0, 100) as val) t0 = db.createPartitionedTable(t, `tb, `id) t0.append!(t) select geometricMean(val) from t0 group by id 3.3 随机访问大数组 可以对大数组进行随机访问,但要经过下标计算。用getSegmentSizeInBit函数获得块大小的二进制位数,通过位运算获得块的偏移量和块内偏移量: int segmentSizeInBit = x->getSegmentSizeInBit(); int segmentMask = (1 << segmentSizeInBit) - 1; double **segments = (double **) x->getDataSegment(); int index = 3000000; // 想要访问的下标 double result = segments[index >> segmentSizeInBit][index & segmentMask]; // ^ 块的偏移量 ^ 块内偏移量 3.4 应该选择哪种方式访问向量 上一章介绍了通过getDoubleConst,getIntConst等一族方法获得只读缓冲区,以及通过getDoubleBuffer,getIntBuffer等一族方法获得可读写缓冲区,这两种访问向量的方法。本章介绍了通过getDataArray和getDataSegment方法直接访问向量的底层存储。在实际开发中,前一种方法更通用,一般应该选择前一种方法。但在某些特别的场合(例如明确知道数据存储在大数组中,且知道数据的类型),可以采用第二种方法。 4. 如何开发支持新的分布式算法的插件函数 在DolphinDB中,Map-Reduce是执行分布式算法的通用计算框架。DolphinDB提供了mr函数和imr函数,使用户能通过脚本实现分布式算法。而在编写分布式算法的插件时,使用的同样是这两个函数。本章主要介绍如何用C++语言编写自定义的map, reduce等函数,并调用mr和imr两个函数,最终实现分布式计算。 4.1 分布式算法范例 本章将以mr为例,实现一个函数,求分布式表中相应列名的所有列平均值,介绍编写DolphinDB 分布式算法插件的整体流程,及需要注意的技术细节。 在插件开发中,用户自定义的map, reduce, final, term函数,可以是运算符函数,也可以是系统函数。 本例的map函数,对表的一个分区内对应列名的列做计算,返回一个长度为2的元组,分别包含数据的和,及数据非空元素的个数。具体实现如下: ConstantSP columnAvgMap(Heap *heap, vector<ConstantSP> &args) { TableSP table = args[0]; ConstantSP colNames = args[1]; double sum = 0.0; int count = 0; for (int i = 0; i < colNames->size(); i++) { string colName = colNames->getString(i); VectorSP col = table->getColumn(colName); sum += col->sum()->getDouble(); count += col->count(); } ConstantSP result = Util::createVector(DT_ANY, 2); result->set(0, new Double(sum)); result->set(1, new Int(count)); return result; } 本例的reduce函数,是对map结果的相加。DolphinDB的内置函数add就提供了这个功能,可以用heap->currentSession()->getFunctionDef("add")获得这个函数: FunctionDefSP reduceFunc = heap->currentSession()->getFunctionDef("add"); 本例的final函数,是对reduce结果中的数据总和sum和非空元素个数count做除法,求得所有分区中对应列的平均数。具体实现如下: ConstantSP columnAvgFinal(const ConstantSP &result, const ConstantSP &placeholder) { double sum = result->get(0)->getDouble(); int count = result->get(1)->getInt(); return new Double(sum / count); } 定义了map, reduce, final等函数后,将它们导出为插件函数(在头文件的函数声明前加上extern "C",并在加载插件的文本文件中列出这些函数),然后通过heap->currentSession->getFunctionDef获取这些函数,就能以这些函数为参数调用mr函数。如: FunctionDefSP mapFunc = Heap->currentSession()->getFunctionDef("columnAvg::columnAvgMap"); 在本例中,map函数接受两个参数table和colNames,但mr只允许map函数有一个参数,因此需要以部分应用的形式调用map函数,可以用Util::createPartialFunction将它包装为部分应用,实现如下: vector<ConstantSP> mapWithColNamesArgs {new Void(), colNames}; FunctionDefSP mapWithColNames = Util::createPartitalFunction(mapFunc, mapWithColNamesArgs); 用heap->currentSession()->getFunctionDef("mr")获得系统内置函数mr,调用mr->call方法,就相当于在DolphinDB脚本中调用mr函数。最后实现的columnAvg函数定义如下: ConstantSP columnAvg(Heap *heap, vector<ConstantSP> &args) { ConstantSP ds = args[0]; ConstantSP colNames = args[1]; FunctionDefSP mapFunc = heap->currentSession()->getFunctionDef("columnAvg::columnAvgMap"); vector<ConstantSP> mapWithColNamesArgs = {new Void(), colNames}; FunctionDefSP mapWithColNames = Util::createPartialFunction(mapFunc, mapWithColNamesArgs); // columnAvgMap{, colNames} FunctionDefSP reduceFunc = heap->currentSession()->getFunctionDef("add"); FunctionDefSP finalFunc = heap->currentSession()->getFunctionDef("columnAvg::columnAvgFinal"); FunctionDefSP mr = heap->currentSession()->getFunctionDef("mr"); // mr(ds, columnAvgMap{, colNames}, add, columnAvgFinal) vector<ConstantSP> mrArgs = {ds, mapWithColNames, reduceFunc, finalFunc}; return mr->call(heap, mrArgs); } 4.2 在DolphinDB中调用函数 如果是在单机环境中执行这个函数,只需要在执行的节点上加载插件。但如果有数据位于远程节点,需要在每一个远程节点加载插件。可以手动在每个节点执行loadPlugin函数,也可以用以下脚本快速在每个节点上加载插件: each(rpc{, loadPlugin, pathToPlugin}, getDataNodes()) 加载插件后,用sqlDS函数生成数据源,并调用函数: n = 100 db = database("dfs://testColumnAvg", VALUE, 1..4) t = db.createPartitionedTable(table(10:0, `id`v1`v2, [INT,DOUBLE,DOUBLE]), `t, `id) t.append!(table(take(1..4, n) as id, rand(10.0, n) as v1, rand(100.0, n) as v2)) ds = sqlDS(<select * from t>) columnAvg::columnAvg(ds, `v1`v2) 5.如何开发支持流数据处理的插件函数 在DolphinDB中,流数据订阅端可以通过一个handler函数处理收到的数据。订阅数据可以是一个数据表,或一个元组,由subsrciebeTable函数的msgAsTable参数决定。通常可以用handler函数对流数据进行过滤、插入另一张表等操作。 本章将编写一个handler函数。它接受的消息类型是元组。另外接受两个参数:一个是int类型的标量或向量indices,表示元组中元素的下标,另一个是一个表table。它将元组中对应下标的列插入到表中。 向表中添加数据的接口是bool append(vector<ConstantSP>& values, INDEX& insertedRows, string& errMsg),如果插入成功,返回true,并向insertedRows中写入插入的行数。否则返回false,并在errMsg中写入出错信息。插件的实现如下: ConstantSP handler(Heap *heap, vector<ConstantSP> &args) { ConstantSP indices = args[0]; TableSP table = args[1]; ConstantSP msg = args[2]; vector<ConstantSP> msgToAppend; for (int i = 0; i < indices->size(); i++) { int index = indices->get(i); msgToAppend.push_back(msg->get(index)); } INDEX insertedRows; string errMsg; table->append(msgToAppend, insertedRows, errMsg); return new Void(); } 在实际应用中,可能需要知道插入出错时的原因。可以引入头文件Logger.h,将出错信息写入日志中。注意需要在编译插件时加上宏定义-DLOGGING_LEVEL_2: // ... bool success = table->append(msgToAppend, insertedRows, errMsg); if (!success) LOG_ERR("Failed to append to table: ", errMsg); 可以用以下脚本模拟流数据写入,验证handler函数: loadPlugin("/path/to/PluginHandler.txt") share streamTable(10:0, `id`sym`timestamp, [INT,SYMBOL,TIMESTAMP]) as t0 t1 = table(10:0, `sym`timestamp, [SYMBOL,TIMESTAMP]) subscribeTable(, `t0, , , handler::handler{[1,2], t1}) t0.append!(table(1..100 as id, take(`a`b`c`d, 100) as symbol, now() + 1..100 as timestamp)) select * from t1 6.如何开发支持外部数据源的插件函数 在为第三方数据设计可扩展的接口插件时,有几个需要关注的问题: 数据源(Data source)。数据源是一个特殊的数据对象,包含了数据实体的元描述,执行一个数据源能获得数据实体,可能是表、矩阵、向量等等。用户可以提供数据源调用olsEx,randomForestClassifier等分布式计算函数,也可以调用mr,imr或ComputingModel.h中定义的更底层的计算模型做并行计算。DolphinDB的内置函数sqlDS就通过SQL表达式获取数据源。在设计第三方数据接口时,通常需要实现一个获取数据源的函数,它将大的文件分成若干个部分,每部分都表示数据的一个子集,最后返回一个数据源的元组。数据源一般用一个Code object表示,是一个函数调用,它的参数是元数据,返回一个表。 结构(Schema)。表的结构描述了表的列数,每一列的列名和数据类型。第三方接口通常需要实现一个函数,快速获得数据的表结构,以便用户在这个结构的基础上调整列名和列的数据类型。 IO问题。在多核多CPU的环境中,IO可能成为瓶颈。DolphinDB提供了抽象的IO接口,DataInputStream和DataOutputStream,这些接口封装了数据压缩,Endianness,IO类型(网络,磁盘,buffer等)等细节,方便开发。此外还特别实现了针对多线程的IO实现,BlockFileInputStream和BlockFileOutputStream。这个实现有两个优点: 实现计算和IO并行。A线程在处理数据的时候,后台线程在异步帮A线程预读取后面需要的数据。 避免了多线程的磁盘竞争。当线程个数增加的时候,如果并行往同一个磁盘上读写,性能会急剧下降。这个实现,会对同一个磁盘的读写串行化,从而提高吞吐量。 本章将介绍通常需要实现的几个函数,为设计第三方数据接口提供一个简单的范例。 6.1 数据格式描述 假定本例中的数据储存在平面文件数据库,以二进制格式按行存储,数据从文件头部直接开始存储。每行有四列,分别为id(按有符号64位长整型格式存储,8字节),symbol(按C字符串格式存储,8字节),date(按BCD码格式存储,8字节),value(按IEEE 754标准的双精度浮点数格式存储,8字节),每行共32字节。以下是一行的例子: 这一行的十六进制表示为: 0x 00 00 00 00 00 00 00 05 0x 49 42 4D 00 00 00 00 00 0x 02 00 01 09 00 03 01 03 0x 40 24 33 33 33 33 33 33 6.2 extractMyDataSchema函数 这个函数提取数据文件的表结构。在本例中,表结构是确定的,不需要实际读取文件。该函数提供了一个如何生成表结构的范例。它通过Util::createTable函数创建一张结构表: ConstantSP extractMyDataSchema(const ConstantSP &placeholderA, const ConstantSP &placeholderB) { ConstantSP colNames = Util::createVector(DT_STRING, 4); ConstantSP colTypes = Util::createVector(DT_STRING, 4); string names[] = {"id", "symbol", "date", "value"}; string types[] = {"LONG", "SYMBOL", "DATE", "DOUBLE"}; colNames->setString(0, 4, names); colTypes->setString(0, 4, types); vector<ConstantSP> schema = {colNames, colTypes}; vector<string> header = {"name", "type"}; return Util::createTable(header, schema); } 在实际开发中,可能需要以读取文件头等方式获得表结构。如何读文件将在后面介绍。 6.3 loadMyData函数 loadMyData函数读取文件,并输出一张DolphinDB表。给定一个文件的路径,可以通过Util::createBlockFileInputStream创建一个输入流,此后,可对这个流调用readBytes函数读取给定长度的字节,readBool读取下一个bool值,readInt读取下一个int值,等等。本例给loadMyData函数设计的语法为:loadMyData(path, [start], [length])。除了接受文件路径path,还接受两个int类型的参数start和length,分别表示开始读取的行数和需要读取的总行数。createBlockFileInputStream函数可以通过参数决定开始读取的字节数和需要读取的总字节数: ConstantSP loadMyData(Heap *heap, vector<ConstantSP> &args) { ConstantSP path = args[0]; long long fileLength = Util::getFileLength(path->getString()); size_t bytesPerRow = 32; int start = args.size() >= 2 ? args[1]->getInt() : 0; int length = args.size() >= 3 ? args[2]->getInt() : fileLength / bytesPerRow - start; DataInputStreamSP inputStream = Util::createBlockFileInputStream(path->getString(), 0, fileLength, Util::BUF_SIZE, start * bytesPerRow, length * bytesPerRow); char buf[Util::BUF_SIZE]; size_t actualLength; while (true) { inputStream->readBytes(buf, Util::BUF_SIZE, actualLength); if (actualLength <= 0) break; // ... } } 在读取数据时,通常将数据缓存到数组中,等待缓冲区满后批量插入。例如,假定要读取一个内容全为char类型字节的二进制文件,将它写入一个char类型的DolphinDB向量vec。最后返回只由vec一列组成的表: char buf[Util::BUF_SIZE]; VectorSP vec = Util::createVector(DT_CHAR, 0); size_t actualLength; while (true) { inputStream->readBytes(buf, Util::BUF_SIZE, actualLength); if (actualLength <= 0) break; vec->appendChar(buf, actualLength); } vector<ConstantSP> cols = {vec}; vector<string> colNames = {"col0"}; return Util::createTable(colNames, cols); 本节的完整代码请参考附件中的代码。在实际开发中,加载数据的函数可能还会接受表结构参数schema,按实际需要改变读取的数据类型。 6.4loadMyDataEx函数 loadMyData函数总是将数据加载到内存,当数据文件非常庞大时,工作机的内存很容易成为瓶颈。所以设计loadMyDataEx函数解决这个问题。它通过边导入边保存的方式,把静态的二进制文件以较为平缓的数据流的方式保存为DolphinDB的分布式表,而不是采用全部导入内存再存为分区表的方式,从而降低内存的使用需求。 loadMyDataEx函数的参数可以参考DolphinDB内置函数loadTextEx。它的语法是:loadMyDataEx(dbHandle, tableName, partitionColumns, path, [start], [length])。如果数据库中的表存在,则将导入的数据添加到已有的表result中。如果表不存在,则创建一张表result,然后添加数据。最后返回这张表: string dbPath = ((SystemHandleSP) db)->getDatabaseDir(); vector<ConstantSP> existsTableArgs = {new String(dbPath), tableName}; bool existsTable = heap->currentSession()->getFunctionDef("existsTable")->call(heap, existsTableArgs)->getBool(); // 相当于existsTable(dbPath, tableName) ConstantSP result; if (existsTable) { // 表存在,直接加载表 vector<ConstantSP> loadTableArgs = {db, tableName}; result = heap->currentSession()->getFunctionDef("loadTable")->call(heap, loadTableArgs); // 相当于loadTable(db, tableName) } else { // 表不存在,创建表 TableSP schema = extractMyDataSchema(new Void(), new Void()); ConstantSP dummyTable = DBFileIO::createEmptyTableFromSchema(schema); vector<ConstantSP> createTableArgs = {db, dummyTable, tableName, partitionColumns}; result = heap->currentSession()->getFunctionDef("createPartitionedTable")->call(heap, createTableArgs); // 相当于createPartitionedTable(db, dummyTable, tableName, partitionColumns) } 读取数据并添加到表中的代码实现采用了Pipeline框架。它的初始任务是一系列具有不同start参数的loadMyData函数调用,pipeline的follower函数是一个部分应用append!{result},相当于把整个读取数据的任务分成若干份执行,调用loadMyData分块读取后,将相应的数据通过append!插入表中。核心部分的代码如下: int sizePerPartition = 16 * 1024 * 1024; int partitionNum = fileLength / sizePerPartition; vector<DistributedCallSP> tasks; FunctionDefSP func = Util::createSystemFunction("loadMyData", loadMyData, 1, 3, false); int partitionStart = start; int partitionLength = length / partitionNum; for (int i = 0; i < partitionNum; i++) { if (i == partitionNum - 1) partitionLength = length - partitionLength * i; vector<ConstantSP> partitionArgs = {path, new Int(partitionStart), new Int(partitionLength)}; ObjectSP call = Util::createRegularFunctionCall(func, partitionArgs); // 将会调用loadMyData(path, partitionStart, partitionLength) tasks.push_back(new DistributedCall(call, true)); partitionStart += partitionLength; } vector<ConstantSP> appendToResultArgs = {result}; FunctionDefSP appendToResult = Util::createPartialFunction(heap->currentSession()->getFunctionDef("append!"), appendToResultArgs); // 相当于append!{result} vector<FunctionDefSP> functors = {appendToResult}; PipelineStageExecutor executor(functors, false); executor.execute(heap, tasks); 本节的完整代码请参考附件中的代码。用Pipeline框架实现数据的分块导入,只是一种思路。在具体开发时,可以采用ComputingModel.h中声明的StaticStageExecutor,也可以使用Concurrent.h中声明的线程模型Thread。实现方法有很多种,需要根据实际场景选择。 6.5myDataDS函数 myDataDS函数返回一个数据源的元组。每个数据源都是一个表示函数调用的Code object,可以通过Util::createRegularFunctionCall生成。执行这个对象可以取得对应的数据。以下是基于loadMyData函数产生数据源的一个例子: ConstantSP myDataDS(Heap *heap, vector<ConstantSP> &args) { ConstantSP path = args[0]; long long fileLength = Util::getFileLength(path->getString()); size_t bytesPerRow = 32; int start = args.size() >= 2 ? args[1]->getInt() : 0; int length = args.size() >= 3 ? args[2]->getInt() : fileLength / bytesPerRow - start; int sizePerPartition = 16 * 1024 * 1024; int partitionNum = fileLength / sizePerPartition; int partitionStart = start; int partitionLength = length / partitionNum; FunctionDefSP func = Util::createSystemFunction("loadMyData", loadMyData, 1, 3, false); ConstantSP dataSources = Util::createVector(DT_ANY, partitionNum); for (int i = 0; i < partitionNum; i++) { if (i == partitionNum - 1) partitionLength = length - partitionLength * i; vector<ConstantSP> partitionArgs = {path, new Int(partitionStart), new Int(partitionLength)}; ObjectSP code = Util::createRegularFunctionCall(func, partitionArgs); // 将会调用loadMyData(path, partitionStart, partitionLength) dataSources->set(i, new DataSource(code)); } return dataSources; } 教程中的完整代码见https://github.com/dolphindb/Tu

优秀的个人博客,低调大师

干货丨时序数据库DolphinDB作业管理概述

作业(Job)是DolphinDB中最基本的执行单位,可以简单理解为一段DolphinDB脚本代码在DolphinDB系统中的一次执行。Job根据阻塞与否可分成同步作业和异步作业。 同步作业 同步作业也称为交互式作业(Interactive Job),它的主要来源有: Web notebook DolphinDB GUI DolphinDB命令行界面 通过DolphinDB提供的各个编程语言API接口 由于这种类型的作业对实时性要求较高,DolphinDB在执行过程中会自动给予较高的优先级,使其更快地得到计算资源。 异步作业 异步作业是在DolphinDB后台执行的作业,包括: 通过submitJob或submitJobEx函数提交的批处理作业。 通过scheduleJob函数提交的定时作业。 Streaming 作业。 这类任务一般对结果的实时反馈要求较低,且需要长期执行,DolphinDB一般会给予较低的优先级。 子任务 在DolphinDB中,若数据表数据量过大,一般都需要进行分区处理。如果一个Job A里含有分区表的查询计算任务(如SQL查询),将会分解成多个子任务并送到不同的节点上并行执行,等待子任务执行完毕之后,再合并结果,继续Job A的执行。类似的,DolphinDB的分布式计算也会产生子任务。因此,Job也可以理解成一系列的子任务。 Worker与Executor DolphinDB是一个P2P架构的系统,即每一个Data Node的角色都是相同的,它们都可以执行来自用户提交的Job,而因为一个Job可能产生子任务,每个Data Node需要有负责Job内部执行的调度者,我们称它为Worker,它负责处理用户提交的Job,简单计算任务的执行,并执行Job的任务分解,任务分发,并汇集最终的执行结果。Job中分解出来的子任务将会被分发到集群中的Data Node上(也有可能是本地Data Node),并由Data Node上的Worker或Executor线程负责执行。 具体Worker与executor在执行job的时候主要有以下几种情况: 当一个表没有进行分区,对其查询的Job将会有Worker线程执行掉。 当一个表被分区存放在单机上时候,对其的查询Job可能会分解成多个子任务,并由该节点上的多个Executor线程执行,达到并行计算的效果。 当一个表被分区存储在DFS时,对其查询的Job可能会被分解成多个子任务,这些子任务会被分发给其他Node的Worker上执行,达到分布式计算的效果。 为了最大化性能,DolphinDB会将子任务发送到数据所在的Data Node上执行,以减少网络传输开销。比如: 对于存储在DFS中的分区表,Worker将会根据分区模式以及分区当前所在Data Node来进行任务分解与分发。 对于分布式计算,Worker将会根据数据源信息,发送子任务到相应的数据源Data Node执行。 Job调度 Job优先级 在DolphinDB中,Job是按照优先级进行调度的,优先级的取值范围为0-9,取值越高优先级则越高。对于优先级高的Job,系统会更及时地给与计算资源。每个Job一般默认会有一个default priority,取值为4,然后根据Job的类型又会有所调整。 Job调度策略 基于Job的优先级,DolphinDB设计了多级反馈队列来调度Job的执行。具体来说,系统维护了10个队列,分别对应10个优先级,系统总是分配线程资源给高优先级的Job,对于处于相同优先级的Job,系统会以round robin的方式分配线程资源给Job;当一个优先级队列为空的时候,才会处理低优先级的队列中的Job。 Job并行度 由于一个Job可能会分成多个并行子任务,DolphinDB的Job还拥有一个并行度parallelism,表示在一个Data Node上,将会最多同时用多少个线程来执行Job产生的并行任务,默认取值为2,可以认为是一种时间片单位。举个例子,若一个Job的并行度为2,Job产生了100个并行子任务,那么Job被调度的时候系统只会分配2个线程用于子任务的计算,因此需要50轮调度才能完成整个Job的执行。 Job优先级的动态变化 为了防止处于低优先级的Job被长时间饥饿,DolphinDB会适当降低Job的优先级。具体的做法是,当一个job的时间片被执行完毕后,如果存在比其低优先级的Job,那么将会自动降低一级优先级。当优先级到达最低点后,又回到初始的优先级。因此低优先级的任务迟早会被调度到,解决了饥饿问题。 设置Job的优先级 DolphinDB的Job的优先级可以通过以下方式来设置: 对于console、web notebook以及API提交上来的都属于interactive job,其优先级取值为min(4,一个可调节的用户最高优先级),因此可以通过改变用户自身的优先级值来调整。 对于通过submitJob提交上的batch job,系统会给与default priority,即为4。用户也可以使用submitJobEx函数来指定优先级。 定时任务的优先级无法改变,默认为4。 计算容错 DolphinDB database 的分布式计算含有一定的容错性,主要得益于分区副本冗余存储。当一个子任务被发送到一个分区副本节点上之后,若节点出现故障或者分区副本发生了数据校验错误(副本损坏),Job Scheduler(即某个Data Node的一个worke线程)将会发现这个故障,并且选择该分区的另一个副本节点,重新执行子任务。用户可以通过设置dfsReplicationFactor参数来调整这种冗余度。 计算与存储耦合以及作业之间的数据共享 DolphinDB的计算是尽量靠近存储的。DolphinDB之所以不采用计算存储分离,主要有以下几个原因: 计算与存储分离会出现数据冗余。考虑存储与计算分离的Spark+Hive架构,Spark应用程序之间是不共享存储的。若N个Spark应用程序从Hive读取某个表T的数据,那么首先T要加载到N个Spark应用程序的内存中,存在N份,这将造成机器内存的的浪费。在多用户场景下,比如一份tick数据可能会被多个分析人员共享访问,如果采取Spark那种模式,将会提高IT成本。 拷贝带来的延迟问题。虽然说现在数据中心逐渐配备了RDMA,NVMe等新硬件,网络延迟和吞吐已经大大提高。但是这主要还是在数据中心,DolphinDB系统的部署环境可能没有这么好的网络环境以及硬件设施,数据在网络之间的传输会成为严重的性能瓶颈。 综上这些原因,DolphinDB采取了计算与存储耦合的架构。具体来说: 对于内存浪费的问题,DolphinDB的解决方案是Job(对应Spark应用程序)之间共享数据。在数据经过分区存储到DolphinDB的DFS中之后,每个分区的副本都会有自己所属的节点,在一个节点上的分区副本将会在内存中只存在一份。当多个Job的子任务都涉及到同一个分区副本时,该分区副本在内存中可以被共享地读取,减少了内存的浪费。 对于拷贝带来的延迟问题,DolphinDB的解决方案是将计算发送到数据所在的节点上。一个Job根据DFS的分区信息会被分解成多个子任务,发送到分区所在的节点上执行。因为发送计算到数据所在的节点上相当于只是发送一段代码,网络开销大大减少。

优秀的个人博客,低调大师

Apache IoTDB 0.10.1 发布,物联网时序数据库

Apache IoTDB 0.10.1 发布了。Apache IoTDB(孵化中)是一个集成数据专为时间序列数据设计的管理引擎。它为用户提供以下服务:数据收集、存储和分析。由于其轻巧的结构,高性能和丰富的功能集,以及与 Apache Hadoop、Spark 和 Flink 的深度集成,IoTDB 可以满足海量需求物联网中的数据存储、高速数据提取和复杂数据分析等工业领域。 当前版本是 IoTDB 0.10.0 的错误修复版本,主要包括: 因 WAL 错误而导致 InsertTablet 反序列化 无法升级所有存储组 显示最新时间序列时插入时死锁 Windows 启动检查中的重命名文件或删除文件错误 Hive 连接器中的 BufferUnderflowException 未关闭的文件阅读器,有小的内存泄漏 并发查询吞吐量低 存在一些无序数据时查询结果不正确 在会话中将默认提取大小更改为 10000 修复设置的 rowLimit 和 rowOffset 错误 为那些在第一次启动后不能再修改的参数增加一个新的配置类型 改进 “group by” 查询性能 删除 v0.10 的日志可视化工具 修复许可证二进制 当查询存储在 HDFS 中的 TsFile 时,修复 BufferUnderflowException python 会话客户端版本 0.10.0 修复 selfCheck() 中的错误 修复 Tablet 中 MeasurementSchema 的文档 修复升级 system.properties 时崩溃,多次升级 mlog 的问题 改进 IoTDB 重新启动过程 删除 hive-serde 2.8.4 引入的 jol-core 依赖关系 由于许可证兼容性,删除 org.json 依赖项 修复加载额外的 tsfile 时设置历史版本的问题 更新说明

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 16 发布,开源时序数据库

InfluxDB 2.0.0 Beta 16 现已发布,具体更新内容如下: Breaking 19066:弃用不推荐使用的 /packages route tree 19116:支持更多类型的模板 envRef 默认值,并且需要明确的默认值 19104:从 API 中删除 orgs/labels嵌套路由。 Features 19075:从公共的 HTTP API list/read 调用中添加资源链接到堆栈的资源 19103:在达到极限时增强资源创建体验 19223:将 dashboards 命令添加到 Influx CLI 19225:允许 user onboarding时选择设置密码 18841:限制在 QueryBuilder 中建立的查询响应大小,要求有一个汇总窗口。 Bug 修复 19043:强制所有 influx CLI flagargs 有效 19188:存在多个查询时,dashboard cells 正确映射结果 19146:当切换到 UTC 时区时,dashboard cells 和 overlay 使用 UTC 作为查询时间 19222: Bucket名称不得包含引号 用户界面改进 19231:警报页面过滤器输入现在具有用于键盘导航的选项卡索引 更新说明:https://github.com/influxdata/influxdb/releases

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 15 发布,开源时序数据库

InfluxDB 2.0.0 Beta 15 现已发布,具体更新内容如下: Breaking 004:从 influxd 二进制文件中删除了 migration 命令。 18921:受限制的 UI 变量名称不与 Flux 保留字冲突 Features 18888:将事件源添加到 Influx 堆栈操作中 18910:添加堆栈的卸载功能 18912:删除不推荐使用的Influxpkg command tree 18997:将 Telegraf 管理命令添加到 Influx CLI 19030:为 influx CLI 配置文件启用动态目标 19029:离开 dashboard 会取消所有待处理的查询 19003:升级到 Flux v0.74.0 Bug 修复 18891:允许 0 为 Y 域的自定义设置最小值 18969:Single Stat cells 应再次在 Safari 中正确呈现 18974:将查询提交给使用过的变量时限制变量查询 19039:解决了切换 orgs 未正确重定向的问题 18989:在高级构建器中停止获取标签 更新说明:https://github.com/influxdata/influxdb/releases

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 14 发布,开源时序数据库

InfluxDB 2.0.0 Beta 14 现已发布,具体更新内容如下: Features 18758:扩展流入堆栈更新cmd,无需添加模板即可添加资源 18793:在新的 /api/v2/templates和 /api/v2/stacks public API 下标准化 InfluxDB 模板 18818:使用种类标识符扩展template Summary和 Diff 嵌套类型。 18857:Flux 更新至 v0.71.1 18805:为 Linux 添加了静态构建 Bug 修复 18878:不要覆盖通过 ldflags 设置的构建日期 18842:修复了导入一个 Check 后定义查询不可用的问题 18845:更新文档链接 更新说明:https://github.com/influxdata/influxdb/releases

优秀的个人博客,低调大师

Apache IoTDB 0.10.0 发布,物联网时序数据库

Apache IoTDB 0.10.0 已发布,Apache IoTDB(孵化中)是一个集成数据专为时间序列数据设计的管理引擎。它为用户提供以下服务:数据收集、存储和分析。由于其轻巧的结构,高性能和丰富的功能集,以及与 Apache Hadoop、Spark 和 Flink 的深度集成,IoTDB 可以满足海量需求物联网中的数据存储、高速数据提取和复杂数据分析等工业领域。 此版本是 IoTDB 的第三个主要版本,也是一个大版本更新,它提供了许多新功能和改进。部分新功能如下: IOTDB-217新增 GROUPBY 语法,如 select avg(s1) from root.sg.d1.s1 GROUP BY ([1, 50), 5ms) IOTDB-220 新增热加载配置功能 IOTDB-292新增加载外部 tsfile 功能 IOTDB-298 支持查询最新数据点,如 select last * from root IOTDB-305 在执行设备对齐时增加数值过滤功能 IOTDB-396 支持新的查询子句:disable align,如 select * from root disable align IOTDB-447 支持查询不存在的测量值和常量值 IOTDB-448 添加 IN 操作,如 where time in (1,2,3) IOTDB-456 支持 GroupByFill 查询,如 select last_value(s1) from root.sg.d1 GROUP BY ([1, 10), 2ms) FILL(int32[previousUntilLast]) IOTDB-497支持 Apache Flink Connector 与 IoTDB 的连接 IOTDB-565 支持 MQTT 协议,默认为禁用状态,在 iotdb-engine.properties 中打开 IOTDB-588 增加标签和属性管理 可从 0.9.x 进行在线升级 JDBC OSGi 可以使用,并增加了一个特性文件 IOTDB-700添加基于 OpenID Connect 的 JWT 访问作为用户名/密码的替代方案 IOTDB-715在 previousuntillast 中支持以前的时间范围 IOTDB-719在配置中增加avg_series_point_number_threshold IOTDB-734在 SQL 语法中增加对 Double / Floats 的 NaN 支持 详情查看发布公告。

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 12 发布,开源时序数据库

InfluxDB 2.0.0 Beta 12 现已发布,具体更新内容如下: Features 8279:通过堆栈将所有 pkg 应用程序设为有状态 18322:增加将堆栈现有的(平台中的)资源状态导出为 pkg 的功能。 18334:以改进的用法和长格式示例更新了 influx pkg 命令。 18344:使用版本和 User-Agent 扩展 Influx CLI。 18355:集成了 RedirectTo 功能,因此,CLOUD 用户现在可以在登录后导航回原始链接的页面 18392:在模板下合并 pkg influx 命令。这会除去部分 CLI 命令的嵌套。 18400:仪表板在 navigating away 后保持排序顺序 18480:允许任务在新选项卡中打开 Bug 修复 18331:在 DBRP 操作中,除了 ID 之外,还支持 organization name。 18335:当向 influx CLI 提供意外错误时,禁用失败。 18345:让 influx 删除cmd respect 配置 18385:在读取时强制执行 pkger 的存储初始化。 18434:Backfill pkger 中直方图缺少的 fillColumns 字段。 18471:在切换功能时通知用户如何退出演示模式 UI Improvements 18319:在存储区列表中显示存储区 ID 并启用 1 单击复制 18361:Tokens list 现在与其他 resource lists 一致 18346:切换变量时减少被 hydrated 的变量的数量 18447:重新设计仪表板单元的加载指示器,使其更加明显 更新说明:https://github.com/influxdata/influxdb/releases

优秀的个人博客,低调大师

Apache IoTDB 0.9.3 发布,物联网时序数据库

Apache IoTDB0.9.3 已经发布了,这是 0.9 系列一个小版本,主要修了一个严重的 bug:如果执行了错误的元数据操作,比如删除一个不存在的存储组,重启后元数据为空。因为重启重做元数据日志时,遇到异常直接将元数据模块置空了,修复方法是跳过错误的元数据日志。 具体更新内容包括: [IOTDB-531] 修复 JDBC UTL 不支持域名的 bug [IOTDB-563] 修复 pentaho 无法被下载的问题 [IOTDB-608] 重启时跳过错误的元数据日志 [IOTDB-634] 修复将底层文件系统设置为 HDFS 时的数据合并问题 [IOTDB-636] 修复Grafana 连接器不使用正确的时间粒度问题 [IOTDB-528] 增加 Grafana 连接器的降采样方式 [IOTDB-635] 当 Grafana 对数据类型使用错误的聚合时,修改为通用的聚合函数 last 重试 移除官网关于加载外部 TsFile的 文档(这个功能是 0.10 版本的,还没发布) 详情查看发布公告。 此外,IoTDB 官网进行了大升级:http://iotdb.apache.org/zh,界面更新,并且新增了中英文的设计文档。

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 8 发布,开源时序数据库

InfluxDB 2.0.0 Beta 8现已发布,具体更新内容如下: Features 17490:influx config -,切换回先前的激活配置 17581:介绍新的导航菜单 17595:将-f(--file)选项添加到influx query和influx task命令 17498:添加了对命令行选项的支持,以限制查询的内存 Bug 修复 17612:在仪表板索引视图中修复卡的大小和布局问题 17651:修复了默认为黑色的检查图字体和线条,导致图不可读 17660:修复了向资源添加标签时文本换行显示问题和弹出窗口大小调整错误 17670:尊重已编译查询的当前时间(如果已提供) 17692:更新giraffe以固定刻度之间的间距 17694:修复了 Flux 功能列表中的错别字 17701:在 Safari 的脚本编辑器中允许鼠标光标 17609:修复了变量不能使用其他变量的问题 UI Improvements 7583:更新“Alerts”页面的布局以在所有屏幕尺寸上工作 17657:对“Getting Started”页面上的仪表板进行了最新修改 更新说明:https://github.com/influxdata/influxdb/releases

优秀的个人博客,低调大师

InfluxDB 2.0.0 Beta 7 发布,开源时序数据库

InfluxDB 2.0.0 Beta 7现已发布,具体更新内容如下: Features 17232:允许仪表板以浅色模式显示(可选) 17273:为 influx cli 添加 shell 完成命令 17353:通过 meta.name 字段使所有 pkg 资源唯一 17363:创建后无法再检索 Telegraf 配置令牌,但是可以在设置 Telegraf 后创建新令牌 17400:可以通过 cli 按名称删除 bucket 17396:添加模块以将行数据写入指定的 url、org 和 bucket 17448:为 pkger 堆栈、有状态的程序包管理添加基础 17462:标记以禁用任务计划 17470:添加了将 cli 输出作为 json 输出并隐藏表头的功能 17472:添加一种通过 cli 切换配置的简便方法 Bug 修复 17240:NodeJS 徽标在 Firefox 中正确显示 17363:修复了 Telegraf 配置错误,其中系统 buckets 出现在 buckets 下拉列表中 17391:修复了阈值检查错误,当字段名称中有空格时,无法创建检查 17384:重新使用由迭代器构建的切片以减少分配 17404:更新了重复检查错误消息,使其更加明确和可操作 UI Improvements 17291:重新设计 OSS 登录页面 17297:显示板没有单元格时显示图形 更新说明: https://github.com/influxdata/influxdb/releases

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册