在 MySql 中锁定表的完美策略是什么?

发布于 2025-01-09 09:35:44 字数 1032 浏览 1 评论 0原文

我已经编写了这个创建新的 A/c 凭证的程序。它运行在 MySql 5.0 上。现在,是时候在我们的生产中实施这一点了。但我不确定它是否满足要求。这个锁定策略完美吗?请帮忙。

它这样调用:

CALL SpAcVoucherCreate(1,'2022/03/31','2831',5000,'A001');

我的程序如下:

USE `FinanceDB`;

DROP PROCEDURE IF EXISTS  `SpAcVoucherCreate`;

DELIMITER $$

CREATE DEFINER=`root`@`localhost` PROCEDURE `SpAcVoucherCreate`(
    V_VOCHNO    INT,
    V_VOCHDT    CHAR(10),
    V_ACCODE    CHAR(4),
    V_AMOUNT    DECIMAL(12,2),
    V_USER_ID   CHAR(5)
    )
BEGIN

DECLARE V_ERR_OCCURED   BOOLEAN;

SET V_ERR_OCCURED=FALSE;

BEGIN

DECLARE EXIT HANDLER FOR SQLEXCEPTION
SET V_ERR_OCCURED=TRUE;

SET AUTOCOMMIT=0;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;

LOCK TABLES `ACTRANS` WRITE;

INSERT INTO ACTRANS
        (VOCHNO,VOCHDT,ACCODE,AMOUNT,USER_ID)
        VALUES
        (V_VOCHNO,V_VOCHDT,V_ACCODE,V_AMOUNT,V_USER_ID);

IF V_ERR_OCCURED=TRUE THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

UNLOCK TABLES;

SET AUTOCOMMIT=1;

END;

    END $$
DELIMITER ;

I have wrote this procedure which creates new A/c voucher. It runs on MySql 5.0. Now, it's time to implement this in our production. But I am not sure that it meets the requirement. Is this Lock Strategy perfect ? Please help.

It calls this way :

CALL SpAcVoucherCreate(1,'2022/03/31','2831',5000,'A001');

My procedure is as follows :

USE `FinanceDB`;

DROP PROCEDURE IF EXISTS  `SpAcVoucherCreate`;

DELIMITER $

CREATE DEFINER=`root`@`localhost` PROCEDURE `SpAcVoucherCreate`(
    V_VOCHNO    INT,
    V_VOCHDT    CHAR(10),
    V_ACCODE    CHAR(4),
    V_AMOUNT    DECIMAL(12,2),
    V_USER_ID   CHAR(5)
    )
BEGIN

DECLARE V_ERR_OCCURED   BOOLEAN;

SET V_ERR_OCCURED=FALSE;

BEGIN

DECLARE EXIT HANDLER FOR SQLEXCEPTION
SET V_ERR_OCCURED=TRUE;

SET AUTOCOMMIT=0;

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;

LOCK TABLES `ACTRANS` WRITE;

INSERT INTO ACTRANS
        (VOCHNO,VOCHDT,ACCODE,AMOUNT,USER_ID)
        VALUES
        (V_VOCHNO,V_VOCHDT,V_ACCODE,V_AMOUNT,V_USER_ID);

IF V_ERR_OCCURED=TRUE THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

UNLOCK TABLES;

SET AUTOCOMMIT=1;

END;

    END $
DELIMITER ;

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

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

发布评论

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

评论(1

無心 2025-01-16 09:35:45

我发现该过程做了一些不必要的事情。

  • 对于您正在做的事情,SERIALIZABLE 的作用与 REPEATABLE READ 相同。 MySQL 中 SERIALIZABLE 的唯一作用是它使非锁定 SELECT 语句变成锁定 SELECT 语句,就像您使用了 SELECT...LOCK IN SHARE MODE 一样。由于您只执行 INSERT,因此这种差异没有影响。

  • 无需禁用自动提交,然后为单个语句执行事务启动和提交/回滚。如果您启用了自动提交,则成功的 INSERT 将提交,而不成功的 INSERT 将不会提交。它的结果与您的代码相同。

  • 不需要 SQLEXCEPTION 处理程序来回滚事务,因为事务中只有一条语句。它要么成功,要么不成功。

  • 无论上述情况如何,您都没有显示出使用 LOCK TABLES 的理由。如果多个客户端尝试同时添加行,它只会阻止不必要的 INSERT/UPDATE/DELETE。如果有特殊原因需要这样做,您没有描述。

  • 您的过程主体有一个不必要的 BEGIN/END 块。它没有任何作用。另外,我相信 DECLARE 只允许在第一个 BEGIN 之后,而不是在主体的其他块中。

  • 可能根本没有理由使用存储过程,因为它只完成单个 INSERT 操作。为什么不直接在客户端中执行 INSERT 呢?我想如果您想限制对过程进行插入的权限,如果用户没有该权限,这将是一个原因。

我也同意上面的评论,即您使用的 MySQL 版本可疑已经过时。根据https://endoflife.software/applications/databases,MySQL 5.0 已于 2012 年结束生命周期/mysql。您缺少许多错误修复、安全补丁,当然还有现代功能。


更新:

SolarFlare 的评论是正确的,不允许在 MySQL 的存储过程中使用 LOCKUNLOCK 语句:

mysql> create procedure p()
    -> begin
    -> lock tables mytable write;
    -> insert into mytable () values ();
    -> unlock tables;
    -> end//
ERROR 1314 (0A000): LOCK is not allowed in stored procedures

I find the procedure to be doing several things that are unnecessary.

  • SERIALIZABLE acts the same as REPEATABLE READ for what you're doing. The only effect of SERIALIZABLE in MySQL is that it makes non-locking SELECT statements into locking SELECT statements as if you had used SELECT...LOCK IN SHARE MODE. Since you only do an INSERT, this difference has no effect.

  • There's no need to disable autocommit and then do a transaction start and commit/rollback for a single statement. If you had left autocommit enabled, then a successful INSERT would commit, and an unsuccessful INSERT would not commit. It would have the same result as your code.

  • There would be no need for an SQLEXCEPTION handler to rollback the transaction, because you have only a single statement in the transaction. It either succeeds or it does not succeed.

  • Regardless of any of the above, you have shown no reason to use LOCK TABLES. It will only block INSERT/UPDATE/DELETE unnecessarily, if multiple clients try to add rows concurrently. If there is a special reason you need to do this, you have not described it.

  • Your procedure body has an unnecessary BEGIN/END block. It doesn't do anything. Also I believe that DECLARE is only allowed following the first BEGIN, not within other blocks in the body.

  • There is probably no reason to use a stored procedure at all, since it only accomplishes a single INSERT operation. Why not just do the INSERT directly in the client? I suppose if you want to restrict the privilege to do inserts to the procedure, if the user doesn't have that privilege, that would be a reason.

I also agree with the comment above that you are using a version of MySQL that is suspiciously out of date. MySQL 5.0 passed its end of life date in 2012, according to https://endoflife.software/applications/databases/mysql. You are missing many bug fixes, security patches, and of course modern features.


Update:

The comment from SolarFlare is correct, one is not allowed to use LOCK or UNLOCK statements in stored procedures in MySQL:

mysql> create procedure p()
    -> begin
    -> lock tables mytable write;
    -> insert into mytable () values ();
    -> unlock tables;
    -> end//
ERROR 1314 (0A000): LOCK is not allowed in stored procedures
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文