Starcounter中的自动增量

时间:2016-04-17 21:21:08

标签: starcounter

例如,考虑http://starcounter.io/tutorials/1-introduction-to-part-1/ InvoiceDemo

上的示例应用程序

声明以下数据库对象

using Starcounter;

[Database]
public class Invoice {
    public int InvoiceNo;
    public string Name;
    public decimal Total {
        get {
            return Db.SQL<decimal>(@"SELECT sum(r.Total)
                                     FROM InvoiceRow r
                                     WHERE r.Invoice = ?",
                                     this).First;
        }
    }
    public QueryResultRows<InvoiceRow> Items {
        get {
            return Db.SQL<InvoiceRow>(@"SELECT r
                                       FROM InvoiceRow r
                                       WHERE r.Invoice = ?",
                                       this);
        }
    }

    public Invoice() {
        new InvoiceRow() {
            Invoice = this
        };
    }
}

[Database]
public class InvoiceRow {
    public Invoice Invoice;
    public string Description;
    public int Quantity;
    public decimal Price;
    public decimal Total {
        get {
            return Quantity * Price;
        }
    }

    public InvoiceRow() {
        Quantity = 1;
    }
}

如果我想确定我知道已添加的Invoice Rows的顺序,我会在标准SQL DB中使用自动增量ID。 Starcounter的最佳做法是什么?

2 个答案:

答案 0 :(得分:3)

Starcounter不提供自动递增的用户ID,我怀疑它会因不同的原因而出现。例如:

  1. 此类功能需要对其进行集中计数和序列化请求。这是一个性能问题。
  2. 序列化顺序取决于内部实现,不一定符合应用程序开发人员的期望。
  3. 一个例子,说明什么顺序对申请意味着什么和期望是不明显的:

      

    我需要确保我可以按创建顺序对行进行排序

    创作时间可能意味着:

    1. 插入并提交记录时。
    2. 在内存中分配记录时。
    3. 当用户提交请求时,会导致创建记录。
    4. 调用处理程序时,会创建记录。
    5. 由于可以在不同的交易中同时添加记录,因此它们的顺序在选项之间可能会有很大差异。只有应用程序开发人员知道它的含义以及它将如何影响用户体验(UX)。

      Starcounter分配基于计数器的object identity,因此它类似于自动增量ID,但适用于数据库中的所有对象/记录。但是,重要的是要记住:

      1. 有些情况可以预先分配ID组,因此增量顺序会被破坏。例如,复制器场景。
      2. 订单没有必要与开发人员期望的相同,如上所述。
      3. 在某些情况下,分配器实现或全栈框架中的轻微改进可以产生与以前不同的顺序。这可能会破坏用户体验。
      4. 由于性能原因,将来很可能会改变对象标识的分配方案。因此,对象身份号码的诉讼程序将不及时对应诉讼程序。
      5. 由于最后一点使用Starcounter的对象标识来按创建时间排序记录并不是一个好主意。

        我猜其他数据库中自动递增的ID也有类似的问题。因此,如果应该保留用户体验或者将来可能会遇到重大变化(通常无法预见),使用它们并不是那么安全。

        我的意见是订单是特定于应用程序的,应该在应用程序逻辑中实现。我看到了几种方式:

        1. 使用timestamp字段来标识时间顺序和对象标识,以确定方式排序具有相同时间戳的记录,因为可以保证对同一身份的对象标识始终相同。
        2. [编辑] 在数据库外部拥有共享计数器对象。您需要通过锁或其他方式实现序列化。例如,可以使用__sync_bool_compare_and_swapInterlocked.Increment,这是原子操作并且不需要锁定,但它对缓存不友好。请注意,在可重新调整的事务范围之外访问它很重要,即使用快照隔离。但是,在此类交易中使用它仍然有效,但每次重试时都会增加计数器。
        3. 使用数据库对象存储计数器。主要问题是由于乐观的并发性,在高负载下会出现太多冲突。
        4. [编辑] 根据@warpech的建议:获取最高的现有ID,例如SELECT MAX(InvoiceRowNo) FROM InvoiceRow,并将其递增。通过creating unique index添加唯一约束,以避免在并发情况下使用相同的ID。在并发增量的情况下,将发生事务之间的冲突,并且将重试其中一个事务,而另一个事务将成功。请注意,在高负载时,冲突可能经常发生,并且一些不幸的请求将被大大延迟或永远不会通过。

答案 1 :(得分:1)

如果它仅用于排序,而不是真实的(例如带有规则的收据#)seqno,我建议你在对象中添加DateTime进行过滤/排序。如果那不够好,创建一个单例类(可能)在启动时从数据库中选择最高知名的SeqNo,然后使用这个单例来获取/递增计数器(或者如果你是懒惰的,每次选择它) 。如果你想100%确定它们是唯一的,即使你删除了项目,也要让seqno配置保持不变,这样即使你删除了最新的条目,也总是保存最后使用的SeqNo。

不确定如何在SC中管理ObjectID,但我认为将它们用于除了唯一标识符之外的任何东西都可能是一件坏事。 可能你会得到OID(n-1)&lt; OID(n),但你不应该依赖它......