我在 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 ;
因此,如果两个应用程序同时调用存储过程,则必须同步该任务。
一个。但是启动TRANSACTION 和 COMMIT 没有同步执行。
湾并且LOCK TABLES tableA 不能在存储过程中使用以确保同步。
℃。我试图在应用程序级别同步存储过程调用。我用了
boost_interprocess scoped_lock lock();
它在boost 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 ;
答案 0 :(得分: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
形式的锁定名称。< / p>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
,因为第GET_LOCK()
次来电自动释放了锁'lock1'
。如果多个客户端正在等待锁定,则它们将获取它的顺序是未定义的,并且取决于诸如正在使用的线程库之类的因素。特别是,应用程序不应假设客户端将按照发出锁定请求的顺序获取锁定。
注意强>
在MySQL 5.5.3之前,如果客户端尝试获取另一个客户端已经拥有的锁,它将根据timeout
参数进行阻止。如果被阻止的客户端终止,则在锁定请求超时之前,其线程不会死亡。此函数对于基于语句的复制不安全。从MySQL 5.5.1开始,如果在
binlog_format
设置为STATEMENT
时使用此功能,则会记录警告。 (Bug#47995)
答案 1 :(得分: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;
...
使用这种结构,永远不会有两个并发事务在明确告知执行锁定读取的'NAME_ID'
语句之后的同一SELECT
上运行。