开篇引文在上一篇文章中,我们提到了ApacheIoTDB是如何支持Nullable的。IoTDB使用NaN表示没有值。经过查询发现,NaN代表的是当前数据类型的默认值。价值。例如:FLOAT的默认值为0.0。那么这个结果就是v.0.11.2版本的行为,但是从NaN的设计来看,这是一个错误的行为,或者说这是一个bug,在IoTDB-1158的讨论区我们也提到了这个,它有已在master中修复。那么今天就来说说吧。有了NaN,我们还需要为ApacheIoTDB添加Nallable语法支持吗?Nallable的本质是我们可能需要考虑两个维度来支持任何功能。一是业务需求。客观的业务场景需要的功能一定是我们需要解决的。另一个是领域标准。对于时间序列数据库,我们必须考虑传统的数据库标准。我们必须考虑时序域中的标准。从业务的角度来看,业务期望的是当某个传感器由于某种原因无法提供当前时刻的值时,需要查询的是业务所感知的。然后IoTDB提供了NaN策略来支持Float和Double等值不存在,NaN(Notanumber)。在字段标准方面,传统数据库是支持NOTNULL等语法的,也就是说如果字段类型声明没有声明NOTNULL,默认情况下用户可以插入整行而不携带该字段的值,但声明NOTNULL那么每个插入语句都必须携带当前值。今天,在本文的最后,我们将使用ApacheIoTDBMaster代码查看对NaN的支持。BasedonCommit:b986cd1e5f类似(InfluxDB)的行为,有时候我们买东西的时候经常会货比三家,不管这个东西是好是坏,是贵还是贵。便宜,我们看看其他商家的质量和价格,可以帮助我们做出判断。那么,正在学习ApacheIoTDB的朋友们,我也建议对InfluxDB有一定的了解。InfluxDB当前的稳定版本是1.8.4。下面以1.8.4为例,体验一下InfluxDB是如何支持null的。二进制安装我们今天是在macOS下操作,首先确保你已经安装了brew工具。然后我们可以安装InfluxDBv1.8.4.brewupdatebrewinstallinfluxdb启动InfluxDB服务influxd客户端连接并创建测试数据库jincheng:~jincheng.sunjc$influx-precisionrfc3339连接到http://localhost:8086版本1.8.4InfluxDBshellversion:1.8.4CREATEDATABASE"lemming";uselemming;INSERTcpu,host=LemmingServer,region=zh_hzvalue=1.68SELECT*FROMcpu;Nullable支持单值insertNull(参考InfluxDB的历史资料)https://github.com/influxdata/influxdb/issues/7722https://github.com/influxdata/influxdb/pull/2429https://github.com/influxdata/influxdb/blob/v1.8.4/CHANGELOG.md>插入cpu,host=LemmingServer,region=zh_hzvalue=ERR:{"error":"unabletoparse'cpu,host=LemmingServer,region=zh_hzvalue=':缺少字段值"}INSERTcpu,host=LemmingServer,region=zh_hzvalue=''ERR:{"error":"unabletoparse'cpu,host=LemmingServer,region=zh_hzvalue=''':invalidboolean"}>INSERTcpu,host=LemmingServer,region=zh_hzvalue=nullERR:{"错误":"无法解析'cpu,host=LemmingServer,region=zh_hzvalue=null':invalidnumber"}上面的现象说明InfluxDB本身是不支持插入单值的Null的,所以这和IoTDB的现状是一样的,我们来看看多值时的“现象”同时插入。多插入Null正常插入:>INSERTtemperature,machine=unit42,type=assemblyexternal=25,internal=37>select*fromtemperature;name:temperaturetimeexternalinternalmachinetype-------------------------------2021-02-24T04:03:35.634296Z2537unit42assemblyinternal为空:>INSERTtemperature,machine=unit42,type=assemblyexternal=25,internal=ERR:{"error":"unabletoparse'temperature,machine=unit42,type=assemblyexternal=25,internal=':missingfieldvalue"}internal为null:>INSERTtemperature,machine=unit42,type=assemblyexternal=25,internal=nullERR:{"error":"unabletoparse'temperature,machine=unit42,type=assemblyexternal=25,internal=null':invalidnumber"}不携带internal:>INSERTtemperature,machine=unit42,type=assemblyexternal=25>select*fromtemperature;name:temperaturetimeexternal内部机器类型--------------------------------2021-02-24T04:03:35.634296Z2537unit42作为sembly2021-02-24T04:05:37.18028Z25unit42assembly所以根据目前的表现,InfulxDBv1.8.4不支持直接插入Null,但是可以支持IoTDBMaster在插入多个值时不携带某个值,即,空值Behavior(b986cd1e5f)我们基于b986cd1e5f编译,如下:启动服务和CLI启动服务,如下:jincheng:apache-iotdb-0.12.0-SNAPSHOT-all-binjincheng.sunjc$nohupsbin/start-server.sh>/dev/null2>&1&[1]22357请注意,我们的最新版本目录已更改`apache-iotdb-0.12.0-SNAPSHOT-all-bin`。连接服务进入CLI,如下:sbin/start-cli.sh-h127.0.0.1-p6667-uroot-pwrootNaN支持先进行一些初始化,如下:SETSTORAGEGROUPTOroot.lnCREATETIMESERIESroot.ln.wf01.wt01.statusWITHDATATYPE=BOOLEAN,ENCODING=PLAINCREATETIMESERIESroot.ln.wf01.wt01.temperatureWITHDATATYPE=FLOAT,ENCODING=RLE单值插入INSERTINTOroot.ln.wf01.wt01(timestamp,状态)值(100,真);INSERTINTOroot.ln.wf01.wt01(timestamp,temperature)values(200,20.71)我们可以看到正常插入单个值是可以的。同时,我们在查询的时候,如果同一个ts的某些值不存在的时候,我们查询出来显示为null,用户可以感知多个时间序列时间对齐到时哪些字段为null形成一行数据。异常插入IoTDB>INSERTINTOroot.ln.wf01.wt01(timestamp,temperature)values(200,)Msg:401:将SQL解析为物理计划时出错:第1:64行不匹配的输入')'期望{NOW,TRUE,FALSE,'-','.','NaN',INT,EXPONENT,DATETIME,DOUBLE_QUOTE_STRING_LITERAL,SINGLE_QUOTE_STRING_LITERAL}IoTDB>插入root.ln.wf01.wt01(timestamp,temperature)values(200,'')Msg:313:无法插入测量值[temperature]由Forinputstring:"''"IoTDB>INSERTINTOroot.ln.wf01.wt01(timestamp,temperature)values(200,null)Msg:401:ErroroccurredwhileparsingSQLtophysicalplan:line1:64mismatchedinput'null'expecting{NOW,TRUE,FALSE,'-','.','NaN',INT,EXPONENT,DATETIME,DOUBLE_QUOTE_STRING_LITERAL,SINGLE_QUOTE_STRING_LITERAL}IoTDB>这个现象其实是相关的到InfluxDB的外观是一样的,我们这样设计也是合理的,因为我们是一个强类型系统,如果声明了FLOAT类型,那么FLOAT值必须是插入。那么我们再看看多值插入的行为。多值插入正常InsertINSERTINTOroot.ln.wf01.wt01(timestamp,status,temperature)values(200,false,20.71)以上一切正常,注意我们插入的时间戳是200,那么会被覆盖同一时间戳的前一个值。插入缺失值INSERTINTOroot.ln.wf01.wt01(timestamp,status,temperature)values(200,false,)INSERTINTOroot.ln.wf01.wt01(timestamp,status,temperature)values(200,false,null)INSERTINTOroot.ln.wf01.wt01(timestamp,status,temperature)values(200,false,NaN)其实大家发现多值插入支持和单值插入一样。一列的值,那么values中必须有一个合法的值才能插入。如果没有业务价值,IoTDB提供NaN表示没有具体的价值。当然,NaN不仅可以用在插入多个值的时候,也可以用在插入单个值的时候。例如:文末提问:为什么需要NaN?看到这里,你可能会有疑问。由于我插入的时候没有值,所以不需要插入。查询时,IoTDB可以显示一行按时间对齐的数据。对应时间戳中不存在的值用null显示。那么为什么我们仍然使用NaN呢?这个跟业务有关系,比如:某个列的值显示为null,是不是设备当时没有发送这个值,或者虽然设备上发送了这个值,但是由于导致无法成功插入的业务程序错误(或非法值)?这个问题很难回答,因为:如果设备没有上传过,我们就不插入这个值,查询的时候是null。设备虽然上传了,但是业务代码有bug,插入不成功,查询的时候也是null,但是如果我们有NaN支持,那么我们把值插入到每个时间戳对应的所有列中业务代码。如果没有值或者值不合法,那么我们会显式设置一个NaN,表示此时业务本身没有收到合法的值。如果该值为null,说明该业务当时没有收到设备的任何数据事件,也没有进行任何insert动作,所以查询时为null,可以很容易定位到问题。回到一开始的问题,有了NaN,我们还需要给ApacheIoTDB增加Nallable语法支持吗?NaNisnotequaltoNullableNaN解决了Float/Double在某些场景下的业务问题,但是不支持其他非数值类型,比如今天涉及到的Boolean类型。从语义的角度来看,非数值类型不应该有NaN设计,所以关于非数值类型的Null的考虑值得进一步讨论。下一步是什么?我们希望每篇文章都针对用户提到的问题进行讨论。下一篇我们会讲InfluxDB和IoTDB如何解决以下朋友的问题:如果你有答案,也可以提前留言,看看你和我的想法是不是一样……:)我们'今天就到这里吧,下次见。作者介绍孙金城,51CTO社区编辑,ApacheFlinkPMC成员,ApacheBeamCommitter,ApacheIoTDBPMC成员,ALC北京成员,Apache神鱼导师,Apache软件基金会成员。专注于技术领域的流计算和时序数据存储。
