如何处理独特的“伪序”。在数据库中多个进程?

发布于 2025-02-13 08:07:24 字数 634 浏览 1 评论 0原文

我正在使用Sqlalchemy Orm和Oracle数据库使用Python 3.10。

数据定义无法更改(要求)。
我有一个具有独特约束和“伪序列”的字段,这意味着该领域是唯一的,它由以下形式组成:Year-2000 + sequence-number。
示例是220001、220002、220003等。它必须严格增加而没有空白。

目前,我们将这样的字段设置为这样(假设一年不会改变):

WHILE True
   SELECT max(sequence)
   Add 1 to the current maximum from above
   INSERT new record with this updated value in DB. 
   IF Success or counter > max_tries: break
   ELSE counter = counter + 1

自然存在种族条件,因为我们的网络服务器运行了多个进程(不是线程)(也是要求)。

现在,我们尝试“乐观地”多次插入值,这意味着,如果另一个记录在同一时间同时再次尝试上述“算法”。如果将来我们获得更多的流量,那么这不会比许多重试相比,这并不是那么良好的重点,而DB负载则增加。

有其他方式而不是以中等费用的乐观锁定?

I am using Python 3.10 with the SQLAlchemy ORM and an Oracle Database.

The data definition cannot be changed (requirement).
I have a field with a unique constraint and a "pseudo-sequence", which means that the field is unique and it consists of a number in following form: Year - 2000 + sequence-number.
Examples are 220001, 220002, 220003, etc. It must strictly be increasing without gaps.

For now we set this field like this (assume year does not change):

WHILE True
   SELECT max(sequence)
   Add 1 to the current maximum from above
   INSERT new record with this updated value in DB. 
   IF Success or counter > max_tries: break
   ELSE counter = counter + 1

Naturally there is a race condition because our webserver runs multiple processes (not threads) (also a requirement).

Right now we try to insert the value multiple times "optimistically", which means, if another record has written the number in the mean time we try the above "algorithm" again. This does not scale that good respecitvely if we get much more traffic in the future, than many retries are necessary and the DB load increases.

Is there another way instead of this optimistic locking with moderate expense?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无所谓啦 2025-02-20 08:07:24

[tl; dr]只需使用普通序列,如果要生成伪序列,则可以计算出出于显示目的。


如果您有一个带有列的表:

CREATE TABLE table_name (
  id NUMBER
     GENERATED ALWAYS AS IDENTITY
     CONSTRAINT table_name__id__pk PRIMARY KEY,
  dt DATE
     CONSTRAINT table_name__dt__nn NOT NULL
);

注意:Identity列可从Oracle 12. 获得

,则您有一个增量序列(您可以用作主键,并且用于任何参考约束)和日期列。

您可以使用row_number()分析函数和算术来生成伪序列( display 目的,而不是作为参考约束

SELECT t.*,
       (EXTRACT(YEAR FROM dt) - 2000)*10000
       + ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR FROM dt) ORDER BY id)
         AS pseudo_seq
FROM   table_name t

)伪序值,然后您可以使用:

ALTER TABLE table_name ADD ( pseudo_seq NUMBER(8,0) );

MERGE INTO table_name dst
USING (
  SELECT ROWID rid,
         (EXTRACT(YEAR FROM dt) - 2000)*10000
         + ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR FROM dt) ORDER BY id)
           AS pseudo_seq
  FROM   table_name
) src
ON (dst.ROWID = src.rid)
WHEN MATCHED THEN
  UPDATE
  SET   pseudo_seq = src.pseudo_seq
  WHERE pseudo_seq IS NULL;

但是,鉴于id列将依次增加,然后这是不必要的,并且pseudo_seq列只能动态生成。

db<>>

[TL;DR] Just use a normal sequence and if you want to generate the pseudo-sequence then you can calculate if for display purposes.


If you have a table with the columns:

CREATE TABLE table_name (
  id NUMBER
     GENERATED ALWAYS AS IDENTITY
     CONSTRAINT table_name__id__pk PRIMARY KEY,
  dt DATE
     CONSTRAINT table_name__dt__nn NOT NULL
);

Note: IDENTITY columns are available from Oracle 12.

Then you have an incrementing sequence (that you can use as the primary key and for any referential constraints) and a date column.

You can generate your pseudo-sequence (for display purposes, rather than as a referential constraint) using the ROW_NUMBER() analytic function and arithmetic:

SELECT t.*,
       (EXTRACT(YEAR FROM dt) - 2000)*10000
       + ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR FROM dt) ORDER BY id)
         AS pseudo_seq
FROM   table_name t

If you did want to persist the pseudo-sequence values then you can use:

ALTER TABLE table_name ADD ( pseudo_seq NUMBER(8,0) );

MERGE INTO table_name dst
USING (
  SELECT ROWID rid,
         (EXTRACT(YEAR FROM dt) - 2000)*10000
         + ROW_NUMBER() OVER (PARTITION BY EXTRACT(YEAR FROM dt) ORDER BY id)
           AS pseudo_seq
  FROM   table_name
) src
ON (dst.ROWID = src.rid)
WHEN MATCHED THEN
  UPDATE
  SET   pseudo_seq = src.pseudo_seq
  WHERE pseudo_seq IS NULL;

However, given that the id column will be increasing sequentially then that is not necessary and the pseudo_seq column can just be dynamically generated.

db<>fiddle here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文