查询不断增长的数据集

时间:2014-04-14 06:35:47

标签: c++

我们有一个数据集,在应用程序处理数据集时会增长。经过长时间的讨论,我们决定此时不需要阻塞或异步API,我们将定期查询数据存储。

我们考虑了两种设计用于查询存储的API的选项:

  1. 查询方法返回数据的快照,以及指示我们可能拥有更多数据的天气的标志。当我们完成对最后返回的快照的迭代时,我们再次查询以获取其余数据的另一个快照。
  2. 查询方法返回" live"对数据进行迭代,当此迭代器前进时,它返回以下选项之一:数据可用,没有更多数据,可能有更多数据。
  3. 我们正在使用C ++,并且我们借用了.NET样式枚举器API,原因超出了这个问题的范围。这里有一些代码来演示这两个选项。您更喜欢哪个选项?

    /* ======== FIRST OPTION ============== */
    
    // similar to the familier .NET enumerator.
    class IFooEnumerator
    {
        // true --> A data element may be accessed using the Current() method
        // false --> End of sequence. Calling Current() is an invalid operation.
        virtual bool MoveNext() = 0;
        virtual Foo Current() const = 0;
        virtual ~IFooEnumerator() {}
    };
    
    enum class Availability
    {
        EndOfData,
        MightHaveMoreData,
    };
    
    class IDataProvider
    {
        // Query params allow specifying the ID of the starting element. Here is the intended usage pattern:
        //  1. Call GetFoo() without specifying a starting point. 
        //  2. Process all elements returned by IFooEnumerator until it ends.
        //  3. Check the availability. 
        //     3.1 MightHaveMoreDataLater --> Invoke GetFoo() again after some time by specifying the last processed element as the starting point
        //                                    and repeat steps (2) and (3)   
        //     3.2 EndOfData --> The data set will not grow any more and we know that we have finished processing.
        virtual std::tuple<std::unique_ptr<IFooEnumerator>, Availability> GetFoo(query-params) = 0;
    };
    
    
    /* ====== SECOND OPTION ====== */
    
    enum class Availability
    {
        HasData,
        MightHaveMoreData,
        EndOfData,
    };
    
    
    class IGrowingFooEnumerator
    {
        // HasData:
        //      We might access the current data element by invoking Current()
        // EndOfData:
        //      The data set has finished growing and no more data elements will arrive later
        // MightHaveMoreData:
        //      The data set will grow and we need to continue calling MoveNext() periodically (preferably after a short delay)
        //      until we get a "HasData" or "EndOfData" result.
        virtual Availability MoveNext() = 0;
        virtual Foo Current() const = 0;
        virtual ~IFooEnumerator() {}
    };
    
    class IDataProvider
    {
        std::unique_ptr<IGrowingFooEnumerator> GetFoo(query-params) = 0;
    };
    

    更新

    鉴于目前的答案,我有一些澄清。争论主要在于界面 - 它在表达查询中的表现力和直觉性,这些数据集在某个时间点将停止增长。由于以下属性,两个接口的实现都可以在没有竞争条件的情况下(至少我们相信):

    1. 如果迭代器+标志对代表查询时系统的快照,则可以正确实现第一个选项。获取快照语义不是问题,因为我们使用数据库事务。
    2. 如果正确实施第一个选项,可以实施第二个选项。 &#34; MoveNext()&#34;第二个选项将在内部使用类似第一个选项的内容,并在需要时重新发出查询。
    3. 数据集可以更改为&#34;可能有更多数据&#34;到&#34;数据结束&#34;,但反之不然。因此,如果我们错误地返回&#34;可能会有更多数据&#34;由于竞争条件,我们只需要获得较小的性能开销,因为我们需要再次查询,下次我们将收到&#34;数据结束&#34;。

3 个答案:

答案 0 :(得分:0)

  

&#34;一段时间后再次调用GetFoo(),指定最后处理的元素为起点&#34;

你打算怎么做?如果它使用之前返回的IFooEnumerator,那么在功能上这两个选项是等效的。否则,让调用者破坏&#34;枚举器&#34;然而,不久之后,请致电GetFoo()继续迭代意味着您失去了监控客户对查询结果持续兴趣的能力。可能就是现在你没有必要这样做,但我认为在整个结果处理过程中排除跟踪状态的能力是不好的设计。

答案 1 :(得分:0)

这真的取决于很多事情,整个系统是否会全部工作(没有详细介绍你的实际实施):

  1. 无论你怎么扭曲它,在检查&#34之间会有竞争条件;是否有更多数据&#34;并且有更多数据被添加到系统中。这意味着尝试捕获最后几个数据项可能毫无意义?
  2. 您可能需要限制重复运行的次数为&#34;是否有更多数据&#34;,或者您可能最终进入无限循环的&#34;处理最后一批时出现新数据&#34 34 ;.
  3. 知道数据是否已更新是多么容易 - 如果所有更新都是&#34;新项目&#34;如果新的ID按顺序更高,您可以简单地查询&#34;是否有数据高于X&#34;,其中X是您的最后一个ID。但是,例如,如果您计算数据中有多少项属性Y设置为值A,那么数据可能会在数据库中的任何位置更新(例如,出租车当前所在的数据库,通过GPS每隔几秒钟就有数千辆汽车,自从上次读数据库以来,可能很难确定哪些车有更新)。
  4. 关于您的实施,在选项2中,我不确定您对MightHaveMoreData州的意思 - 它有没有,或者它没有,对吗?在这种情况下,重复轮询获取更多数据是一个糟糕的设计 - 假设您永远无法100%确定没有新数据&#34;提取从获取最后一个数据到处理和采取行动所花费的时间(显示,用于购买股票市场上的股票,停止火车或您处理新数据后想要做的任何事情) 。

答案 2 :(得分:0)

读写锁可能有所帮助。许多读者可以同时访问数据集,只有一个编写者。 这个想法很简单: - 当您需要只读访问权限时,阅读器使用&#34; read-block&#34 ;,可以与其他读取共享,与写入者独占; - 当您需要写入权限时,writer使用写锁定,这对读者和作者都是独有的;