Diminta untuk Tidak Menggunakan Transaksi dan Menggunakan Solusi untuk Simulasi


43

Saya telah mengembangkan T-SQL selama beberapa tahun dan saya selalu menggali lebih dalam, terus mempelajari semua yang saya bisa tentang semua aspek bahasa. Saya baru-baru ini mulai bekerja di perusahaan baru dan telah menerima apa yang saya pikir merupakan saran aneh mengenai transaksi. Jangan pernah menggunakannya. Alih-alih, gunakan solusi yang mensimulasikan transaksi. Ini berasal dari DBA kami yang bekerja dalam satu basis data dengan banyak transaksi dan selanjutnya, banyak pemblokiran. Basis data tempat saya bekerja tidak mengalami masalah ini dan saya melihat transaksi telah digunakan di masa lalu.

Saya memahami bahwa pemblokiran diharapkan dengan transaksi karena sudah menjadi sifatnya untuk melakukan hal itu dan jika Anda dapat pergi tanpa menggunakannya, tentu saja lakukanlah. Tetapi saya memiliki banyak kesempatan di mana setiap pernyataan HARUS dilaksanakan dengan sukses. Jika salah satu gagal mereka semua harus gagal untuk melakukan.

Saya selalu menjaga ruang lingkup transaksi saya sesempit mungkin, selalu digunakan bersama dengan SET XACT_ABORT ON dan selalu dalam TRY / CATCH.

Contoh:

CREATE SCHEMA someschema;
GO


CREATE TABLE someschema.tableA
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColA VARCHAR(10) NOT NULL
);
GO

CREATE TABLE someschema.tableB
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColB VARCHAR(10) NOT NULL
); 
GO


CREATE PROCEDURE someschema.ProcedureName @ColA VARCHAR(10), 
                                          @ColB VARCHAR(10)
AS
SET NOCOUNT, XACT_ABORT ON;
BEGIN
BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);

--Implement error
    SELECT 1/0 

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@trancount > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END;
    THROW;
    RETURN;
END CATCH;
END;
GO

Inilah yang mereka sarankan agar saya lakukan.

GO



CREATE PROCEDURE someschema.ProcedureNameNoTransaction @ColA VARCHAR(10), 
                                                       @ColB VARCHAR(10)
AS
SET NOCOUNT ON;
BEGIN
BEGIN TRY
    DECLARE @tableAid INT;
    DECLARE @tableBid INT;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);
    SET @tableAid = SCOPE_IDENTITY();

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);
    SET @tableBid = SCOPE_IDENTITY();

--Implement error
    SELECT 1/0 

END TRY
BEGIN CATCH
    DELETE FROM someschema.tableA
    WHERE id = @tableAid;

    DELETE FROM someschema.tableB
    WHERE id = @tableBid;

    THROW;

    RETURN;
END CATCH;
END;
GO

Pertanyaan saya kepada komunitas adalah sebagai berikut. Apakah ini masuk akal sebagai solusi untuk transaksi?

Pendapat saya dari apa yang saya ketahui tentang transaksi dan apa yang diusulkan solusinya adalah tidak, ini bukan solusi yang layak dan memperkenalkan banyak poin kegagalan.

Dalam solusi yang disarankan, saya melihat empat transaksi implisit terjadi. Dua sisipan dalam percobaan dan kemudian dua transaksi lagi untuk penghapusan tangkapan. Itu "membatalkan" sisipan tetapi tanpa memutar apa pun sehingga tidak ada yang benar-benar dibatalkan.

Ini adalah contoh yang sangat mendasar untuk menunjukkan konsep yang mereka sarankan. Beberapa prosedur tersimpan aktual yang telah saya lakukan ini membuatnya sangat panjang dan sulit untuk dikelola karena "memutar kembali" beberapa hasil set vs dua nilai parameter dalam contoh ini menjadi sangat rumit seperti yang dapat Anda bayangkan. Karena "memutar kembali" sedang dilakukan secara manual sekarang, kesempatan untuk melewatkan sesuatu karena nyata.

Masalah lain yang menurut saya ada adalah untuk timeout atau koneksi terputus. Apakah ini masih bisa dibatalkan? Ini adalah pemahaman saya tentang mengapa SET XACT_ABORT ON harus digunakan sehingga dalam kasus ini, transaksi akan dibatalkan.

Terima kasih atas tanggapan Anda sebelumnya!


4
Komentar yang tidak sesuai dengan tujuan yang disebutkan telah dihapus, atau dipindahkan ke jawaban Wiki Komunitas.
Paul White

Jawaban:


61

Anda tidak dapat tidak menggunakan transaksi di SQL Server (dan mungkin setiap RDBMS yang tepat lainnya). Dengan tidak adanya batasan transaksi eksplisit ( begin transaction... commit) setiap pernyataan SQL memulai transaksi baru, yang secara implisit dilakukan (atau dibatalkan) setelah pernyataan selesai (atau gagal).

Simulasi transaksi yang disarankan oleh orang yang menampilkan dirinya sebagai "DBA" Anda gagal memastikan tiga dari empat properti yang diperlukan untuk pemrosesan transaksi, karena hanya mengatasi kesalahan "lunak" dan tidak mampu menangani kesalahan "keras", seperti terputusnya jaringan, pemadaman listrik, kegagalan disk, dan sebagainya.

  • Atomicity: gagal. Jika kesalahan "keras" terjadi di suatu tempat di tengah-tengah transaksi semu Anda, perubahan itu akan non-atom.

  • Konsistensi: gagal. Berikut ini dari atas bahwa data Anda akan dalam keadaan tidak konsisten setelah kesalahan "keras".

  • Isolasi: gagal. Ada kemungkinan bahwa transaksi semu bersamaan mengubah beberapa data yang dimodifikasi oleh transaksi semu Anda sebelum Anda selesai.

  • Daya tahan: sukses. Perubahan yang Anda buat akan tahan lama, server database akan memastikan bahwa; ini adalah satu-satunya pendekatan rekan Anda yang tidak bisa gagal.

Kunci adalah metode yang banyak digunakan dan secara empiris berhasil untuk memastikan ACIDity transaksi dalam segala jenis atau RDBMSes (situs ini menjadi contoh). Saya merasa sangat tidak mungkin bahwa DBA acak dapat menghasilkan solusi yang lebih baik untuk masalah konkurensi daripada ratusan, mungkin ribuan ilmuwan dan insinyur komputer yang telah membangun beberapa sistem basis data yang menarik selama 50 tahun terakhir, apa, 50? 60 tahun? (Saya menyadari ini agak keliru sebagai argumen "banding ke otoritas", tapi saya akan tetap menggunakannya.)

Sebagai kesimpulan, abaikan saran "DBA" Anda jika Anda bisa, lawanilah jika Anda punya semangat, dan kembali ke sini dengan masalah konkurensi spesifik jika muncul.


14

Ada beberapa kesalahan yang sangat parah sehingga blok CATCH tidak pernah dimasukkan. Dari dokumentasi

Kesalahan yang memiliki tingkat keparahan 20 atau lebih tinggi yang menghentikan pemrosesan tugas SQL Server Database Engine untuk sesi. Jika terjadi kesalahan yang memiliki tingkat keparahan 20 atau lebih tinggi dan koneksi basis data tidak terganggu, COBA ... CATCH akan menangani kesalahan tersebut.

Perhatian, seperti permintaan interupsi klien atau koneksi klien terputus.

Ketika sesi diakhiri oleh administrator sistem dengan menggunakan pernyataan KILL.

...

Kompilasi kesalahan, seperti kesalahan sintaksis, yang mencegah batch berjalan.

Kesalahan yang terjadi ... karena resolusi nama yang ditangguhkan.

Banyak dari ini mudah diproduksi melalui SQL dinamis. Membatalkan pernyataan seperti yang Anda tunjukkan tidak akan melindungi data Anda dari kesalahan tersebut.


2
Benar - dan jika tidak ada yang lain, klien sekarat saat mengeksekusi kode akan merupakan kesalahan "begitu parah sehingga blok CATCH tidak pernah dimasukkan". Tidak peduli seberapa besar Anda mempercayai perangkat lunak (bukan hanya kode Anda sendiri, tetapi SETIAP bagian dari SEMUA tumpukan perangkat lunak yang terlibat), selalu ada kemungkinan kegagalan perangkat keras (sekali lagi, berpotensi di mana saja di sepanjang rantai) menghentikan Anda kedinginan setiap saat . Mempertahankan hal ini adalah pertahanan yang baik terhadap pemikiran yang mengarah pada "solusi" semacam ini.
dgould

2
Juga, Anda mungkin menjadi korban kebuntuan. Blok CATCH Anda berjalan tetapi melempar jika mereka mencoba menulis ke database.
Yosua

10

i-one : Solusi yang disarankan kepada Anda memungkinkan (setidaknya) melanggar "A" dari ACID . Misalnya, jika SP sedang dijalankan oleh klien jarak jauh dan koneksi terputus, maka sebagian "komit" / "kembalikan" dapat terjadi, karena server dapat mengakhiri sesi antara dua penyisipan / penghapusan (dan batalkan eksekusi SP sebelum mencapai akhirnya) .

Apakah ini masuk akal sebagai solusi untuk transaksi?

dan-guzman : Tidak,CATCHblok tidak pernah dieksekusi dalam kasus batas waktu kueri karena API klien membatalkan batch. Tanpa transaksi,SET XACT_ABORT ONtidak dapat mengembalikan apa pun selain pernyataan saat ini.

tibor-karaszi : Anda memiliki 4 transaksi, yang berarti lebih banyak masuk ke file log transaksi. Ingatlah bahwa setiap transaksi memerlukan catatan log catatan yang sinkron hingga saat itu, yaitu Anda mendapatkan kinerja yang lebih buruk dari aspek itu saat menggunakan banyak transaksi.

rbarryyoung : Jika mereka mendapatkan banyak pemblokiran maka mereka perlu memperbaiki desain data mereka, merasionalisasi urutan akses tabel mereka, atau menggunakan tingkat isolasi yang lebih tepat. Mereka berasumsi bahwa masalah mereka (dan kegagalan untuk memahaminya) akan menjadi masalah Anda. Bukti dari jutaan database lain adalah tidak.

Juga, apa yang mereka coba terapkan secara efektif adalah konkurensi optimis orang miskin. Yang harus mereka lakukan adalah menggunakan konkurensi optimis terbaik di dunia, yang sudah ada di dalam SQL Server. Ini pergi ke titik isolasi di atas. Dalam semua kemungkinan mereka perlu beralih dari tingkat isolasi konkurensi pesimis apa pun yang saat ini mereka gunakan ke salah satu tingkat isolasi konkurensi optimis, SNAPSHOTatau READ_COMMITTED_SNAPSHOT. Ini secara efektif akan melakukan hal yang sama dengan kode manual mereka, kecuali itu akan melakukannya dengan benar.

ross-presser : Jika Anda memiliki proses yang berjalan sangat lama - seperti sesuatu terjadi hari ini dan minggu depan sesuatu harus ditindaklanjuti, dan jika hal minggu depan gagal maka hari ini harus gagal surut - Anda mungkin ingin melihat ke dalam kisah - kisah . Sebenarnya ini di luar basis data, karena memerlukan layanan bus.


5

Kode ide buruk hanya akan lebih mahal untuk diperbaiki.

Jika ada masalah pemblokiran menggunakan transaksi eksplisit (rollback / commit), arahkan DBA Anda ke internet untuk beberapa ide hebat untuk mengatasi masalah tersebut.

Berikut cara untuk membantu meringankan pemblokiran: https://www.sqlservercentral.com/articles/using-indexes-to-reduce-blocking-in-concurrent-transactions

Indeks mengurangi jumlah pencarian yang harus terjadi dalam tabel / halaman untuk menemukan baris / set baris. Mereka umumnya dilihat sebagai metode untuk mengurangi waktu eksekusi untuk query SELECT * dan juga. Mereka tidak dianggap cocok untuk tabel yang terlibat dalam sejumlah besar UPDATES. Sebenarnya INDEKS ditemukan tidak menguntungkan dalam kasus ini karena mereka meningkatkan waktu yang dibutuhkan untuk menyelesaikan permintaan UPDATE.

Tapi ini tidak selalu terjadi. Menggali sedikit ke dalam pelaksanaan pernyataan UPDATE kami menemukan bahwa itu juga melibatkan mengeksekusi pernyataan SELECT terlebih dahulu. Ini adalah skenario khusus dan sering terlihat di mana kueri memperbarui set baris yang saling eksklusif. INDEKS di sini dapat menyebabkan peningkatan signifikan dalam kinerja mesin basis data yang bertentangan dengan kepercayaan populer.


4

Strategi transaksi palsu berbahaya karena memungkinkan masalah konkurensi yang secara spesifik mencegah transaksi. Pertimbangkan bahwa dalam contoh kedua, salah satu data dapat diubah di antara pernyataan.

Penghapusan transaksi palsu tidak DIJAMIN untuk dijalankan atau berhasil. Jika server database mati selama transaksi palsu, beberapa tetapi tidak semua efek akan tetap ada. Mereka juga tidak dijamin berhasil dalam cara mengatakan rollback transaksi.

Strategi ini mungkin bekerja dengan sisipan, tetapi pasti tidak akan bekerja dengan pembaruan atau penghapusan (tidak ada pernyataan SQL mesin waktu).

Jika konkurensi transaksi yang ketat menyebabkan pemblokiran ada banyak solusi, bahkan solusi yang mengurangi tingkat perlindungan ... ini adalah cara yang tepat untuk menyelesaikan masalah.

DBA Anda menawarkan solusi yang mungkin berfungsi baik jika hanya ada satu pengguna basis data, tetapi sama sekali tidak layak untuk segala jenis penggunaan serius.


4

Ini bukan masalah pemrograman, melainkan masalah interpersonal / miskomunikasi. Kemungkinan besar "DBA" Anda khawatir tentang kunci, bukan transaksi.

Jawaban lain sudah menjelaskan mengapa Anda harus menggunakan transaksi ... Maksud saya apa yang dilakukan RDBMS, tanpa transaksi yang digunakan dengan benar tidak ada integritas data, jadi saya akan fokus pada bagaimana menyelesaikan masalah nyata, yaitu: cari tahu mengapa "DBA" Anda mengembangkan alergi terhadap transaksi dan meyakinkannya untuk berubah pikiran.

Saya pikir orang ini membingungkan "skenario tertentu di mana kode yang buruk mengakibatkan kinerja yang mengerikan" dengan "semua transaksi buruk." Saya tidak akan mengharapkan DBA yang kompeten untuk melakukan kesalahan itu, jadi itu sangat aneh. Mungkin dia memiliki pengalaman yang sangat buruk dengan beberapa kode yang mengerikan?

Pertimbangkan skenario seperti ini:

BEGIN
UPDATE or DELETE some row, which takes locks it
...do something that takes a while
...perform other queries
COMMIT

Gaya penggunaan transaksi ini memegang kunci (atau beberapa kunci) yang berarti transaksi lain yang memukul baris yang sama harus menunggu. Jika kunci dipegang untuk waktu yang lama, dan terutama jika banyak transaksi lain ingin mengunci baris yang sama, ini benar-benar dapat merusak kinerja.

Yang dapat Anda lakukan adalah bertanya kepadanya mengapa ia memiliki ide aneh yang salah tentang tidak menggunakan transaksi, jenis pertanyaan apa yang bermasalah, dll. Kemudian cobalah membujuknya bahwa Anda pasti akan menghindari skenario buruk yang serupa, bahwa Anda akan memantau penggunaan kunci Anda dan kinerja, meyakinkan dia, dll.

Apa yang dia katakan adalah "jangan sentuh obeng!" jadi kode yang Anda posting pada pertanyaan Anda pada dasarnya menggunakan palu untuk menggerakkan sekrup. Opsi yang jauh lebih baik adalah meyakinkan dia bahwa Anda tahu cara menggunakan obeng ...

Saya bisa memikirkan beberapa contoh ... well, mereka menggunakan MySQL tapi itu juga bisa berhasil.

Ada sebuah forum di mana indeks teks lengkap perlu beberapa saat untuk memperbarui. Ketika pengguna mengirimkan posting, transaksi akan memperbarui tabel topik untuk meningkatkan jumlah posting dan tanggal posting terakhir (sehingga mengunci baris topik), kemudian memasukkan posting, dan transaksi akan menahan kunci sampai indeks teks lengkap selesai memperbarui dan KOMIT sudah selesai.

Karena ini berjalan pada rustbucket dengan RAM terlalu sedikit, memperbarui mengatakan indeks fulltext sering mengakibatkan beberapa detik IO acak yang intens pada drive berputar lambat tunggal di dalam kotak.

Masalahnya adalah bahwa orang yang mengklik topik menyebabkan kueri untuk meningkatkan jumlah tampilan pada topik, yang juga membutuhkan kunci pada baris topik. Dengan demikian, tidak ada yang bisa melihat topik sementara indeks teks lengkapnya sedang diperbarui. Maksud saya, baris bisa dibaca, tetapi memperbarui itu akan mengunci.

Lebih buruk lagi, posting akan memperbarui jumlah posting di tabel forum induk dan juga menahan kunci sementara indeks teks lengkap sedang memperbarui ... yang membekukan seluruh forum selama beberapa detik dan menyebabkan banyak permintaan menumpuk di antrian server web .

Solusi adalah dengan mengambil kunci dalam urutan yang benar: MULAI, masukkan posting dan perbarui indeks teks lengkap tanpa mengambil kunci apa pun, kemudian dengan cepat perbarui baris topik / forum dengan jumlah posting dan tanggal posting terakhir, dan COMMIT. Itu sepenuhnya memecahkan masalah. Itu hanya bergerak di sekitar beberapa pertanyaan, sangat sederhana.

Dalam hal ini, transaksi bukan masalah ... Itu memperoleh kunci yang tidak perlu sebelum operasi yang panjang. Contoh lain dari hal-hal yang harus dihindari saat memegang kunci dalam transaksi: menunggu input pengguna, mengakses banyak data yang tidak di-cache dari drive yang berputar lambat, IO jaringan, dll.

Tentu saja, kadang-kadang, Anda tidak punya pilihan dan Anda harus melakukan pemrosesan yang panjang sambil memegang kunci yang tidak praktis. Ada trik di sekitar ini (beroperasi pada salinan data, dll) tetapi cukup sering kemacetan kinerja berasal dari kunci yang tidak sengaja diperoleh, dan hanya menyusun ulang kueri memecahkan masalah. Lebih baik lagi, menyadari kunci yang diambil saat menulis pertanyaan ...

Saya tidak akan mengulangi jawaban lain tapi sungguh ... menggunakan transaksi. Masalah Anda meyakinkan "DBA" Anda, tidak mengerjakan fitur terpenting dari basis data ...


3

TLDR: Gunakan tingkat isolasi yang tepat .

Seperti yang Anda perhatikan dengan benar pendekatan tanpa transaksi dan dengan pemulihan "manual" bisa sangat kompleks. Kompleksitas yang tinggi biasanya berarti lebih banyak waktu untuk mengimplementasikannya dan lebih banyak waktu untuk memperbaiki kesalahan (karena kompleksitas menyebabkan lebih banyak kesalahan dalam implementasi). Ini berarti pendekatan semacam itu dapat membuat pelanggan Anda jauh lebih mahal.

Perhatian utama dari rekan "dba" Anda adalah kinerja. Salah satu cara untuk memperbaikinya adalah dengan menggunakan tingkat isolasi yang tepat. Misalkan Anda memiliki prosedur yang menyediakan beberapa jenis data ikhtisar kepada pengguna. Prosedur seperti itu tidak harus menggunakan tingkat isolasi SERIALIZABLE. Dalam banyak kasus READ UNCOMMITTED bisa cukup memadai. Ini berarti, prosedur tersebut tidak akan diblokir oleh transaksi Anda yang membuat atau memodifikasi beberapa data.

Saya sarankan Anda meninjau semua fungsi / prosedur yang ada dalam database Anda, mengevaluasi tingkat isolasi yang wajar untuk masing-masing, menjelaskan manfaat kinerja kepada pelanggan Anda. Kemudian sesuaikan fungsi / prosedur ini sesuai.


2

Anda juga dapat memutuskan untuk menggunakan tabel OLTP Dalam Memori. Mereka tentu saja masih menggunakan transaksi, tetapi tidak ada pemblokiran yang terlibat.
Alih-alih memblokir semua operasi akan berhasil, tetapi selama fase komit mesin akan memeriksa konflik transaksi dan salah satu komit mungkin gagal. Microsoft menggunakan istilah "Penguncian optimis".
Jika masalah penskalaan disebabkan oleh konflik antara dua operasi penulisan, seperti dua transaksi bersamaan yang mencoba memperbarui baris yang sama, OLTP Dalam Memori memungkinkan satu transaksi berhasil dan gagal transaksi lainnya. Transaksi yang gagal harus diajukan kembali baik secara eksplisit maupun implisit, mencoba kembali transaksi.
Lebih lanjut di: Dalam memori OLTP


-5

Ada cara sekitar menggunakan transaksi hingga batas tertentu dan itu adalah dengan mengubah model data Anda menjadi lebih berorientasi objek. Jadi, alih-alih menyimpan misalnya data demografis tentang seseorang dalam beberapa tabel dan menghubungkannya satu sama lain dan memerlukan transaksi, Anda dapat memiliki dokumen JSON tunggal yang menyimpan semua yang Anda ketahui tentang orang itu dalam satu bidang. Tentu saja bekerja jauh bahwa peregangan domain adalah tantangan desain lain, paling baik dilakukan oleh pengembang bukan DBA

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.