9

2 つのアプリケーションがストアド プロシージャを呼び出した場合、タスクを実行するためにコードのセクションにアクセスできるのは 1 つだけであり、他のアプリケーションは最初のアプリケーションまでブロックされるように、同期する必要があるタスクを実行するストアド プロシージャがmysqlにあります。 1つはタスクを完了します。

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 ;

そのため、2 つのアプリケーションがストアド プロシージャを同時に呼び出す場合は、タスクを同期する必要があります。

を。しかし、 START TRANSACTIONCOMMITは実行を同期しませんでした。

b. また、LOCK TABLES tableAをストアド プロシージャで使用して同期を確保することもできません。

c. アプリケーション レベルでストアド プロシージャ コールを同期しようとしました。使った

boost_interprocess scoped_lock lock();

ブースト1.41で完全に機能しました

しかし、プロセス間ロックミューテックスは、boost 1.34 ライブラリではサポートされていません。これは、私の場合に利用できるものです。

コードのストアド プロシージャ セクションを同期して、2 つの呼び出しが同時に行われたときに、一方が実行される前に一方がブロックされるようにする方法はありますか?

(以下を追加)編集されたコード:ストアドプロシージャの同期ブロックで何を実行しようとしているのかを理解するため。

最後に割り当てられた 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 ;
4

2 に答える 2

9

上記の私のコメントで述べたように、ほとんどのニーズにはトランザクションで十分であることがわかるはずです。ただし、他の呼び出しが完了するまで明示的に待機する必要がある場合は、次を使用しますGET_LOCK(str,timeout)

strのタイムアウトを使用して、 string で指定された名前のロックを取得しようとします。ロックが正常に取得された場合、試行がタイムアウトした場合 (たとえば、別のクライアントが以前に名前をロックしたため)、またはエラーが発生した場合 (メモリ不足やスレッドが で強制終了されたなど) をtimeout返します。で取得したロックがある場合は、 を実行するか、新しい を実行するか、接続が (正常または異常に) 終了したときに解放されます。で取得したロックは、トランザクションと対話しません。つまり、トランザクションをコミットしても、トランザクション中に取得されたそのようなロックは解放されません。10NULLmysqladmin killGET_LOCK()RELEASE_LOCK()GET_LOCK()GET_LOCK()

この関数を使用して、アプリケーション ロックを実装したり、レコード ロックをシミュレートしたりできます。名前はサーバー全体でロックされます。名前が 1 つのクライアントによってロックされている場合、GET_LOCK()別のクライアントによる同じ名前のロック要求をブロックします。これにより、特定のロック名に同意するクライアントは、その名前を使用して協調的なアドバイザリ ロックを実行できます。ただし、これにより、協力しているクライアントのセットに含まれていないクライアントが、不注意または故意に名前をロックできるようになり、協力しているクライアントがその名前をロックできなくなることに注意してください。この可能性を減らす 1 つの方法は、データベース固有またはアプリケーション固有のロック名を使用することです。たとえば、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');
        -> ヌル

2 番目の呼び出しによってロックが自動的に解放されたため、2 番目のRELEASE_LOCK()呼び出しが返されます。NULL'lock1'GET_LOCK()

複数のクライアントがロックを待機している場合、それらがロックを取得する順序は未定義であり、使用中のスレッド ライブラリなどの要因によって異なります。特に、アプリケーションは、クライアントがロック要求を発行したのと同じ順序でロックを取得すると想定しないでください。

注意
MySQL 5.5.3 より前では、クライアントが別のクライアントによってすでに保持されているロックを取得しようとすると、timeout引数に従ってブロックされます。ブロックされたクライアントが終了しても、そのスレッドはロック要求がタイムアウトするまで終了しません。

この関数は、ステートメント ベースのレプリケーションでは安全ではありません。MySQL 5.5.1 以降、binlog_formatが に設定されているときにこの関数を使用すると、警告がログに記録されSTATEMENTます。(バグ #47995)

于 2012-05-12T10:18:11.293 に答える
8

START TRANSACTION でトランザクションを開始しても、実際には開始されません。START TRANSACTION に続く最初のテーブル アクセスで実行されます。トランザクションを開くことも、同時実行制御の手段ではありません。GET_LOCK()それだけが必要な場合は、MySQL が、RELEASE_LOCK()、およびその他の関連する関数を通じて提供するアドバイザリ ロック システムに頼ることができます。

今回はトランザクションを通じて同時実行制御を実装する別の方法は、排他的な行ロックに依存することです。InnoDB ではステートメントは非ロックであるためSELECT、このようなクエリを発行するとトランザクションが開始されますが、ロックは設定されず、既存のロックも考慮されません。SELECT同じ情報 (行) で動作する以前のトランザクションがある場合にステートメントを実際にブロックするには、句を使用する必要がありますFOR UPDATE。例えば:

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

この構造では、ロック読み取りを実行するように明示的に指示されたステートメントを'NAME_ID'過ぎて、同時に 2 つのトランザクションが同時に実行されることはありません。SELECT

于 2012-05-12T12:00:09.470 に答える