PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

postgresql - skrip yang menggunakan blok transaksi gagal membuat semua catatan

Ya, Anda melakukan sesuatu yang salah.
Lihat contoh sederhana.

Sesi 1

postgres=# select * from user_reservation_table;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | f         |      0 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)


postgres=# \set user 1
postgres=#
postgres=# begin;
BEGIN
postgres=# UPDATE user_reservation_table
postgres-# SET UsedYesNo = true, userid=:user
postgres-# WHERE uservalue IN(
postgres(#    SELECT uservalue FROM user_reservation_table
postgres(#    WHERE UsedYesNo=false Order By id ASC Limit 1)
postgres-# RETURNING uservalue;
 uservalue
-----------
         1
(1 wiersz)


UPDATE 1
postgres=#


Sesi 2 - pada saat yang sama, tetapi hanya 10 ms kemudian

postgres=# \set user 2
postgres=# begin;
BEGIN
postgres=# UPDATE user_reservation_table
postgres-# SET UsedYesNo = true, userid=:user
postgres-# WHERE uservalue IN(
postgres(#    SELECT uservalue FROM user_reservation_table
postgres(#    WHERE UsedYesNo=false Order By id ASC Limit 1)
postgres-# RETURNING uservalue;

Sesi 2 hang ....... dan menunggu sesuatu ....

kembali ke Sesi 1

postgres=# commit;
COMMIT
postgres=#



dan lagi Sesi 2

 uservalue
-----------
         1
(1 wiersz)


UPDATE 1
postgres=# commit;
COMMIT
postgres=#

Sesi 2 tidak menunggu lagi, dan menyelesaikan transaksinya.

Dan apa hasil akhirnya?:

postgres=# select * from user_reservation_table order by id;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | t         |      2 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)

Dua pengguna mengambil nilai 1 yang sama, tetapi hanya pengguna 2 yang terdaftar di tabel





======================EDIT ==================================

Dalam skenario ini kita dapat menggunakan SELECT .. FOR UPDATE dan memanfaatkan cara postgre mengevaluasi ulang kueri dalam mode Read Committed Isolation Level,
lihat dokumentasi:http://www.postgresql.org/docs/9.2/static/transaction-iso.html

Singkatnya:
jika satu sesi mengunci baris, dan sesi lainnya mencoba mengunci baris yang sama, maka sesi kedua akan "hang" dan akan menunggu sesi pertama melakukan commit atau rollback. Saat sesi pertama melakukan transaksi, maka sesi kedua akan mengevaluasi kembali kondisi pencarian WHERE. Jika kondisi pencarian tidak sesuai (karena transaksi pertama mengubah beberapa kolom), maka sesi kedua akan melewati baris tersebut, dan akan memproses baris berikutnya yang cocok dengan WHERE kondisi.

Catatan:perilaku ini berbeda di Tingkat Isolasi Baca Berulang. Dalam hal ini sesi kedua akan memunculkan kesalahan:tidak dapat membuat serial akses karena pembaruan bersamaan, dan Anda harus mencoba lagi seluruh transaksi.

Permintaan kami mungkin terlihat seperti:

select id from user_reservation_table
where usedyesno = false
order by id
limit 1
for update ;

lalu:

  Update .... where id = (id returned by SELECT ... FOR UPDATE)



Secara pribadi, saya lebih suka menguji skenario penguncian menggunakan klien konsol lama biasa (psql untuk postgree, mysql atau SQLPlus untuk oracle)

Jadi, mari uji kueri kita di psql:

session1 #select * from user_reservation_table order by id;
 id | usedyesno | userid | uservalue
----+-----------+--------+-----------
  1 | t         |      2 |         1
  2 | f         |      0 |         2
  3 | f         |      0 |         3
  4 | f         |      0 |         4
  5 | f         |      0 |         5
(5 wierszy)


session1 #begin;
BEGIN
session1 #select id from user_reservation_table
postgres-# where usedyesno = false
postgres-# order by id
postgres-# limit 1
postgres-# for update ;
 id
----
  2
(1 wiersz)


session1 #update user_reservation_table set usedyesno = true
postgres-# where id = 2;
UPDATE 1
session1 #

Sesi 1 mengunci dan memperbarui baris id=2

Dan sekarang sesi2

session2 #begin;
BEGIN
session2 #select id from user_reservation_table
postgres-# where usedyesno = false
postgres-# order by id
postgres-# limit 1
postgres-# for update ;

Sesi 2 macet saat mencoba mengunci id baris =2

Oke, mari komit sesi 1

session1 #commit;
COMMIT
session1 #

dan lihat apa yang terjadi di sesi 2:

postgres-# for update ;
 id
----
  3
(1 wiersz)

Bingo - sesi 2 melewatkan baris id =2 dan memilih (dan mengunci) baris id =3


Jadi kueri terakhir kami mungkin:

update user_reservation_table
set usedyesno = true
where id = (
   select id from user_reservation_table
   where usedyesno = false
   order by id
   limit 1
   for update
) RETURNING uservalue;

Beberapa reservasi - contoh ini hanya untuk tujuan pengujian Anda dan tujuannya adalah untuk membantu memahami cara kerja penguncian di postgre.
Bahkan, kueri ini akan membuat serial akses ke tabel, dan tidak dapat diskalakan dan mungkin akan berfungsi buruk (lambat) di lingkungan multi-pengguna.
Bayangkan bahwa 10 sesi mencoba secara bersamaan untuk mendapatkan baris berikutnya dari tabel ini- setiap sesi akan hang dan akan menunggu hingga sesi sebelumnya akan dijalankan.
Jadi, jangan gunakan kueri ini dalam kode produksi.
Apakah Anda benar-benar ingin "menemukan dan menyimpan nilai berikutnya dari tabel"? Mengapa ?
Jika ya, Anda harus memiliki beberapa perangkat serialisasi (seperti kueri ini, atau, mungkin lebih mudah diterapkan, mengunci seluruh tabel), tetapi ini akan menjadi hambatan.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kesalahan postgres dalam sisipan batch:relasi hibernate_sequence tidak ada posisi 17

  2. Cara melintasi struktur struktur pohon hierarkis ke belakang menggunakan kueri rekursif

  3. SequelizeConnectionError:sertifikat yang ditandatangani sendiri

  4. Bagaimana cara melihat kode CREATE VIEW untuk tampilan di PostgreSQL?

  5. Bisakah saya menggunakan fungsi Postgres untuk menemukan titik di dalam persegi panjang berputar dengan ukuran tetap?