【问题标题】:Storing thrift-serialized objects on disk在磁盘上存储 thrift 序列化的对象
【发布时间】:2017-06-01 23:44:38
【问题描述】:

要求

  • 我需要将 Thrift 序列化对象流存储在文件中,以便稍后读取文件并获取反序列化对象。
  • 对象可以是不同的类型。

问题

(i) 分隔文件中每个对象的最佳方法是什么。例如,在文本文件中,我可以用换行符分隔每个对象。这种方法对二进制文件也有效吗?

(ii) 为了标记每个对象的类型(在反序列化期间使用),我计划在每个字节序列的开头添加一个类型字段。有更好的方法吗?

【问题讨论】:

    标签: scala serialization binary thrift


    【解决方案1】:

    最重要的问题是您打算如何访问文件:顺序访问还是随机访问?

    顺序访问(某种)或少量数据

    在文件中分隔每个对象的最佳方法是什么。例如,在文本文件中,我可以用换行符分隔每个对象。这种方法对二进制文件也有效吗?

    显然不是 ;-)。但别担心,有更好的解决方案。要存储数据列表,可以简单地直接存储list<data>。换句话说,是这样的:

    struct foo { 1: string field, 2: i32 otherfield }
    
    list<foo>
    

    如果数据不仅是foo,而且是不同类型的,在两者之间放置一个联合:

    struct foo { 1: string field, 2: i32 otherfield }
    
    struct bar { 1: map<string,wtf> seinfield, 2: double cloverfield }
    
    struct wtf { 1: list<double> even_more_fields }
    
    union MyDataRecord {
      1: foo foo
      2: bar bar
      3: wtf wtf
    }
    
    list<MyDataRecord>
    

    因为我们仍然一次读取和写入所有内容,所以不需要人为的限制器。

    为了标记每个对象的类型(在反序列化期间使用),我计划在每个字节序列的开头添加一个类型字段。有更好的方法吗?

    如果您将数据放入如上所述的list&lt;&gt;,Thrift 会处理它。您只需将整个列表作为一个整体进行读写。

    随机访问和/或大量数据

    如果您希望随机访问您的数据,情况将会发生巨大变化。这样做的问题是 - 为了使其高效和快速 - 您需要以某种方式确定文件中给定元素的位置 而不总是先扫描整个文件1) 。在大多数情况下,写入的条目的字节大小会有所不同。即使它们都只是一种类型foo,仍然不能假设某个元素位于

    position = sizeof(foo) *  index_of_desired_element
    

    因为foo 有一个可变大小的数据成员:string 字段。

    要解决这个问题,我们基本上有两种选择。

    (1) 固定大小的记录:我们可以确保所有元素不超过预定义的最大大小,并将其用作文件记录大小。我们也不再使用list&lt;&gt;,而是将数据写入文件中的正确位置。第n个元素的位置比再次

    position = N * predefined_record_size
    

    缺点显然是我们可能会浪费大量空间,而且数据量有限。

    (2) 索引文件:第二种选择是维护一个单独的索引文件,它保存数据文件中每个条目的位置。这又可以是一个简单的整数列表:

    list<i32>
    

    这里的缺点是,您需要确保索引的形状正确,尤其是在文件中间的插入、删除和更新操作时。

    上述两个选项的更普遍的问题是,尤其是插入和删除操作可能会变得很痛苦,因为您可能必须移动大量数据。为了解决这个问题,您会发现自己在解决方案中添加了更多的噱头,例如删除标记等。

    底线

    如果您只有一堆数据,list&lt;union&gt; 方法可能是您正在寻找的。因为union 可以随时扩展,所以该解决方案也为以后添加的其他元素做好了准备。

    如果您绝对只想按顺序访问数据,请选择list&lt;union&gt; 方法,或逐个读取/写入union 元素。 Thrift 支持Skip() 函数,如果需要,该函数允许跳过不需要的数据。

    但是,如果您想随机访问数据和/或要处理大量数据,那么真正的数据库可能更合适。


    1) 扫描一个可能很大的文件以查找记录分隔符不是the kind of O(?) you want

    【讨论】:

    • 感谢您的解释。我的用例是顺序访问。但是,与其维护索引文件,我认为将记录的大小与记录一起存储会更好。例如,每条记录的前 4 个字节表示构成记录的字节数(假设没有记录大于 4GB)。这样,就不需要存储绝对偏移量,并且还可以使插入/删除更容易。
    • 假设您的数据文件跨越几个 GB,然后问问自己,在索引 15.883.127124.657.013 记录中搜索、插入或删除条目是否真的是 O(1) task 或至少是 O(logN)使用那个算法?请记住,如果您只有 10 个数据项,几乎所有算法都很快。.
    • 如果你还想做,看TFramedProtocol。它正是这样做的,并将要跟随的数据帧的大小作为 i32 写入输出流。
    • 当然,在这种情况下,插入和删除将是一项昂贵的操作。然而,正如我之前提到的,我的用例只是顺序访问,它总是从索引 0 开始,即文件的开头。在这种情况下,每次访问都是 O(1)。
    猜你喜欢
    • 1970-01-01
    • 2012-07-24
    • 2010-09-20
    • 2012-11-14
    • 2017-01-10
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    相关资源
    最近更新 更多