8

我有一个存储过程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 ; 

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

a。但开始交易COMMIT没有同步执行。

b。和LOCK TABLES TABLEA不能在存储过程中使用,以确保同步了。

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

boost_interprocess scoped_lock lock();

它在提升1.41

但进程间锁定互斥的工作完全正常的助推1.34库,这是在我的情况下,可用不支持。

有没有一种方法,当两个呼叫同时进行,一个被其他被执行之前阻止同步的代码,例如存储过程部分?

(已添加以下内容) 已编辑的代码:说明我正在尝试在存储过程的同步块中执行什么操作。

它得到的最后分配的ID,并通过一个递增,并检查它是否不用于一些其它的名字“记录。 找到一个有效的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 ; 
+0

你的表使用什么存储引擎?他们必须是InnoDB才能进行交易/锁定工作。 – eggyal

+0

存储引擎是InnoDB的表。但它似乎还没有工作。为了验证这一点,我做了一个睡眠(15),在START TRANSACTION之后立即等待15秒。当我同时调用存储过程时,两者似乎在15秒后出现。如果START TRANSACTION确保同步,则在存储过程的第二次调用应该在30秒左右后结束。对? (第一次来电15秒,然后第二次进入START TRANSACTION,睡15秒) –

+0

你想要同步的代码是什么?如果您只是希望SQL操作是原子操作,那么在事务中执行它们应该可以实现这一点。如果您正在影响其他状态,如系统变量,则需要获取专用于此目的的表/记录的锁定。在第二次调用中没有延迟并不一定表示锁失败(例如,可能存在缓存问题,使第二次调用比第一次调用更快;或者第二次调用可能执行不同的执行第一条路径)。请提供完整的代码。 – eggyal

回答

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; 
... 

利用这种结构永远不会有过去的SELECT声明被明确告知要执行锁定读同一'NAME_ID'运行两个并发事务。

+0

工作得很好!谢谢。 –

9

正如我上面的评论中提到,你应该找到一个事务足以满足大多数需求;但是,如果你需要明确等到其他调用已完成,使用GET_LOCK(str,timeout)

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

此函数可用于实现应用程序锁定或模拟记录锁定。名称在服务器范围内锁定。如果名称已被一个客户端锁定,则GET_LOCK()将阻止其他客户端对具有相同名称的锁的任何请求。这使得同意给定锁名称的客户端可以使用该名称执行合作咨询锁定。但请注意,它还可以使不属于协作客户端的客户端无意或故意锁定名称,从而防止任何合作客户端锁定该名称。降低这种可能性的一种方法是使用特定于数据库或特定于应用程序的锁定名称。例如,使用的形式db_name.strapp_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时使用此函数,则会记录一条警告。 (Bug#47995)

+0

我从mysql文档中阅读了关于GET_LOCK的内容。然后我试着通过执行mysql> SELECT GET_LOCK('lock2',10)来测试它。 - > 1在同一个会话中两次,并假定第二次调用GET_LOCK具有相同的名称(lock2)dint等待第一个完成。我刚刚通过调用两个不同会话的存储过程在我的存储过程中测试了这一点。它工作得很好。第二次调用GET_LOCK等待第一个释放之前释放。谢谢,eggyal! –

+0

虽然你的解决方案和Mushu的解决我的情况,但我只能为这个问题选择一个答案。我选择了他的那个,因为那个更简单,更适合我的情况。 –

+0

抱歉,我仍然无法'upvote',因为我没有足够的积分来这样做。当我得到足够的分数时,我会回到这里。谢谢! –