必要なのはロックです 。トランザクションは確かに「厳密には必要ありません」。
「悲観的ロック」と「楽観的ロック」のどちらかを選択できます。この2つの可能性のどちらを使用するかはあなた次第であり、基本的に次の点を考慮して評価する必要があります。
- 現在の並行性のレベル
- データベースに対する不可分操作の期間
- 操作全体の複雑さ
関係することのアイデアを構築するために、この2つを読むことをお勧めします:
- 楽観的ロックと悲観的ロック
- MySQLでの最適なロック (ここでは、トランザクションが厳密に必要とされない方法を示すいくつかの例)
よりよく説明する例
これはそれほどエレガントではないかもしれませんが、トランザクションなしで(そして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の期間は可能な限り最短です。悲観的ロックをできるだけ短くすることは、アプリケーションの速度低下を回避するための重要なポイントです。
とにかく、コードが実質的に同じであり、同じ方法で成功/失敗情報を取得するため、成功したかどうかを知るために、影響を受けるレコードの戻り値の数を確認する必要があります。
ここで
私は「楽観的」です これは変わらないこと;-)