如何确定SQL查询是否是原子的

时间:2014-08-24 12:13:30

标签: mysql sql atomic mariadb acid

两个相关问题:

  • 我的插入物是原子的吗(参见下面的代码)?
  • 如何确定插入是否是原子的(即我在哪里可以找到给我保证的规范。)

我应该对SQL做出其他改进吗?有问题的数据是一张包含发票的表格,每张发票都绑定到客户端ID和客户特定的订单ID。这意味着发票ID和元组(客户ID,订单ID)必须是唯一的。

我的第一个版本是:

DROP TABLE IF EXISTS invoice;

CREATE TABLE invoice (
    id int(11) AUTO_INCREMENT,
    client_id int(11) NOT NULL,
    order_id int(11) NOT NULL,
    INDEX(id, client_id, order_id)
);

DELIMITER //

CREATE TRIGGER invoice_insert
BEFORE insert
ON invoice
FOR EACH ROW
BEGIN
    SET @max_order_id = (SELECT IFNULL(MAX(order_id), 0) FROM invoice WHERE client_id = NEW.client_id);
    SET NEW.order_id = @max_order_id + 1;
END//

DELIMITER ;


-- example use
INSERT INTO invoice (client_id) VALUES (1), (1), (2), (1), (2), (2), (2);
INSERT INTO invoice (client_id) VALUES (1), (1), (2), (1), (2), (2), (2);
SELECT * from invoice;

第二个版本是:

DROP TABLE IF EXISTS invoice, client;

CREATE TABLE invoice (
    id int(11) AUTO_INCREMENT,
    client_id int(11) NOT NULL,
    order_id int(11) NOT NULL,
    INDEX(id, client_id, order_id)
);

CREATE TABLE client (
    id int(11) AUTO_INCREMENT,
    max_order_id int(11) NOT NULL DEFAULT '0',
    INDEX(id)
);

DELIMITER //

CREATE TRIGGER invoice_insert
BEFORE INSERT
ON invoice
FOR EACH ROW
BEGIN
    SET @max_order_id = (SELECT max_order_id FROM client where id = NEW.client_id);
    SET NEW.order_id = @max_order_id + 1;
    UPDATE client SET max_order_id = NEW.order_id WHERE id = NEW.client_id;
END//

DELIMITER ;


-- example use
INSERT INTO client () VALUES (), (), ();
INSERT INTO invoice (client_id) VALUES (1), (1), (2), (1), (2), (2), (2);
INSERT INTO invoice (client_id) VALUES (1), (1), (2), (1), (2), (2), (2);
SELECT * from invoice;
SELECT * from client;

第二个版本的优点是它为每个客户维护一个单独的订单ID计数器,这使我可以删除条目而不会面临创建重复订单ID的危险。

1 个答案:

答案 0 :(得分:0)

首先,我会质疑您从OrderId获得的价值。由于每个OrderId显然都是唯一分配给一张发票的,为什么不简单地使用发票的ID?

其次,即使触发器在事务中运行,这两种方法都不安全。例如,假设一个客户端具有最大OrderId = 27,当两个发票“同时”进入时。自动增量给它们ID 53和54说,然后启动触发器。

53的事务启动触发器并运行SELECT,因此得到OrderId 27.在它前进到触发器中的下一个语句之前,54的事务启动触发器并因此运行相同的触发器 - 因此也获得27。然后每个事务都完成其触发器,因此最终会出现重复的OrderIds。

该行为的一个缓解是触发器在发出select以读取更新值之前执行更新语句FIRST(因此获取独占锁)。还是那太可怕了。

最好是(a)使用上面提到的发票ID - 或者如果由于任何原因这是不可能的话,那么(b)有一个自动递增的ORDER表。