繁体   English   中英

在mysql中同步执行存储过程

[英]Synchronized stored procedure execution in mysql

我在mysql中有一个存储过程,可以执行需要同步的任务,因此,如果两个应用程序调用该存储过程,则只有一个程序可以访问一段代码来执行该任务,而使另一个程序被阻塞,直到第一个一个完成任务。

DELIMITER $$
CREATE PROCEDURE SP_GEN_ID(IN NAME VARCHAR(20))

BEGIN 
  DECLARE maxLen int default 0;
START TRANSACTION;
   #the section of code that needs to be synchronized
COMMIT
END;
$$

DELIMITER ;

因此,如果两个应用程序同时调用存储过程,则必须同步任务。

一种。 但是Start TRANSACTIONCOMMIT没有同步执行。

b。 并且LOCK TABLES tableA也不能用于存储过程中以确保同步。

C。 我试图在应用程序级别同步存储过程调用。 我用了

boost_interprocess scoped_lock lock();

在Boost 1.41中效果很好

但是boost 1.34库中不支持进程间锁定互斥锁,在我的情况下可用。

有没有一种方法可以同步代码的存储过程部分,以便当同时进行两个调用时,一个被阻塞而另一个被执行?

(添加了以下内容)编辑后的代码:给出了我要在存储过程的同步块中尝试执行的操作的想法。

它获取最后分配的ID,并将其递增1,并检查它是否未用于其他“名称”记录。 找到有效的ID后,更新最后分配的ID记录表,然后将其与给定的“名称”相关联。

DELIMITER $$
CREATE PROCEDURE SP_GEN_ID(IN NAME VARCHAR(20))

BEGIN 
  DECLARE maxLen int default 0;
START TRANSACTION;
   #the section of code that needs to be synchronized
    SELECT lastid into lastgenerated FROM DB_last_id WHERE key = 'NAME_ID';    
findid_loop:
    LOOP
    set lastid = lastid + 1;
    #this is to check whether it was assigned with someother name before.
    IF not EXISTS (SELECT 1 FROM user_name_id WHERE name_id = lastgenerated) then
                     set nameid = lastgenerated;
                     set found = true;
                     LEAVE findid_loop;
            END IF;

            #for loop limit check
            IF (counter < loopLimit) then
                    set counter = counter + 1;
                    ITERATE findid_loop;
            ELSE
                    #reached the limit and not found.
                    LEAVE findid_loop;
            END IF;
    END LOOP findid_loop;

     #if a valid id, update last id and assign to name.
     IF (found) THEN
            #update the id.
            update DB_last_id  set lastid = nameid where key = 'NAME_ID';
            insert into user_name_id values (nameid ,name);
     ELSE
            #return an empty string for the application to take action on the failure.
            set nameid = '';
    END IF;
#end transaction here.
COMMIT

END;
$$

DELIMITER ;

正如我在上面的评论中提到的,您应该发现一笔交易足以满足大多数需求; 但是,如果您需要显式等待另一个调用完成,请使用GET_LOCK(str,timeout)

尝试使用timeout秒数超时来获得具有字符串str给出的名称的锁。 如果成功获得了锁定,则返回1如果尝试超时(例如,因为另一个客户端先前已锁定名称),则返回0如果发生错误(例如,内存不足或线程被mysqladmin kill ),则返回NULL )。 如果您具有通过GET_LOCK()获得的锁,则在执行RELEASE_LOCK() ,执行新的GET_LOCK()时释放该锁,或者连接终止(正常或异常)。 GET_LOCK()获得的锁不与事务交互。 也就是说,提交事务不会释放在事务期间获得的任何此类锁。

此功能可用于实现应用程序锁定或模拟记录锁定。 名称在服务器范围内被锁定。 如果名称已被一个客户端锁定,则GET_LOCK()阻止另一客户端对具有相同名称的锁的任何请求。 这使同意给定锁名称的客户端可以使用该名称执行协作建议锁定。 但是请注意,它还使不在协作客户端集合中的客户端意外或故意锁定名称,从而防止任何协作客户端锁定该名称。 降低这种可能性的一种方法是使用特定于数据库或特定于应用程序的锁名。 例如,使用db_name.strapp_name.str形式的锁名。

  mysql> SELECT GET_LOCK('lock1',10);\n         -> 1\n mysql> SELECT IS_FREE_LOCK('lock2');\n         -> 1\n mysql> SELECT GET_LOCK('lock2',10);\n         -> 1\n mysql> SELECT RELEASE_LOCK('lock2');\n         -> 1\n mysql> SELECT RELEASE_LOCK('lock1');\n         ->空\n

第二个RELEASE_LOCK()调用返回NULL因为第二个GET_LOCK()调用自动释放了锁'lock1'

如果多个客户端正在等待锁,则获取它们的顺序是不确定的,并且取决于诸如所使用的线程库之类的因素。 特别是,应用程序不应假定客户端将按照发出锁定请求的顺序来获得锁定。

注意
在MySQL 5.5.3之前,如果一个客户端试图获取另一个客户端已经持有的锁,它将根据timeout参数阻塞。 如果被阻止的客户端终止,则在锁定请求超时之前,其线程不会消失。

此功能对于基于语句的复制不安全。 从MySQL 5.5.1开始,如果在binlog_format设置为STATEMENT时使用此函数, binlog_format记录一条警告。 (缺陷#47995)

使用START TRANSACTION启动事务实际上并不会启动它。 START TRANSACTION之后的第一个表访问。 打开事务也不是并发控制的一种手段。 如果只需要这些,则可以依靠MySQL通过GET_LOCK()RELEASE_LOCK()和其他一些相关功能提供的咨询锁系统。

这次通过事务实现并发控制的另一种方法是依靠互斥行锁。 由于SELECT语句在InnoDB中是非锁定的,因此发出这样的查询会启动事务,但是它既未设置任何锁,也未遵守任何现有锁。 如果在相同的信息(行)上存在更早的事务,要使SELECT语句实际阻塞,则必须使用FOR UPDATE子句。 例如:

START TRANSACTION;
SELECT lastid into lastgenerated FROM DB_last_id WHERE key = 'NAME_ID' FOR UPDATE;
...

通过这种构造,在明确指示执行锁定读取的SELECT语句之后,永远不会有两个并发事务在相同的'NAME_ID'上进行操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM