我们正在为我们的客户(企业客户)构建一个票务系统,我们正在努力想出一种方法来拥有一个顺序的、唯一的、用户友好的票证 ID 生成系统。
唯一 ID 生成是一个众所周知的问题,我们已经了解了其他公司使用的一些解决方案,例如 Flickr 或 Twitter,但它们并没有达到我们成为“用户-友好的”。它们太长了,我们的客户无法通过电子邮件或电话记住或使用它们进行交流。
我们也不能使用普通的 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 个字符)。
除非有大量写入(>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 的好方法。
这类似于 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());