IoTDB删除通过添加Modification 文件来实现,每一个TsFile都可能有一个Modification文件,该文件记录了对应TsFile文件中数据被删除的情况。
IoTDB的删除操作并不会真正删除TsFile中的实际数据,而是在查询过程中排除已被删除的数据,从而使用户得到的结果中不包含已被删除的部分数据。
0.12 之前版本
Modification 文件中的记录由timeseries path,删除version值,删除最小时间和最大时间组成。
删除version值用来记录执行删除时的版本,Modification文件中每条删除记录只会对version值较小的chunk起作用,不会对拥有更大version值的chunk起作用。
0.12 以后
Modification 文件中的记录由timeseries path,tsFileOffset,删除最小时间和最大时间组成。
tsFileOffset,用于表示delete对于相应的Tsfile文件生效的文件位置,如果TsFile中一个chunk的offset小于等于tsFileOffset,则表示delete对这个chunk生效。
对于以下的任意删除语句
DELETE FROM <PrefixPath> [COMMA <PrefixPath>]*
实际的删除过程分为Delete和Query两部分,执行delete语句之后IoTDB实际上只有Delete流程被执行,删除记录写入Modification文件。其他的删除工作在Query过程中生效。
若对某个正在被合并的TsFile产生删除操作时,会往对应的mods文件追加一条deletion记录,并新建一个.compaction.mods文件用于记录合并中产生的删除记录,当合并完成后会将源文件的.compaction.mods文件里的删除记录追加写入到目标文件的.mods文件里,并删除源文件的相关文件。
对于以上delete语句中每一个PrefixPath,考虑到用户输入的中可能包含通配符"*",0.11版本中delete操作需要考虑一条delete语句删除多个时间序列数据的情况
StorageEngine.java 中的delete接口为:
delete(PartialPath path, long startTime, long endTime)
在delete流程中需要考虑如下三种情景下的数据删除。
由于working memory table存在于内存中,在delete语句执行过程中可以直接删除满足条件的数据。
在TVList.java中通过delete()方法将所有已被删除的数据直接排除在最终结果之外。public int delete(long lowerBound, long upperBound) {
int newSize = 0;
minTime = Long.MAX_VALUE;
for (int i = 0; i < size; i++) {
long time = getTime(i);
if (time < lowerBound || time > upperBound) {
set(i, newSize++);
minTime = time < minTime ? time : minTime;
}
}- 删除flushing memory table中的数据
正在处于flushing状态中的memory table是不可变的,因此不能像working memory table那样直接删除。
对于flushing状态中的memory table,每做一次删除操作,该memory table将这个deletion对象添加到自己的deletionList当中。public void delete(Deletion deletion) {
this.modifications.add(deletion);
}
在上述Delete流程所提到的三种情况中,对于2和3的删除只是将删除记录到变量中或Modification file中,实际的删除操作由查询流程完成。
查询flushing memory table中的数据
AbstractMemtable.java 的query()方法负责对给定的timeseries进行查询。
由于在delete流程中已经储存了所有该timeseries的所有deletion记录,查询前只需要对deletionList的所有的TimeRange元素按照删除起始时间进行排序,以提高查询时检索删除记录的速度。
查询时遍历TVList中每一个数据点,使用已排好序的deletionList对所有数据点进行过滤,将未被删除的数据加入到结果集中。具体的过滤过程由TVlist.java中的isPointDeleted()方法完成。
private boolean isPointDeleted(long timestamp) {
while (deletionList != null && deleteCursor < deletionList.size()) {
if (deletionList.get(deleteCursor).contains(timestamp)) {
return true;
} else if (deletionList.get(deleteCursor).getMax() < timestamp) {
deleteCursor++;
} else {
return false;
}
}
return false;
}
查询TsFile中的数据
查询TsFile中被删除的数据过程也分为两个阶段
调用StorageEngine.java中的query()方法,用来得到所有相关的顺序文件和乱序文件。
在此过程中,所有对于TsFile的删除记录将会被存入每个chunkMetadata中,为阶段2中的查询做准备。
这一过程位于QueryUtils.java的modifyChunkMetaData()方法中。首先取得每个TsFile对应的Modification file中的deletion记录,遍历TsFile中所有的chunkMetadata结构,如果deletion的version值大于chunkMetadata中的version 值,则说明这个deletion对此chunkMetadata是应该生效的,加入此chunkMetadata的deletionList。
在此处也可以判断一个chunkMetadata是否被完全删除,被完全删除的chunkMetadata也会从Tsfile的chunkMetadataList中删除。- 在SeriesReader中查询具体的数据点
所有相关的delete信息已被存入chunkMetadata的deletionList中,因此每个chunk中的page也能够得到与该page相关的deletion信息。
在扫描page时即可去掉所有被deletionList所覆盖的数据点,从而得到最终的查询结果。