Yang Anda butuhkan adalah mengunci . Transaksi memang "tidak sepenuhnya diperlukan".
Anda dapat memilih antara "penguncian pesimis" dan "penguncian optimis". Keputusan tentang salah satu dari dua kemungkinan ini terserah Anda dan harus dievaluasi pada dasarnya dengan mempertimbangkan:
- tingkat konkurensi yang Anda miliki
- durasi operasi atom yang harus ada pada database
- kompleksitas seluruh operasi
Saya akan merekomendasikan untuk membaca dua ini untuk membangun gagasan tentang hal-hal yang terlibat:
- Penguncian optimis vs. pesimis
- Penguncian optimis di MySQL (ini beberapa contoh yang menunjukkan bagaimana transaksi tidak terlalu dibutuhkan)
Contoh untuk menjelaskan lebih baik
Ini mungkin tidak begitu elegan tetapi hanya sebuah contoh yang menunjukkan bagaimana mungkin untuk melakukan semua tanpa transaksi (dan bahkan tanpa batasan UNIK). Yang perlu dilakukan adalah menggunakan gabungan pernyataan INSERT + SELECT berikut dan setelah eksekusinya untuk memeriksa jumlah baris yang terpengaruh. Jika jumlah baris yang terpengaruh adalah 1 maka telah berhasil sebaliknya (jika 0) telah terjadi tabrakan dan pihak lain menang.
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);
Ini adalah contoh Penguncian Optimis yang diperoleh tanpa transaksi dan dengan satu operasi SQL.
Seperti yang tertulis, ada masalah bahwa setidaknya harus ada satu baris di slot
tabel agar berfungsi (jika tidak, klausa SELECT akan selalu mengembalikan recordset kosong dan dalam hal ini tidak ada yang dimasukkan bahkan jika tidak ada tabrakan. Ada dua kemungkinan untuk membuatnya benar-benar berfungsi:
- masukkan satu baris dummy ke dalam tabel mungkin dengan tanggal yang sudah lewat
-
tulis ulang sehingga klausa FROM utama merujuk ke tabel mana pun yang memiliki setidaknya satu baris atau lebih baik buat satu tabel kecil (mungkin bernama
dummy
) dengan hanya satu kolom dan hanya satu record di dalamnya dan tulis ulang sebagai berikut (perhatikan bahwa klausa GROUP BY tidak diperlukan lagi)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);
Berikut serangkaian instruksi yang jika Anda cukup salin/tempel menunjukkan ide dalam tindakan. Saya berasumsi bahwa Anda menyandikan tanggal/waktu pada bidang int sebagai angka dengan digit tanggal dan waktu yang digabungkan.
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`;
Ini jelas merupakan contoh ekstrim dari Penguncian Optimis tetapi sangat efisien pada akhirnya karena semua dilakukan hanya dengan satu instruksi SQL dan dengan interaksi rendah (pertukaran data) antara server database dan kode php. Selanjutnya praktis tidak ada penguncian "nyata".
...atau dengan Penguncian Pesimis
Kode yang sama dapat menjadi implementasi Penguncian Pessimistc yang baik yang hanya mengelilingi dengan instruksi penguncian/pembukaan tabel yang eksplisit:
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;
Tentu saja dalam kasus ini (Pessimistic Locking) SELECT dan INSERT dapat dipisahkan dan beberapa kode php dieksekusi di antaranya. Namun kode ini tetap sangat cepat untuk dieksekusi (tidak ada pertukaran data dengan php, tidak ada kode php perantara) dan durasi Pessimistic Lock adalah sesingkat mungkin. Menjaga Pesimistic Lock sesingkat mungkin adalah poin kunci untuk menghindari memperlambat aplikasi.
Bagaimanapun, Anda perlu memeriksa jumlah nilai pengembalian catatan yang terpengaruh untuk mengetahui apakah itu berhasil karena kodenya praktis sama sehingga Anda mendapatkan informasi keberhasilan/kegagalan dengan cara yang sama.
Di sini http://dev.mysql.com/doc/ refman/5.0/en/insert-select.html mereka mengatakan bahwa "MySQL tidak mengizinkan penyisipan bersamaan untuk pernyataan INSERT ... SELECT" jadi seharusnya tidak diperlukan Pessimistic Lock tapi bagaimanapun ini bisa menjadi pilihan yang baik jika Anda berpikir bahwa ini akan berubah di versi MySQL yang akan datang.
Saya "Optimis" bahwa ini tidak akan berubah;-)