一、背景介绍
在时序数据库中运行场景中,会由于各种原因导致数据无法按时到达,会产生乱序数据。 部分项目的采集数据也是单点采集传输,如果没有乱序数据处理,会产生大量缺省值情况,影响数据库数据的压缩和计算。所以时序数据库需要支持乱序数据处理的时间窗口(disorder_interval),乱序时间窗口是基于每个表的数据时间线,每个表拥有自己的时间窗口,如果表中最新数据时间点是T, 则该表的乱序时间窗口范围在[T - disorder_interval, T],在时间范围内到达的乱序数据进行处理,超出时间范围的乱序数据被丢弃,丢弃信息记录在日志中。
由于乱序窗口的存在,如果向时序表误操作插入了一条未来时间戳的数据,会导致乱序窗口的更新,从而导致当前时间戳的正常数据无法被写入,所以需要向用户提供一种可以删除未来数据,同时将失败数据回收的工具。
二、整体设计
通过 kwdbAdmin 工具给 DBA 等提供维护工具,DBA 可以删除未来数据并重新导入写入失败的数据,该失败数据是由于未来数据提前插入从而导致部分数据无法正常写入。数据库与 kwdbAdmin 之间通过进程间通信完成接口调用。未成功写入的数据通过日志的方式输出到日志文件,当调用 kwdbAdmin 删除未来数据后可以调用 show_disorder_data 命令筛选出失败的数据,并以 csv 格式导入到数据库中。

2.1 kwdbAdmin flag 命令支持
DBA 等数据库管理人员可以通过数据库管理工具 kwdbAdmin 调用该命令完成未来数据删除,及筛选出未成功写入的数据。
2.1.1 truncate_data 命令
系统提供工具可以清理未来时间戳的数据,命令如下:
kwdbAdmin truncate_data –table database.table [datetime]
该命令将把database.table数据表内,大于当前系统时间的数据标记删除。也可以通过[datetime]参数指定要删除数据的最小时间(必须大于当前系统时间)。用于设置待删除数据的最小时间,格式:YYYY-MM-DD:HH-MM-SS.sss,精度可以到毫秒。该时间必须大于等于当前系统时间,未设置时默认等于当前系统时间。命令解析流程如下:
• 检查命令参数个数和命名是否正确,检查关键字 “–table”,拆分库名m_db和表名m_table;
• Datetime若大于当前系统时间,进行解析,解析失败输出提示信息,当用户未设置时,将当前系统时间戳传给消息队列。
• 将库名m_db、表名m_table和解析后的时间戳传给消息队列。

时间戳解析处理逻辑如下,支持时间格式为%Y-%m-%d:%H-%M-%S或%Y-%m-%d:%H-%M-%S.sss
• 根据指定格式对时间字符进行解析,失败则提示用户格式错误。否则获取到struct tm类型的时间戳t_tm,继续对字符串中毫秒进行解析。
• 检查毫秒是否设置,未设置则t_ms=0,否则将毫秒字符串转换成int类型t_ms;
• 将tm结构体(t_tm)转换成uint_64类型,与t_ms组合成时间戳。

2.1.2 show_disorder_data 命令
由于未来数据导致的写入失败的数据会记录到日志文件中,输出逻辑如下:
• 获取当前行的库名和表名,遍历行中每列,根据列的类型转换成string类型。
• 将string类型的列值转换 csv 格式后,将所有列值组合成行,逗号分隔,格式:时间戳,列1,列2,…,
• 以日志的方式输出,输出日志格式:Out of disorder window:库名.表名,时间戳,列1,列2,…, ERROR级别,方式:LOG_ERROR(…),路径文件:KWDB_DATA_ROOT/log/kwdb*.log
例:LOG.ERROR(“Out of disorder window: m_db.m_table,时间戳,列1,列2,…”)

系统提供查询工具可以过滤这部分数据,命令如下:
kwdbAdmin show_disorder_data –table database.table [start_datetime,end_datetime]
该命令是根据指定的库名表名和时间范围筛选符合条件的失败数据,失败数据是由于存在乱序窗口导致插入失败。该命令通过对日志存储目录下所有日志进行筛选,过滤出符合条件的日志并生成 csv 文件导入到数据库中。指令需要指定库名、表名、起始时间与结束时间,如果不指定起始时间与结束时间则输出当前系统时间-乱序窗口以后时间的所有数据。数据筛选实现逻辑如下:
目录路径:KWDB_DATA_ROOT/log/
• 遍历所有日志文件:kwdb*.log;
• 获取筛选日志文件内容,匹配关键字"Out of disorder window:",获取到行;
• 匹配对应的库名(或库名.表名)后,进一步匹配关键字","并拆分字符串 str1,str2;
• str2 匹配","并再拆分字符串,获取时间戳;
• 判断时间戳是否在筛选范围[t_start,t_end],未设置时,t_start=当前系统时间-乱序时间窗口,t_end=UINT64_MAX;
• 符合筛选条件则输出数据,格式:时间戳,列1,列2(csv格式),…;
• 以 csv 格式输出到文件中,同时也可导入到数据库中。





