Apakah ROLLBACK merupakan operasi yang cepat?

Jawaban:

14

Untuk SQL Server, Anda dapat berargumen bahwa operasi komit tidak lebih dari menulis LOP_COMMIT_XACT ke file log dan melepaskan kunci, yang tentu saja akan lebih cepat daripada ROLLBACK dari setiap tindakan yang dilakukan transaksi Anda sejak BEGIN TRAN.

Jika Anda mempertimbangkan setiap tindakan transaksi, bukan hanya komitmen, saya masih berpendapat bahwa pernyataan Anda tidak benar. Tidak termasuk faktor eksternal, kecepatan log disk dibandingkan dengan kecepatan disk data misalnya, kemungkinan rollback dari setiap pekerjaan yang dilakukan oleh transaksi akan lebih cepat daripada melakukan pekerjaan di tempat pertama.

Kembalikan adalah membaca file perubahan berurutan dan menerapkannya ke halaman data dalam memori. "Karya" asli harus membuat rencana eksekusi, memperoleh halaman, bergabung dengan baris, dll.

Edit: Itu tergantung sedikit ...

@JackDouglas menunjuk ke artikel ini yang menjelaskan salah satu situasi di mana rollback dapat memakan waktu lebih lama dari operasi aslinya. Contohnya adalah transaksi 14 jam, mau tidak mau menggunakan paralelisme, yang membutuhkan 48+ jam untuk rollback karena rollback kebanyakan single threaded. Kemungkinan besar Anda juga akan mengaduk kumpulan buffer berulang kali, jadi Anda tidak lagi membalikkan perubahan pada halaman dalam memori.

Jadi, versi revisi dari jawaban saya sebelumnya. Berapa lambatnya rollback? Semua hal lain dipertimbangkan, untuk transaksi OLTP tipikal tidak. Di luar batas khas, mungkin perlu waktu lebih lama untuk "membatalkan" daripada "lakukan" tetapi (apakah ini potensi twister lidah?) Mengapa akan tergantung pada bagaimana "lakukan" itu dilakukan.

Sunting2: Sebagai lanjutan dari diskusi dalam komentar, berikut adalah contoh yang sangat dibuat untuk menunjukkan bahwa pekerjaan yang dilakukan adalah faktor utama dalam menentukan biaya relatif dari komitmen vs kembalikan sebagai operasi.

Buat dua tabel dan kemas dengan tidak efisien (ruang kosong per halaman):

SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
SET NOCOUNT ON;
GO

CREATE TABLE dbo.Foo
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)

CREATE TABLE dbo.Bar
(
    col1 INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    , col2 CHAR(4000) NOT NULL DEFAULT REPLICATE('A', 4000)
)
GO

INSERT dbo.Foo DEFAULT VALUES
GO 100000

INSERT dbo.Bar DEFAULT VALUES
GO 100000

Jalankan kueri pembaruan "buruk", mengukur waktu yang diperlukan untuk melakukan pekerjaan dan waktu yang dibutuhkan untuk mengeluarkan komit.

DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

COMMIT TRANSACTION

SELECT 'Commit', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

Lakukan hal yang sama lagi tetapi keluarkan dan ukur rollback.

    DECLARE 
    @StartTime DATETIME2
    , @Rows INT

SET @Rows = 1

CHECKPOINT
DBCC DROPCLEANBUFFERS

BEGIN TRANSACTION

SET @StartTime = SYSDATETIME()

UPDATE
    dbo.bar
SET
    col2 = REPLICATE('B', 4000)
FROM
    dbo.bar b
INNER JOIN
    (
    SELECT TOP(@Rows)
        col1
    FROM
        dbo.foo
    ORDER BY
        NEWID()
    ) f
ON  f.col1 = b.col1
OPTION (MAXDOP 1)

SELECT 'Find and update row', DATEDIFF(ms, @StartTime, SYSDATETIME())

SET @StartTime = SYSDATETIME()

ROLLBACK TRANSACTION

SELECT 'Rollback', DATEDIFF(ms, @StartTime, SYSDATETIME())
GO

Dengan @ Rows = 1 saya mendapatkan yang cukup konsisten:

  • 5500 ms untuk mencari / memperbarui
  • 3ms commit
  • Kembalikan 1ms

Dengan @ Baris = 100:

  • 8500 ms temukan / perbarui
  • 15ms komit
  • Kembalikan 15ms

Dengan @ Baris = 1000:

  • 15000 ms temukan / perbarui
  • 10ms komit
  • Pemunduran 500 ms

Kembali ke pertanyaan awal. Jika Anda mengukur waktu yang diperlukan untuk melakukan pekerjaan ditambah komit, rollback menang dengan mudah karena sebagian besar pekerjaan itu dihabiskan untuk menemukan baris yang akan diperbarui, tidak benar-benar mengubah data. Jika Anda melihat operasi komit dalam isolasi, harus jelas bahwa komit sangat sedikit "berfungsi" seperti itu. Komit adalah "Saya sudah selesai".

Mark Storey-Smith
sumber
2
'kerja kurang' belum tentu 'lebih cepat'
Jack Douglas
Saya tahu bahwa begin tranhanya meningkatkan konter transaksi. Jika saya mengerti Anda, rdbms melakukan semua tugas (bergabung dengan baris, membuat rencana eksekusi ...) di COMMIT?
garik
3
Tidak, semua pekerjaan dilakukan sebelum melakukan. Operasi komit itu sendiri relatif sedikit.
Mark Storey-Smith
@ Mark Saya telah melakukan beberapa tes kasar dan siap memasukkan baris 2m dan melakukan atau memutar kembali. Waktu keseluruhan termasuk rollback bervariasi dari 10 hingga 30, dibandingkan antara 6s dan 14 untuk keseluruhan waktu termasuk komit. YMMV tentu saja tetapi ini menunjukkan bahwa rollback rata-rata hampir sama atau lebih lama dari transaksi asli setidaknya di lingkungan saya.
Jack Douglas
2
Jika Anda mengukur waktu untuk operasi komit untuk menyelesaikan, saya berharap itu minimal kecuali sebuah pos pemeriksaan kebetulan dikeluarkan pada waktu yang sama (yang terpisah dan tidak terkait). Itulah maksud saya, komit sangat sedikit sedangkan kemunduran melakukan semua yang terjadi sebelum komit ditambah sedikit lebih banyak. Perbedaan dalam tes Anda menunjukkan faktor-faktor lain yang berperan, tetapi saya pasti akan mencoba dan mengumpulkan beberapa skrip nanti.
Mark Storey-Smith
13

Untuk Oracle, rollback dapat memakan waktu beberapa kali lebih lama daripada waktu yang dibutuhkan untuk melakukan perubahan yang bergulir kembali. Ini sering tidak masalah karena

  1. Tidak ada kunci yang ditahan saat transaksi berjalan kembali
  2. Ini ditangani oleh proses latar belakang prioritas rendah

Untuk SQL Server saya tidak yakin apakah situasinya sama tetapi orang lain akan mengatakan jika tidak ...

Adapun "mengapa", saya akan mengatakan itu rollbackharus langka , biasanya hanya jika ada kesalahan, dan tentu saja commitmungkin lebih umum - jadi masuk akal untuk mengoptimalkancommit

Jack Douglas
sumber
9

Kembalikan bukan hanya "oh, tidak apa-apa" - dalam banyak kasus itu benar-benar harus membatalkan apa yang sudah dilakukan. Tidak ada aturan bahwa operasi rollback akan selalu lebih lambat atau selalu lebih cepat dari operasi asli, meskipun bahkan jika transaksi asli berjalan secara paralel, rollback tersebut adalah single-threaded. Jika Anda menunggu, saya sarankan paling aman untuk tetap menunggu.

Ini semua berubah dengan SQL Server 2019, tentu saja, dan Pemulihan Basis Data yang Dipercepat (yang, pada penalti yang juga variabel, memungkinkan untuk mengembalikan secara instan terlepas dari ukuran data).

Aaron Bertrand
sumber
2
Dan kita semua memiliki percakapan "butuh waktu lama untuk mengembalikan, mari kita reboot" di beberapa titik, kan?
Mark Storey-Smith
Saya telah melihat banyak klien melakukannya. Beberapa keluar relatif tanpa cedera, yang lain kurang beruntung.
Aaron Bertrand
1
@ MarkStorey-Smith - Jika Anda me-reboot mid-rollback, bukankah SQL Server harus melanjutkan rollback-nya saat startup?
Nick Chammas
2
@Nick itu tergantung - jika rollback diblokir sebelum reboot, misalnya, mungkin berperilaku jauh lebih cepat setelah restart layanan karena proses lain baru saja dimatikan. Ada BANYAK "bagaimana jika" dalam skenario ini - setiap kali Anda me-reboot server atau memulai kembali layanan untuk "memperbaiki" masalah, mungkin ada beberapa masalah yang jauh lebih serius yang sedang dimainkan.
Aaron Bertrand
2
@Nick, ya itulah yang terjadi. Komentar saya dimaksudkan untuk menjadi "lidah di pipi", sehingga Anda akhirnya harus menjelaskan bahwa kepada pemicu orang-orang bahagia yang ingin menekan reboot ketika sesuatu tidak sesuai dengan yang diharapkan.
Mark Storey-Smith
8

Tidak semua transaksi memiliki aktivitas komit yang berkinerja lebih baik daripada kemunduran mereka. Salah satu kasusnya adalah operasi hapus dalam SQL. Ketika transaksi menghapus baris, baris ini ditandai sebagai catatan hantu. Setelah komit dikeluarkan dan tugas pembersihan catatan hantu dimulai, maka hanya catatan ini yang 'dihapus'.

Jika rollback dikeluarkan sebagai gantinya, itu hanya menghilangkan tanda hantu dari catatan ini, dan bukan pernyataan menyisipkan intensif.

StanleyJohns
sumber
Contoh yang bagus tentang bagaimana operasi tertentu dioptimalkan untuk rollback.
Mark Storey-Smith
5

Tidak semuanya. PostgreSQL tidak membutuhkan waktu lebih lama untuk mundur daripada melakukan komitmen karena kedua operasi ini secara identik identik dalam hal disk I / O. Saya tidak benar-benar berpikir ini adalah pertanyaan tentang dioptimalkan untuk melakukan begitu banyak karena ini adalah pertanyaan tentang apa pertanyaan lain yang mengoptimalkan untuk.

Pertanyaan dasar adalah bagaimana Anda mengatasi tata letak pada-disk dan bagaimana ini mempengaruhi komit vs rollback. Db utama yang memutar kembali lebih lambat daripada melakukan cenderung memindahkan data, terutama dari tabel berkerumun, keluar dari struktur data utama dan memasukkannya ke dalam segmen rollback ketika memperbarui data. Ini berarti bahwa untuk mengkomit Anda hanya menjatuhkan segmen rollback tetapi untuk memutar kembali Anda harus menyalin semua data kembali.

Untuk PostgreSQL, semua tabel adalah heap tables dan indeks terpisah. Ini berarti bahwa ketika memutar kembali atau melakukan, tidak ada data yang harus diatur ulang. Ini membuat komit dan kembalikan keduanya cepat.

Namun, itu membuat beberapa hal lain sedikit lebih lambat. Pencarian kunci utama misalnya, harus melintasi file indeks dan kemudian harus mengenai tabel tumpukan (dengan asumsi tidak ada indeks penutup yang berlaku). Ini bukan masalah besar tetapi hal itu menambah pencarian halaman tambahan atau bahkan mungkin beberapa pencarian halaman acak (jika banyak pembaruan telah terjadi pada baris itu) untuk memeriksa informasi dan visibilitas lainnya.

Namun kecepatan di sini bukan masalah optimasi di PostgreSQL untuk operasi tulis vs yang dibaca. Ini adalah keengganan untuk mengistimewakan beberapa operasi baca di atas yang lain. Akibatnya PostgreSQL melakukan rata-rata tentang serta db lain. Hanya operasi tertentu yang mungkin lebih cepat atau lebih lambat.

Jadi saya pikir jawaban sebenarnya adalah bahwa db dioptimalkan untuk beban kerja tertentu di sisi baca dan ini mengarah pada tantangan di sisi tulis. Biasanya di mana ada pertanyaan, komit biasanya, meskipun tidak selalu, akan lebih disukai daripada kemunduran. Namun ini tergantung pada implikasi dari melakukan keduanya (pembaruan berbeda dari yang dihapus).

Chris Travers
sumber
Jawaban yang bagus, tetapi satu sedikit quibble: "Untuk PostgreSQL, semua tabel adalah tumpukan tabel dan indeks terpisah. Ini berarti bahwa ketika memutar kembali atau melakukan, tidak ada data yang harus diatur ulang" ini bukan alasan bahwa tidak ada data yang harus ditata ulang, bukan karena "db utama yang memutar lebih lambat daripada melakukan cenderung untuk memindahkan data", dan pg tidak, seperti yang Anda sebutkan. Oracle juga default untuk menimbun penyimpanan: perbedaan utama adalah bahwa Oracle menggunakan 'undo' dan merebut kembali semua ruang pada komit / kembalikan daripada pergi pada rute 'vakum'.
Jack Douglas