sql >> データベース >  >> RDS >> Mysql

MySQLトランザクション:SELECT + INSERT

    必要なのはロックです 。トランザクションは確かに「厳密には必要ありません」。

    「悲観的ロック」と「楽観的ロック」のどちらかを選択できます。この2つの可能性のどちらを使用するかはあなた次第であり、基本的に次の点を考慮して評価する必要があります。

    • 現在の並行性のレベル
    • データベースに対する不可分操作の期間
    • 操作全体の複雑さ

    関係することのアイデアを構築するために、この2つを読むことをお勧めします:

    よりよく説明する例

    これはそれほどエレガントではないかもしれませんが、トランザクションなしで(そしてUNIQUE制約なしでも)すべてを実行できる方法を示す例にすぎません。実行する必要があるのは、次のINSERT + SELECTステートメットを組み合わせて、実行後に使用することです。影響を受ける行の数を確認します。影響を受ける行の数が1の場合、他の方法で成功しました(0の場合)。衝突が発生し、相手が勝ちました。

    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
    FROM `slot`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= @endTime AND `end` >= @startTime
        AND `devices_id` = @deviceId)
    GROUP BY (1);
    

    これは、トランザクションなしで単一のSQL操作を使用して取得されたOptimisticLockingの例です。

    書かれているように、slotにすでに少なくとも1つの行が必要であるという問題があります。 テーブルを機能させるために(そうでない場合、SELECT句は常に空のレコードセットを返します。その場合、衝突がない場合は何も挿入されません。実際に機能させるには、次の2つの方法があります。

    • おそらく過去の日付でテーブルにダミー行を1つ挿入します
    • メインのFROM句が少なくとも1行あるテーブルを参照するように書き直し、1つの小さなテーブル(おそらくdummyという名前)を作成します。 )列とレコードが1つだけで、次のように書き直します(GROUP BY句は不要になっていることに注意してください)

      INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
      SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
      FROM `dummy`
      WHERE NOT EXISTS (
          SELECT `id` FROM `slot`
          WHERE `start` <= @endTime AND `end` >= @startTime
          AND `devices_id` = @deviceId);
      

    ここでは、単にコピー/貼り付けすると、実際のアイデアが表示されるという一連の指示に従います。 intフィールドの日付/時刻を、日付と時刻の数字を連結した数値としてエンコードすると仮定しました。

    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    VALUES (1008141200, 1008141210, 11, 2, 'Dummy Record', 14)
    
    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT 1408141206, 1408141210, 11, 2, 'Hello', 14
    FROM `slot`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= 1408141210 AND `end` >= 1408141206
        AND `devices_id` = 14)
    GROUP BY (1);
    
    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT 1408141208, 1408141214, 11, 2, 'Hello', 14
    FROM `slot`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= 1408141214 AND `end` >= 1408141208
        AND `devices_id` = 14)
    GROUP BY (1);
    
    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT 1408141216, 1408141220, 11, 2, 'Hello', 14
    FROM `slot`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= 1408141220 AND `end` >= 1408141216
        AND `devices_id` = 14)
    GROUP BY (1);
    
    SELECT * FROM `slot`;
    

    これは明らかにOptimisticLockingの極端な例ですが、すべてが1つのSQL命令のみで実行され、データベースサーバーとphpコード間の相互作用(データ交換)が少ないため、最終的には非常に効率的です。さらに、「実際の」ロックは事実上ありません。

    ...または悲観的ロックを使用

    同じコードが、明示的なテーブルのロック/ロック解除の指示に囲まれた、優れたPessimistcLockingの実装になる可能性があります。

    LOCK TABLE slot WRITE, dummy READ;
    
    INSERT INTO `slot` (`start`, `end`, `uid`, `group`, `message`, `devices_id`)
    SELECT @startTime, @endTime, @uid, @group, @message, @deviceId
    FROM `dummy`
    WHERE NOT EXISTS (
        SELECT `id` FROM `slot`
        WHERE `start` <= @endTime AND `end` >= @startTime
        AND `devices_id` = @deviceId);
    
    UNLOCK TABLES;
    

    もちろん、この場合(Pessimistic Locking)は、SELECTとINSERTを分離して、その間にいくつかのphpコードを実行することができます。ただし、このコードは実行が非常に高速であるため(phpとのデータ交換、中間phpコードはありません)、PessimisticLockの期間は可能な限り最短です。悲観的ロックをできるだけ短くすることは、アプリケーションの速度低下を回避するための重要なポイントです。

    とにかく、コードが実質的に同じであり、同じ方法で成功/失敗情報を取得するため、成功したかどうかを知るために、影響を受けるレコードの戻り値の数を確認する必要があります。

    ここで http://dev.mysql.com/doc/ refman / 5.0 / en / insert-select.html 彼らは「MySQLはINSERT...SELECTステートメントの同時挿入を許可しない」と言っています したがって、ペシミスティックロックは必要ありませんが、MySQLの将来のバージョンで変更されると思われる場合は、とにかくこれは良いオプションになる可能性があります。

    私は「楽観的」です これは変わらないこと;-)




    1. Postgresqlのデータベース照合、Ctypeを変更します

    2. RLIKEがMariaDBでどのように機能するか

    3. hibernateは次のシーケンス値を取得できませんでした

    4. SELECTステートメントpostgresの動的列