我有以下 2 个表格,其中包含示例值:
producer_tbl:
id (auto-inc, PK) producer_id item_id item_added
2 5 3 20
products_available_tbl:
item_id (PK) avail_cnt blocked_cnt
3 9 2
这是我访问它们的方法:
当制造商向我提供商品时,我会在 Producer_tbl 中插入适当的数据。我同时增加 products_available_tbl 中相应项目的avail_cnt。
当消费者想要给定的商品时,我首先使用 (avail_cnt -blocked_cnt) 来检查所需的数量是否可用。如果是这样,我会增加blocked_cnt 的数量,但不更新avail_cnt。当消费者提交他的请求时,我会减少blocked_cnt和avail_cnt,两者的数量相同。
现在,当有多个生产者和消费者同时接触同一个项目时,我需要上述操作的原子性。
我想知道是否可以通过触发器解决这个问题? (我不想使用外部互斥体)任何人都可以向我指出如何执行此操作的示例示例吗?
最佳答案
根据您的request ,以下是重点关注代码中的性能问题的注释:
优化 add_item()
假设products_available_tbl
在item_id
上有唯一索引,那么
CREATE PROCEDURE add_item(IN in_producer_id INT, IN in_item_id INT,
IN in_item_cnt INT)
BEGIN
DECLARE item INT DEFAULT NULL;
START TRANSACTION;
INSERT INTO producer_tbl (producer_id, item_id, item_cnt)
VALUES (in_producer_id, in_item_id, in_item_cnt);
SELECT item_id FROM products_available_tbl
WHERE item_id=in_item_id INTO item FOR UPDATE;
IF item IS NOT NULL THEN
UPDATE products_available_tbl
SET avail_cnt=avail_cnt + in_item_cnt
WHERE item_id=in_item_id;
ELSE
INSERT INTO products_available_tbl
(item_id, avail_cnt, blocked_cnt)
VALUES (in_item_id, in_item_cnt, 0);
END IF;
COMMIT;
END //
可以重写为:
CREATE PROCEDURE add_item(IN in_producer_id INT, IN in_item_id INT,
IN in_item_cnt INT)
BEGIN
START TRANSACTION;
INSERT INTO producer_tbl (producer_id, item_id, item_cnt)
VALUES (in_producer_id, in_item_id, in_item_cnt);
INSERT INTO products_available_tbl SET
item_id = in_item_id,
avail_cnt = in_item_cnt,
blocked_cnt = 0
ON DUPLICATE KEY UPDATE
avail_cnt = avail_cnt + in_item_cnt;
COMMIT;
END //
优化 block_item()
优化很重要,所以让我们分阶段进行:
首先,我们重写一下
SET out_cnt = var_avail_cnt - var_blocked_cnt;
IF out_cnt >= cnt THEN
SET out_cnt = cnt;
END IF;
作为
SET out_cnt = LEAST(var_avail_cnt - var_blocked_cnt, cnt);
接下来我们重写
SELECT avail_cnt, blocked_cnt FROM products_available_tbl
WHERE item_id=in_item_id INTO var_avail_cnt, var_blocked_cnt
FOR UPDATE;
SET out_cnt = LEAST(var_avail_cnt - var_blocked_cnt, cnt);
作为
SELECT LEAST(avail_cnt - blocked_cnt, cnt) FROM products_available_tbl
WHERE item_id=in_item_id INTO out_cnt
FOR UPDATE;
最后我们重写一下
SELECT LEAST(avail_cnt - blocked_cnt, cnt) FROM products_available_tbl
WHERE item_id=in_item_id INTO out_cnt
FOR UPDATE;
UPDATE products_available_tbl
SET blocked_cnt = var_blocked_cnt + out_cnt
WHERE item_id = in_item_id;
作为
UPDATE products_available_tbl
SET
blocked_cnt = blocked_cnt + (@out_cnt := LEAST(avail_cnt - blocked_cnt, cnt))
WHERE item_id = in_item_id;
所以
CREATE PROCEDURE block_item(IN in_item_id INT, INOUT cnt INT)
BEGIN
DECLARE out_cnt INT DEFAULT cnt;
DECLARE var_avail_cnt, var_blocked_cnt INT DEFAULT 0;
START TRANSACTION;
SELECT avail_cnt, blocked_cnt FROM products_available_tbl
WHERE item_id=in_item_id INTO var_avail_cnt, var_blocked_cnt
FOR UPDATE;
SET out_cnt = var_avail_cnt - var_blocked_cnt;
IF out_cnt >= cnt THEN
SET out_cnt = cnt;
END IF;
UPDATE products_available_tbl
SET blocked_cnt = var_blocked_cnt + out_cnt
WHERE item_id = in_item_id;
SET cnt = out_cnt;
COMMIT;
END //
变成了
CREATE PROCEDURE block_item(IN in_item_id INT, INOUT cnt INT)
BEGIN
UPDATE products_available_tbl
SET
blocked_cnt = blocked_cnt + (@out_cnt := LEAST(avail_cnt - blocked_cnt, cnt))
WHERE item_id = in_item_id;
SET cnt = @out_cnt;
END //
优化commit_item():
让我们重写
CREATE PROCEDURE commit_item(IN in_item_id INT, INOUT cnt INT)
BEGIN
DECLARE out_cnt INT DEFAULT cnt;
DECLARE var_avail_cnt, var_blocked_cnt INT DEFAULT 0;
START TRANSACTION;
SELECT avail_cnt, blocked_cnt FROM products_available_tbl
WHERE item_id=in_item_id INTO var_avail_cnt, var_blocked_cnt
FOR UPDATE;
IF cnt > var_blocked_cnt THEN
SET out_cnt = -1; /* Error case: Caller supplied wrong value. */
ELSEIF var_blocked_cnt > var_avail_cnt THEN
SET out_cnt = -2; /* Error case: Bug in block_item proc. */
ELSE
SET out_cnt = cnt;
UPDATE products_available_tbl
SET blocked_cnt = var_blocked_cnt - out_cnt,
avail_cnt = var_avail_cnt - out_cnt
WHERE item_id = in_item_id;
END IF;
SET cnt = out_cnt;
COMMIT;
END //
作为
CREATE PROCEDURE commit_item(IN in_item_id INT, INOUT cnt INT)
proc: BEGIN
DECLARE var_avail_cnt, var_blocked_cnt INT DEFAULT 0;
UPDATE products_available_tbl
SET blocked_cnt = blocked_cnt - cnt,
avail_cnt = avail_cnt - cnt
WHERE item_id = in_item_id
AND cnt <= blocked_cnt
AND blocked_cnt <= avail_cnt;
IF ROW_COUNT() > 0 THEN
LEAVE proc;
END IF;
SELECT avail_cnt, blocked_cnt FROM products_available_tbl
WHERE item_id=in_item_id INTO var_avail_cnt, var_blocked_cnt;
IF cnt > var_blocked_cnt THEN
SET cnt = -1; /* Error case: Caller supplied wrong value. */
ELSEIF var_blocked_cnt > var_avail_cnt THEN
SET cnt = -2; /* Error case: Bug in block_item proc. */
ELSE
SET cnt = -3; /* UPDATE failed, reasons unknown. */
END IF;
END //
我希望这些有帮助。让我知道你的想法!
关于MySQL:生产者/消费者模型,访问多个表的命令需要原子性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11593380/