Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Transaksi MySQL:SELECT + INSERT

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:

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;-)




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Praktik Terbaik Replikasi MySQL

  2. Berapa banyak baris yang akan dikunci dengan SELECT ... ORDER BY xxx LIMIT 1 FOR UPDATE?

  3. MySQL mengonversi output timediff ke format hari, jam, menit, detik

  4. Mengimpor JSON ke Mysql

  5. Campuran ilegal dari susunan (utf8mb4_unicode_ci,IMPLICIT) dan (utf8mb4_general_ci,IMPLICIT) untuk operasi '='