在mysql中同步存储过程执行

Fly*_*man 8 mysql synchronization stored-procedures locking boost-interprocess

我在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 ;
Run Code Online (Sandbox Code Playgroud)

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

一个.但是启动TRANSACTIONCOMMIT没有同步执行.

湾 并且LOCK TABLES tableA不能用于存储过程以确保同步.

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

boost_interprocess scoped_lock lock();

它在1.41升级中运行得非常好

但是boost 1.34库不支持进程间锁定互斥锁,这是我的情况.

有没有办法同步代码的存储过程部分,以便在同时进行两次调用时,一个在另一个执行之前被阻塞?

(添加以下内容)编辑的代码:了解我在存储过程的synchronized块中尝试执行的操作.

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

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 ;
Run Code Online (Sandbox Code Playgroud)

egg*_*yal 9

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

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

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

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

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

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

注意
在MySQL 5.5.3之前,如果客户端尝试获取另一个客户端已经拥有的锁,它将根据timeout参数进行阻塞.如果被阻止的客户端终止,则在锁定请求超时之前,其线程不会死亡.

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


小智 8

使用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;
...
Run Code Online (Sandbox Code Playgroud)

通过这种结构,在明确告知执行锁定读取'NAME_ID'SELECT语句之后,将永远不会有两个并发事务在同一个操作上运行.