【问题标题】:Dynamic sequential identity column and conditional constraints?动态顺序标识列和条件约束?
【发布时间】:2018-09-28 02:09:35
【问题描述】:

希望你们能帮助我集思广益,为我在工作中遇到的障碍提出一些想法。我会尽量在这里提供尽可能多的信息。

1) ProductSerialEnum - 这是一个包含各种产品类型的序列号枚举的表。它所做的是定义产品可以拥有的序列号范围。新类型将不断出现,因此这是当前示例

ProductTypeId   SerialPrefix SerialStartRange     SerialEndRange
--------------- ------------ -------------------- --------------------
1               2            2000000000           2999999999
2               1            1000000000           1999999999
3               4            4000000000           4999999999
4               3            3000000000           3999999999
5               501          5010000000           5019999999
6               500          5000000000           5009999999
7               601          6010000000           6019999999
8               600          6000000000           6009999999
...             ...          ...                  ...

2) 想要的结果

产品 - 这是产品属性表,我将简化为重要的部分。

这是我希望这张表的样子,但我遇到了符合我所有成功标准的问题。

ProductId   ProductTypeId   SerialNumber
----------- --------------- ------------
1           2               100000000
2           2               100000001
3           2               100000002
4           2               100000003
5           2               100000004
6           2               100000005
7           2               100000006
8           2               100000007
9           4               300000000
10          4               300000001
11          4               300000002

等等……

我的问题是填充 Products 表以像我上面发布的示例一样工作的最佳方法是什么。

当必须插入产品时,我会提供一个 ProductTypeId,并且在插入时,我应该生成一个序列号,该序列号由 SerialEnum 表中的范围定义。

这些序列号必须按 Enum 类型的顺序插入,正如您在我期望的示例中所见。它们的顺序是绝对关键的,我们不能刻录任何序列号,因为这些范围是我们分配的范围。

我最初的想法是生成一个插入 SP,它使用我的关键 ProductType 接收产品信息,然后保存该产品序列范围的最大序列号,然后简单地插入下一个值。

但是数据库接收到大量并发流量,所以我最担心的是序列号冲突和由于并发或锁定问题而跳过大范围的块。

我考虑过其他选项,例如可能对表进行分区以及处理 IDENTITY 列和范围,但是随着产品类型的范围如此频繁地增加,我无法跟上每次都持续运行分区方案的速度产品类型发生变化。

综上所述,每个插入到表中的产品都必须根据 ProductType 表中定义的序列号范围生成唯一且连续的序列号,而不会浪费序列号或由于并发问题而导致错误。

如果有人有一些经验或想法,如果你能把它们从我身上反弹出来,我将非常感激!

【问题讨论】:

  • 当 ProductTypeID 2 的范围为 10000000 时,为什么您的预期数字以 2 开头
  • 在你想要的结果中你显示ProductTypeId 2 和SerialNumber2 开始?不是要从1 开始吗?
  • 几个问题:时间范围是什么——我们是在尝试执行原子操作,还是可以编写然后更新序列?失败怎么办 - 如果我们有多个更新发生,并且一个失败,那么该序列会发生什么。产品删除怎么办,或者这永远不会发生。你说你不想用完范围,这是否意味着你永远不会有差距?
  • @LoztInSpace 你是对的,我的例子不正确,我会更新它

标签: sql sql-server database database-design


【解决方案1】:

我们不能烧掉任何序列号

然而数据库接收到大量的并发流量

是互斥的。如果你不能把它们扔掉,那么你需要序列化你的事务以使其 100% 提交并分配一个数字,然后再进行下一个。如果您使用快速、序列或身份,那么您可能会有间隙,因为 ID 将从序列缓存中分配。失败或回滚会造成差距。

如果您被允许,您可以在活动结束后批量分配序列号。这将需要为主键提供一个快速的序列或标识列,并插入一个空白序列号。您会定期扫描数据库中的空白 SN,并根据代理 ID 的顺序(但不是值)使用 ROW_NUMBER() 分配它们。

这将保证没有间隙,但有时并非所有产品都有序列号。

【讨论】:

  • 我想过这个,但是如果你使用 row_number() 那么如果一个产品曾经被复制,那么任何以后的产品都会改变它们的序列号。
  • 不确定我是否理解,但我建议您使用这种方式分配它,并使用产品类型和该范围内最大未分配编号的组合将其作为新序列号写入表中类型。如果这就是您的意思,我并不是说 SN 是产品 ID 的某个动态函数。
  • 我在谈论这个位Periodically you scan the database for blank SNs and allocate them using ROW_NUMBER() based on the order (but not value) of the surrogate ID。但这归结为用例-是否偶尔存在漏洞真的很重要。如果目标不是超出范围,那么可能不会。
  • 这是我考虑过的,实际上是一个潜在的选择。不过,我明天需要与我们的 CTO 联系,看看在分配连续剧之前可以接受的延迟是多少。
  • 但是我知道软件工程师会处理任何延迟问题,因为生成的序列号将立即(ish)传递给 API 调用以进行进一步处理...
【解决方案2】:

我假设每个范围都足以防止在您的应用的预期生命周期内发生翻转。另外,您可以通过在创建过程中尽可能推迟它们的生成来减少“烧毁”的序列号的数量,但我想不出一个有效的方法来完全消除它。您很可能会有一些差距。

以代码、网络服务或任何对您的应用最方便的方式进行。向ProductSerialEnum 添加一个名为NextAvailableValue 的列,它以SerialStartRange 中的值开头。启动时读取此表中的代码。

您的 API 定义了一个条目,在该条目中分配了下一个可用序列号,其副本递增并且过程返回。新的NextAvalableValue 理所当然地需要被持久化,但这可以随时产生。该 I/O 不会减慢进程的响应时间。

当然,您可以通过查询数据库来做到这一点,但是内存中的进程可以在执行一次查询的时间内返回数​​千个序列号。

有许多有效的方法可以定期更新数据库中的值并保持不断变化的内存中的值。这些取决于您的平台。

这并不是严格意义上的面向数据库的解决方案,但它肯定比这样的解决方案更快,并且可能更健壮。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-17
    • 2020-07-02
    • 2012-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多