1

例如考虑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;
    }
}

如果我想确保我知道已添加的发票行的顺序,我会在标准 SQL 数据库中只使用自动增量 ID。Starcounter的最佳实践是什么?

4

2 回答 2

3

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

  1. 这种功能需要集中计数器和对它的请求序列化。这是一个性能问题。
  2. 序列化顺序取决于内部实现,不一定满足应用程序开发人员的期望。

当应用程序的顺序含义和预期不明确时的示例:

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

创建时间可能意味着:

  1. 插入并提交记录时。
  2. 当记录在内存中分配时。
  3. 当用户提交请求时,这会导致创建记录。
  4. 当调用处理程序时,它会创建记录。

由于可以在不同的事务中同时添加记录,因此选项之间的顺序可能会有很大差异。只有应用程序开发人员才知道顺序是什么意思以及它将如何影响用户体验 (UX)。

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

  1. 在某些情况下,可以预先分配一组 id,因此增量顺序会被破坏。例如,复制器场景。
  2. 顺序不必与开发人员期望的相同,这在上面讨论过。
  3. 在某些情况下,分配器实现或全栈框架的轻微改进可能会产生与以前不同的顺序。这可能会破坏用户体验。
  4. 出于性能原因,对象标识的分配方案很可能在未来发生变化。因此,对象身份号码的诉讼程序将不会及时与诉讼程序相对应。

因为最后一点使用 Starcounter 的对象标识按创建时间排序记录不是一个好主意。

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

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

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

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

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

于 2016-04-18T10:07:25.850 回答