简体   繁体   English

在mysql中同步执行存储过程

[英]Synchronized stored procedure execution in mysql

I have a stored procedure in mysql thats to perform a task that needs to be synchronized such that if two application calls the stored procedure, only one can access a section of code to perform the task, keeping the other one to get blocked until the first one finishes the task. 我在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 ;

So, if two applications call the stored procedure simultaneously, the task has to be synchronized. 因此,如果两个应用程序同时调用存储过程,则必须同步任务。

a. 一种。 But Start TRANSACTION and COMMIT did NOT synchronize the execution. 但是Start TRANSACTIONCOMMIT没有同步执行。

b. b。 And LOCK TABLES tableA can not be used in stored procedure to ensure the synchronization too. 并且LOCK TABLES tableA也不能用于存储过程中以确保同步。

c. C。 I tried to synchronize the stored procedure call in application level. 我试图在应用程序级别同步存储过程调用。 I used 我用了

boost_interprocess scoped_lock lock(); boost_interprocess scoped_lock lock();

It worked perfectly fine in boost 1.41 在Boost 1.41中效果很好

But interprocess locking mutex is not supported in the boost 1.34 library, which is what is available in my case. 但是boost 1.34库中不支持进程间锁定互斥锁,在我的情况下可用。

Is there a way to synchronize the stored procedure section of code such that when two calls are made simultaneously, one gets blocked before the other gets executed? 有没有一种方法可以同步代码的存储过程部分,以便当同时进行两个调用时,一个被阻塞而另一个被执行?

(added the following) edited code: to give an idea what I am trying to perform in the synchronized block of the stored procedure. (添加了以下内容)编辑后的代码:给出了我要在存储过程的同步块中尝试执行的操作的想法。

It gets the last assigned id, and increment it by one and check whether it is not used for someother 'name' record. 它获取最后分配的ID,并将其递增1,并检查它是否未用于其他“名称”记录。 When a valid id is found, update the last assigned id record table and then associate that with the 'name' given. 找到有效的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 ;

As mentioned in my comments above, you should find that a transaction is sufficient for most needs; 正如我在上面的评论中提到的,您应该发现一笔交易足以满足大多数需求; however, if you need to explicitly wait until the other call has completed, use GET_LOCK(str,timeout) : 但是,如果您需要显式等待另一个调用完成,请使用GET_LOCK(str,timeout)

Tries to obtain a lock with a name given by the string str , using a timeout of timeout seconds. 尝试使用timeout秒数超时来获得具有字符串str给出的名称的锁。 Returns 1 if the lock was obtained successfully, 0 if the attempt timed out (for example, because another client has previously locked the name), or NULL if an error occurred (such as running out of memory or the thread was killed with mysqladmin kill ). 如果成功获得了锁定,则返回1如果尝试超时(例如,因为另一个客户端先前已锁定名称),则返回0如果发生错误(例如,内存不足或线程被mysqladmin kill ),则返回NULL )。 If you have a lock obtained with GET_LOCK() , it is released when you execute RELEASE_LOCK() , execute a new GET_LOCK() , or your connection terminates (either normally or abnormally). 如果您具有通过GET_LOCK()获得的锁,则在执行RELEASE_LOCK() ,执行新的GET_LOCK()时释放该锁,或者连接终止(正常或异常)。 Locks obtained with GET_LOCK() do not interact with transactions. GET_LOCK()获得的锁不与事务交互。 That is, committing a transaction does not release any such locks obtained during the transaction. 也就是说,提交事务不会释放在事务期间获得的任何此类锁。

This function can be used to implement application locks or to simulate record locks. 此功能可用于实现应用程序锁定或模拟记录锁定。 Names are locked on a server-wide basis. 名称在服务器范围内被锁定。 If a name has been locked by one client, GET_LOCK() blocks any request by another client for a lock with the same name. 如果名称已被一个客户端锁定,则GET_LOCK()阻止另一客户端对具有相同名称的锁的任何请求。 This enables clients that agree on a given lock name to use the name to perform cooperative advisory locking. 这使同意给定锁名称的客户端可以使用该名称执行协作建议锁定。 But be aware that it also enables a client that is not among the set of cooperating clients to lock a name, either inadvertently or deliberately, and thus prevent any of the cooperating clients from locking that name. 但是请注意,它还使不在协作客户端集合中的客户端意外或故意锁定名称,从而防止任何协作客户端锁定该名称。 One way to reduce the likelihood of this is to use lock names that are database-specific or application-specific. 降低这种可能性的一种方法是使用特定于数据库或特定于应用程序的锁名。 For example, use lock names of the form db_name.str or app_name.str . 例如,使用db_name.strapp_name.str形式的锁名。

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

The second RELEASE_LOCK() call returns NULL because the lock 'lock1' was automatically released by the second GET_LOCK() call. 第二个RELEASE_LOCK()调用返回NULL因为第二个GET_LOCK()调用自动释放了锁'lock1'

If multiple clients are waiting for a lock, the order in which they will acquire it is undefined and depends on factors such as the thread library in use. 如果多个客户端正在等待锁,则获取它们的顺序是不确定的,并且取决于诸如所使用的线程库之类的因素。 In particular, applications should not assume that clients will acquire the lock in the same order that they issued the lock requests. 特别是,应用程序不应假定客户端将按照发出锁定请求的顺序来获得锁定。

Note 注意
Before MySQL 5.5.3, if a client attempts to acquire a lock that is already held by another client, it blocks according to the timeout argument. 在MySQL 5.5.3之前,如果一个客户端试图获取另一个客户端已经持有的锁,它将根据timeout参数阻塞。 If the blocked client terminates, its thread does not die until the lock request times out. 如果被阻止的客户端终止,则在锁定请求超时之前,其线程不会消失。

This function is unsafe for statement-based replication. 此功能对于基于语句的复制不安全。 Beginning with MySQL 5.5.1, a warning is logged if you use this function when binlog_format is set to STATEMENT . 从MySQL 5.5.1开始,如果在binlog_format设置为STATEMENT时使用此函数, binlog_format记录一条警告。 (Bug #47995) (缺陷#47995)

Starting a transaction with START TRANSACTION does not actually start it. 使用START TRANSACTION启动事务实际上并不会启动它。 The first table access following START TRANSACTION does. START TRANSACTION之后的第一个表访问。 Opening a transaction isn't also a mean for concurrency control. 打开事务也不是并发控制的一种手段。 If you need just that, you can rely on the advisory locks system MySQL provides through GET_LOCK() , RELEASE_LOCK() , and a few other related functions. 如果只需要这些,则可以依靠MySQL通过GET_LOCK()RELEASE_LOCK()和其他一些相关功能提供的咨询锁系统。

An alternative way to implement concurrency control, through transactions this time, would be by relying on exclusive row locks. 这次通过事务实现并发控制的另一种方法是依靠互斥行锁。 Since SELECT statements are non-locking in InnoDB, issuing such query starts a transaction, however it neither sets any locks nor respects any pre-existing ones. 由于SELECT语句在InnoDB中是非锁定的,因此发出这样的查询会启动事务,但是它既未设置任何锁,也未遵守任何现有锁。 To have a SELECT statement actually block if there is an earlier transaction operating on the same information (row), you have to use FOR UPDATE clause. 如果在相同的信息(行)上存在更早的事务,要使SELECT语句实际阻塞,则必须使用FOR UPDATE子句。 For example: 例如:

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

With this construction there will never be two concurrent transactions operating on the same 'NAME_ID' past the SELECT statement that was explicitly told to perform a locking read. 通过这种构造,在明确指示执行锁定读取的SELECT语句之后,永远不会有两个并发事务在相同的'NAME_ID'上进行操作。

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

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