受限于实体内的唯一增量 ID 生成

问题描述 投票:0回答:2

我们正在为我们的客户(企业客户)构建一个票务系统,我们正在努力想出一种方法来拥有一个顺序的、唯一的、用户友好的票证 ID 生成系统。

唯一 ID 生成是一个众所周知的问题,我们已经了解了其他公司使用的一些解决方案,例如 FlickrTwitter,但它们并没有达到我们成为“用户-友好的”。它们太长了,我们的客户无法通过电子邮件或电话记住或使用它们进行交流。

我们也不能使用普通的 GUID,因为我们需要顺序排序。

我们最初考虑将 ID 生成为客户 ID 和工单 ID 本身的组合。 例如,客户 1 的第一张票将是 C1-T1,然后是 C1-T2。 客户 2 的第一张票是 C2-T1,然后是 C2-T2。

这当然是可行的。我们可以简单的看一下最后一个

customer_id-ticket_id
组合,直接加1即可,但是涉及到DB查询,我们还需要加锁,这样并发事务就不会重复使用相同的递增的ticket ID。这实质上意味着将其移至异步流程,因为我们不能妨碍任何同步流程。保持同步意味着交易量大的客户最终可能会等待很长时间才能完成 API 调用,因为一堆并发交易(来自同一客户之前的工单)仍在等待。

但是业务需求是需要立即生成ticket ID供用户消费

所以,虽然我们有一些解决方案可以使用,但每个解决方案都有一些缺点。它们要么阻碍了面向客户的 API 的延迟,要么无法立即使用,要么时间太长以至于用户不友好。

我们现在陷入困境,没有好的线索可以合作。

因此,我们想从社区了解是否有任何方法可以生成一个顺序的、唯一的 ID,该 ID 也足够短(可能最多 8-9 个字符)。

mysql guid uniqueidentifier mysql-5.7
2个回答
0
投票

除非有大量写入(>1000/s),否则专用“IDs”表上的临时锁不太可能是昂贵的。

这方面的一个例子,同时保持高吞吐量,将是:

DELIMITER ;
-- Schema
CREATE TABLE Customers (
    CustomerID INT NOT NULL AUTO_INCREMENT,
    Name VARCHAR(50) NOT NULL,
    PRIMARY KEY (CustomerID)
);

CREATE TABLE CustomerSequences (
    CustomerID INT NOT NULL,
    Sequence varchar(50) NOT NULL,
    NextValue INT NOT NULL,
    PRIMARY KEY (CustomerID, Sequence),
    FOREIGN KEY (CustomerID) REFERENCES Customers (CustomerID)
);

-- Sample Data
INSERT INTO Customers (Name) VALUES ('Customer 1'), ('Customer 2');
INSERT INTO CustomerSequences (CustomerID, Sequence, NextValue)
SELECT CustomerID, 'Ticket', 1
FROM Customers;
INSERT INTO CustomerSequences (CustomerID, Sequence, NextValue)
SELECT CustomerID, 'Asset', 1
FROM Customers;

DELIMITER $$
CREATE PROCEDURE GetCustomerSequenceNo (IN cid int, IN seq varchar(50), OUT val int)
BEGIN
    START TRANSACTION;

    SELECT NextValue into val 
    FROM CustomerSequences 
    WHERE CustomerID = cid and Sequence = seq 
    FOR UPDATE;
    
    UPDATE CustomerSequences 
    SET NextValue = val + 1
    WHERE CustomerID = cid and Sequence = seq;
    
    COMMIT;
END$$
DELIMITER ;

-- Example use
select * from CustomerSequences;

call GetCustomerSequenceNo(1, 'Ticket', @Ticket11ID);
select @Ticket11ID;
call GetCustomerSequenceNo(1, 'Ticket', @Ticket12ID);
select @Ticket12ID;
call GetCustomerSequenceNo(2, 'Ticket', @Ticket21ID);
select @Ticket21ID;

select * from CustomerSequences;

您获得一个 ID 作为一个独立的交易,与您可能出于商业目的而开始的长期交易不同。这与

AUTO_INCREMENT
的行为相匹配,可能会导致间隙 - 但在大多数情况下这不是问题。

如果您有少量固定数量的项目来维护单独的序列,则可以改用

SEQUENCE
模式对象。不过,为成百上千的客户这样做是激怒一些 DBA 的好方法。


0
投票

这类似于 Mitch 的回答,但没有 SP 的详细信息,而是使用 MySQL 文档中的 LAST_INSERT_ID() 示例.

门票的简化表-

CREATE TABLE tickets (
    customer_id INT UNSIGNED NOT NULL,
    ticket_id INT UNSIGNED NOT NULL,
    PRIMARY KEY (customer_id, ticket_id)
);

每个客户的工单顺序表 -

CREATE TABLE customer_ticket_sequence (
    customer_id INT UNSIGNED NOT NULL PRIMARY KEY,
    seq INT UNSIGNED NOT NULL
);

-- initialise sequences for customers 1, 2 & 3
INSERT INTO customer_ticket_sequence VALUES (1, 0), (2, 0), (3, 0);

现在,我们不再执行 SELECT 后跟 UPDATE,而是使用单个更新语句,将新序列值存储在 LAST_INSERT_ID() 中,为插入票证做好准备 -

UPDATE customer_ticket_sequence SET seq = LAST_INSERT_ID(seq + 1) WHERE customer_id = 1;
INSERT INTO tickets VALUES (1, LAST_INSERT_ID());

db<>小提琴

© www.soinside.com 2019 - 2024. All rights reserved.