Haruskah saya melakukan atau membatalkan transaksi baca?

95

Saya memiliki kueri baca yang saya jalankan dalam suatu transaksi sehingga saya dapat menentukan tingkat isolasi. Setelah kueri selesai, apa yang harus saya lakukan?

  • Lakukan transaksi
  • Rollback transaksi
  • Tidak melakukan apa-apa (yang akan menyebabkan transaksi dibatalkan pada akhir blok penggunaan)

Apa implikasi dari melakukan masing-masing?

using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
    using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            command.Transaction = transaction;
            command.CommandText = "SELECT * FROM SomeTable";
            using (IDataReader reader = command.ExecuteReader())
            {
                // Read the results
            }
        }

        // To commit, or not to commit?
    }
}

EDIT: Pertanyaannya bukan apakah transaksi harus digunakan atau jika ada cara lain untuk mengatur level transaksi. Pertanyaannya adalah jika ada bedanya bahwa transaksi yang tidak mengubah apa pun telah dilakukan atau dibatalkan. Apakah ada perbedaan kinerja? Apakah itu mempengaruhi koneksi lain? Ada perbedaan lain?

Stefan Moser
sumber
1
Anda mungkin sudah tahu tentang ini, tetapi dengan contoh yang Anda berikan, Anda mungkin memiliki hasil yang setara dengan menjalankan kueri sederhana: SELECT * FROM SomeTable with NOLOCK
JasonTrue
@ Stefan, tampaknya sebagian besar dari kita bertanya-tanya mengapa Anda repot-repot bertransaksi dengan operasi hanya baca. Dapatkah Anda memberi tahu kami jika Anda tahu tentang NOLOCK dan jika Anda tahu, mengapa Anda tidak mengikuti rute itu.
StingyJack
Saya tahu tentang NOLOCK, tetapi sistem ini beroperasi pada database yang berbeda serta SQL Server, jadi saya mencoba menghindari petunjuk penguncian khusus SQL Server. Ini adalah pertanyaan yang lebih ingin tahu daripada apa pun karena aplikasi berfungsi dengan baik dengan kode di atas.
Stefan Moser
Ah, dalam hal ini saya menghapus tag sqlserver, karena itu menunjukkan MSSqlServer sebagai produk target.
StingyJack
@StingyJack - Anda benar, saya seharusnya tidak menggunakan tag sqlserver.
Stefan Moser

Jawaban:

51

Anda berkomitmen. Titik. Tidak ada alternatif lain yang masuk akal. Jika Anda memulai transaksi, Anda harus menutupnya. Committing melepaskan semua kunci yang mungkin Anda miliki, dan sama-sama masuk akal dengan tingkat isolasi ReadUncommitted atau Serializable. Mengandalkan rollback implisit - meski mungkin secara teknis setara - hanyalah bentuk yang buruk.

Jika itu belum meyakinkan Anda, bayangkan saja orang berikutnya yang menyisipkan pernyataan pembaruan di tengah kode Anda, dan harus melacak rollback implisit yang terjadi dan menghapus datanya.

Tandai Brackett
sumber
44
Ada alternatif yang masuk akal - rollback. Rollback eksplisit, yaitu. Jika Anda tidak bermaksud mengubah apa pun, kembalikan memastikan apa pun tidak dilakukan. Tentu saja, seharusnya tidak ada perubahan apa pun; rollback menjamin itu.
Jonathan Leffler
2
DBMS yang berbeda dapat memiliki semantik 'penyelesaian transaksi implisit' yang berbeda. IBM Informix (dan saya yakin DB2) melakukan rollback implisit; berdasarkan rumor, Oracle melakukan komit implisit. Saya lebih suka rollback implisit.
Jonathan Leffler
8
Misalkan saya membuat tabel temp, mengisinya dengan id, menggabungkannya dengan tabel data untuk memilih data yang sesuai dengan id, lalu menghapus tabel temp. Saya benar-benar hanya membaca data, dan saya tidak peduli apa yang terjadi pada tabel temp, karena bersifat sementara ... tetapi dari perspektif kinerja, apakah akan lebih mahal untuk mengembalikan transaksi atau melakukannya? Apa efek dari komit / kembalikan ketika tidak ada selain tabel temp dan operasi baca yang terlibat?
Triynko
4
@Triynko - Secara intuitif, menurut saya ROLLBACK lebih mahal. COMMIT adalah kasus penggunaan normal, dan ROLLBACK kasus luar biasa. Tapi, kecuali secara akademis, siapa yang peduli? Saya yakin ada 1000 poin pengoptimalan yang lebih baik untuk aplikasi Anda. Jika Anda benar-benar penasaran, Anda dapat menemukan kode penanganan transaksi mySQL di bazaar.launchpad.net/~mysql/mysql-server/mysql-6.0/annotate/…
Mark Brackett
3
@Triynko - Satu- satunya cara untuk mengoptimalkan adalah membuat profil. Ini adalah perubahan kode yang sederhana, tidak ada alasan untuk tidak membuat profil kedua metode jika Anda benar-benar ingin mengoptimalkannya. Pastikan untuk memperbarui kami dengan hasil!
Mark Brackett
28

Jika Anda belum mengubah apa pun, Anda dapat menggunakan COMMIT atau ROLLBACK. Salah satu dari mereka akan melepaskan kunci baca yang telah Anda peroleh dan karena Anda belum membuat perubahan apa pun, kunci tersebut akan setara.

Graeme Perrow
sumber
2
Terima kasih telah memberi tahu saya bahwa mereka setara. Menurut pendapat saya, ini paling baik menjawab pertanyaan yang sebenarnya.
chowey
itu akan memberikan transaksi tidak aktif jika kita menggunakan komit tanpa pembaruan aktual. saya baru saja menghadapinya di situs langsung saya
Muhammad Omer Aslam
6

Jika Anda memulai transaksi, praktik terbaiknya adalah selalu melakukannya. Jika pengecualian dilemparkan ke dalam blok penggunaan (transaksi) Anda, transaksi akan secara otomatis dibatalkan.

Neil Barnwell
sumber
3

IMHO masuk akal untuk membungkus kueri hanya baca dalam transaksi karena (terutama di Java) Anda dapat memberi tahu transaksi menjadi "hanya-baca" yang pada gilirannya driver JDBC dapat mempertimbangkan untuk mengoptimalkan kueri (tetapi tidak harus, jadi tidak ada akan mencegah Anda dari mengeluarkan a INSERT). Misalnya driver Oracle akan sepenuhnya menghindari penguncian tabel pada kueri dalam transaksi yang ditandai hanya-baca, yang memperoleh banyak kinerja pada aplikasi yang sangat berorientasi baca.

Oliver Drotbohm
sumber
3

Pertimbangkan transaksi bersarang .

Kebanyakan RDBMS tidak mendukung transaksi bersarang, atau mencoba meniru mereka dengan cara yang sangat terbatas.

Misalnya, di MS SQL Server, rollback dalam transaksi dalam (yang bukan transaksi nyata, MS SQL Server hanya menghitung level transaksi!) Akan mengembalikan semua yang telah terjadi di transaksi terluar (yang merupakan transaksi nyata).

Beberapa pembungkus database mungkin mempertimbangkan rollback dalam transaksi dalam sebagai tanda bahwa kesalahan telah terjadi dan mengembalikan semua yang ada di transaksi terluar, terlepas apakah transaksi terluar dilakukan atau dibatalkan.

Jadi COMMIT adalah cara yang aman, ketika Anda tidak dapat mengesampingkan bahwa komponen Anda digunakan oleh beberapa modul perangkat lunak.

Harap dicatat bahwa ini adalah jawaban umum untuk pertanyaan tersebut. Contoh kode secara cerdik mengatasi masalah dengan transaksi luar dengan membuka koneksi database baru.

Mengenai kinerja: tergantung pada tingkat isolasi, PILIHAN mungkin memerlukan berbagai tingkat KUNCI dan data sementara (snapshot). Ini dibersihkan saat transaksi ditutup. Tidak masalah apakah ini dilakukan melalui COMMIT atau ROLLBACK. Mungkin ada perbedaan yang tidak signifikan dalam waktu CPU yang dihabiskan - COMMIT mungkin lebih cepat untuk diurai daripada ROLLBACK (dua karakter lebih sedikit) dan perbedaan kecil lainnya. Jelas, ini hanya berlaku untuk operasi read-only!

Sama sekali tidak ditanyakan: programmer lain yang mungkin bisa membaca kode mungkin berasumsi bahwa ROLLBACK menyiratkan kondisi kesalahan.

Cakar
sumber
2

Sekadar catatan, tetapi Anda juga dapat menulis kode itu seperti ini:

using (IDbConnection connection = ConnectionFactory.CreateConnection())
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
using (IDbCommand command = connection.CreateCommand())
{
    command.Transaction = transaction;
    command.CommandText = "SELECT * FROM SomeTable";
    using (IDataReader reader = command.ExecuteReader())
    {
        // Do something useful
    }
    // To commit, or not to commit?
}

Dan jika Anda menyusun ulang sedikit, Anda mungkin dapat memindahkan blok penggunaan untuk IDataReader ke atas juga.

Joel Coehoorn
sumber
1

Jika Anda memasukkan SQL ke dalam prosedur tersimpan dan menambahkan ini di atas kueri:

set transaction isolation level read uncommitted

maka Anda tidak perlu melewati rintangan apa pun di kode C #. Menyetel tingkat isolasi transaksi dalam prosedur tersimpan tidak menyebabkan setelan diterapkan ke semua penggunaan koneksi tersebut di masa mendatang (yang merupakan sesuatu yang harus Anda khawatirkan dengan setelan lain karena koneksi tersebut digabungkan). Pada akhir prosedur tersimpan, itu hanya kembali ke koneksi apa pun yang diinisialisasi.

Eric Z Beard
sumber
1

ROLLBACK sebagian besar digunakan jika terjadi kesalahan atau keadaan luar biasa, dan COMMIT jika penyelesaian berhasil.

Kita harus menutup transaksi dengan COMMIT (untuk sukses) dan ROLLBACK (untuk kegagalan), bahkan dalam kasus transaksi hanya-baca yang tampaknya tidak menjadi masalah. Sebenarnya itu penting, untuk konsistensi dan pembuktian masa depan.

Transaksi hanya-baca secara logis dapat "gagal" dalam banyak hal, misalnya:

  • kueri tidak mengembalikan satu baris persis seperti yang diharapkan
  • prosedur tersimpan memunculkan pengecualian
  • data yang diambil ternyata tidak konsisten
  • pengguna membatalkan transaksi karena terlalu lama
  • kebuntuan atau batas waktu

Jika COMMIT dan ROLLBACK digunakan dengan benar untuk transaksi hanya-baca, itu akan terus berfungsi seperti yang diharapkan jika kode tulis DB ditambahkan di beberapa titik, misalnya untuk caching, audit atau statistik.

ROLLBACK implisit hanya boleh digunakan untuk situasi "kesalahan fatal", ketika aplikasi crash atau keluar dengan kesalahan yang tidak dapat dipulihkan, kegagalan jaringan, kegagalan daya, dll.

Sam Watkins
sumber
0

Mengingat bahwa BACA tidak mengubah status, saya tidak akan melakukan apa pun. Melakukan komit tidak akan melakukan apa-apa, kecuali menyia-nyiakan siklus untuk mengirim permintaan ke database. Anda belum melakukan operasi yang statusnya berubah. Begitu juga untuk rollback.

Namun Anda harus, pastikan untuk membersihkan objek Anda dan menutup koneksi Anda ke database. Tidak menutup koneksi Anda dapat menyebabkan masalah jika kode ini dipanggil berulang kali.

Brett McCann
sumber
3
Bergantung pada tingkat isolasi, pilih DAPAT memperoleh kunci yang akan memblokir transaksi lain.
Graeme Perrow
Koneksi akan ditutup pada akhir blok penggunaan- untuk itulah sambungan itu ada. Tapi poin yang bagus bahwa lalu lintas jaringan mungkin merupakan bagian paling lambat dari persamaan.
Joel Coehoorn
1
Transaksi akan dilakukan atau dibatalkan dengan satu atau lain cara, jadi praktik terbaik adalah selalu mengeluarkan komitmen jika berhasil.
Neil Barnwell
0

Jika Anda menyetel AutoCommit false, maka YA.

Dalam percobaan dengan JDBC (driver Postgresql), saya menemukan bahwa jika kueri pemilihan terputus (karena batas waktu), maka Anda tidak dapat memulai kueri pemilihan baru kecuali Anda melakukan rollback.

Shiv Krishna Jaiswal
sumber
-2

Dalam contoh kode Anda, di mana Anda memilikinya

  1. // Lakukan sesuatu yang berguna

    Apakah Anda menjalankan Pernyataan SQL yang mengubah data?

Jika tidak, tidak ada yang namanya Transaksi "Baca" ... Hanya perubahan dari Pernyataan Sisipkan, Perbarui, dan Hapus (pernyataan yang dapat mengubah data) berada dalam Transaksi ... Yang Anda bicarakan adalah kunci yang SQL Server memakai data yang Anda baca, karena transaksi LAIN yang mempengaruhi data itu. Tingkat kunci ini bergantung pada Tingkat Isolasi SQL Server.

Tetapi Anda tidak dapat melakukan, atau mengembalikan apa pun, jika pernyataan SQL Anda tidak mengubah apa pun.

Jika Anda mengubah data, maka Anda dapat mengubah tingkat isolasi tanpa secara eksplisit memulai transaksi ... Setiap Pernyataan SQL secara implisit ada dalam transaksi. memulai Transaksi secara eksplisit hanya diperlukan untuk memastikan bahwa 2 atau lebih pernyataan berada dalam transaksi yang sama.

Jika yang ingin Anda lakukan hanyalah menyetel tingkat isolasi transaksi, cukup setel CommandText perintah ke "Setel Transaction Isolation level Repeatable Read" (atau tingkat apa pun yang Anda inginkan), setel CommandType ke CommandType.Text, dan jalankan perintah. (Anda dapat menggunakan Command.ExecuteNonQuery ())

CATATAN: Jika Anda melakukan pernyataan baca GANDA, dan ingin semuanya "melihat" status database yang sama seperti yang pertama, maka Anda perlu mengatur isolasi Level atas Pembacaan Berulang atau Serializable ...

Charles Bretana
sumber
// Melakukan sesuatu yang berguna tidak mengubah data apa pun, baca saja. Yang ingin saya lakukan adalah menentukan tingkat isolasi kueri.
Stefan Moser
Kemudian Anda dapat melakukannya tanpa secara eksplisit memulai transaksi dari klien ... Cukup jalankan string sql "Set Transaction Isolation Level ReadUncommitted", "... Read Committed", "... RepeatableRead", "... Snapshot" , atau "... Serializable" "Set Isolation Level Read Committed"
Charles Bretana
3
Transaksi tetap penting meskipun Anda hanya membaca. Jika Anda ingin melakukan beberapa operasi baca, melakukannya di dalam transaksi akan memastikan konsistensi. Melakukannya tanpa satu pun tidak akan.
MarkR
ya maaf, Anda benar, setidaknya ini benar jika tingkat Isolasi diatur ke Baca Berulang atau lebih tinggi.
Charles Bretana
-3

Apakah Anda perlu memblokir orang lain agar tidak membaca data yang sama? Mengapa menggunakan transaksi?

@Joel - Pertanyaan saya akan lebih baik diutarakan sebagai "Mengapa menggunakan transaksi pada permintaan baca?"

@ Stefan - Jika Anda akan menggunakan AdHoc SQL dan bukan proc yang disimpan, cukup tambahkan WITH (NOLOCK) setelah tabel dalam kueri. Dengan cara ini Anda tidak dikenakan biaya tambahan (meskipun minimal) dalam aplikasi dan database untuk transaksi.

SELECT * FROM SomeTable WITH (NOLOCK)

EDIT @ Komentar 3: Karena Anda memiliki "sqlserver" di tag pertanyaan, saya berasumsi bahwa MSSQLServer adalah produk target. Sekarang poin itu telah diklarifikasi, saya telah mengedit tag untuk menghapus referensi produk tertentu.

Saya masih tidak yakin mengapa Anda ingin melakukan transaksi pada operasi baca di tempat pertama.

StingyJack
sumber
1
Untuk mengatur tingkat isolasi sekaligus. Anda dapat menggunakan transaksi untuk benar-benar mengurangi jumlah penguncian kueri.
Joel Coehoorn
1
Saya menggunakan transaksi sehingga saya dapat menggunakan tingkat isolasi yang lebih rendah dan mengurangi penguncian.
Stefan Moser
@StingyJack - Kode ini dapat dijalankan terhadap sejumlah database yang berbeda, jadi NOLOCK bukanlah pilihan.
Stefan Moser