Pada titik apa database memperbarui indeksnya dalam suatu transaksi?


11

Saya mencoba memahami urutan peristiwa dalam sisipan yang melibatkan indeks dan transaksi.

Sebagai contoh, dokumentasi Oracle menyatakan:

Jika Anda membuat [atau memiliki] satu atau lebih indeks sebelum memuat data, database kemudian harus memperbarui setiap indeks saat setiap baris dimasukkan.

Tapi apa yang terjadi jika saya membuat transaksi, memasukkan lima baris, lalu komit? Apakah indeks diperbarui untuk setiap sisipan, atau hanya pada titik komit?

Logika memberi tahu saya bahwa mereka hanya akan diperbarui pada titik komit, karena indeks yang diperbarui tidak mungkin digunakan sampai catatan itu dibuat. Tetapi apakah itu benar?

Jika demikian, ketika saya memiliki 1m baris untuk disisipkan, untuk kinerja terbaik saya harus melakukan satu komit besar dari semua baris, bukan 10 transaksi dari catatan 100k? Tentu saja saya menyadari ini berisiko rollback lebih besar jika baris 999.999 gagal.

Mohon maaf jika terminologi saya sedikit keluar. Saya bukan DBA karena perdagangan. Saya tidak begitu tertarik pada database tertentu, seperti database pada umumnya, meskipun Oracle dan Postgres adalah yang paling saya gunakan. Saya telah mencari pada topik ini tetapi tidak dapat menemukan jawaban yang pasti.

Jawaban:


8

Saya bekerja dengan SQL Server dan Oracle. Mungkin ada beberapa pengecualian, tetapi untuk platform tersebut jawaban umumnya adalah bahwa data dan indeks akan diperbarui pada saat yang sama.

Saya pikir akan sangat membantu untuk menarik perbedaan antara ketika indeks diperbarui untuk sesi yang memiliki transaksi dan untuk sesi lainnya. Secara default, sesi lain tidak akan melihat indeks yang diperbarui sampai transaksi dilakukan. Namun, sesi yang memiliki transaksi akan segera melihat indeks yang diperbarui.

Untuk satu cara untuk memikirkannya, pertimbangkan di meja dengan kunci utama. Dalam SQL Server dan Oracle ini diimplementasikan sebagai indeks. Sebagian besar waktu kami ingin ada kesalahan segera jika INSERTdilakukan yang akan melanggar kunci utama. Agar itu terjadi, indeks harus diperbarui bersamaan dengan data. Perhatikan bahwa platform lain, seperti Postgres, memungkinkan batasan yang ditangguhkan yang diperiksa hanya ketika transaksi dilakukan.

Berikut ini adalah demo Oracle cepat yang menunjukkan kasus umum:

CREATE TABLE X_TABLE (PK INT NULL, PRIMARY KEY (PK));

INSERT INTO X_TABLE VALUES (1);
INSERT INTO X_TABLE VALUES (1); -- no commit

INSERTPernyataan kedua melempar kesalahan:

Kesalahan SQL: ORA-00001: kendala unik (XXXXXX.SYS_C00384850) dilanggar

00001. 00000 - "batasan unik (% s.% S) dilanggar"

* Penyebab: Pernyataan UPDATE atau INSERT berusaha memasukkan kunci duplikat. Untuk Oracle Tepercaya yang dikonfigurasi dalam mode MAC DBMS, Anda dapat melihat pesan ini jika entri duplikat ada di tingkat yang berbeda.

* Tindakan: Hapus batasan unik atau jangan masukkan kunci.

Jika Anda lebih suka melihat tindakan pembaruan indeks di bawah ini adalah demo sederhana di SQL Server. Pertama-tama buat tabel dua kolom dengan satu juta baris dan indeks nonclustered pada VALkolom:

DROP TABLE IF EXISTS X_TABLE_IX;

CREATE TABLE X_TABLE_IX (
ID INT NOT NULL,
VAL VARCHAR(10) NOT NULL
PRIMARY KEY (ID)
);

CREATE INDEX X_INDEX ON X_TABLE_IX (VAL);

-- insert one million rows with N from 1 to 1000000
INSERT INTO X_TABLE_IX
SELECT N, N FROM dbo.Getnums(1000000);

Kueri berikut dapat menggunakan indeks yang tidak tercakup karena indeks adalah indeks yang mencakup untuk permintaan itu. Ini berisi semua data yang diperlukan untuk menjalankannya. Seperti yang diharapkan, tidak ada pengembalian yang dikembalikan.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

permintaan 1

Sekarang mari kita mulai transaksi dan perbarui VALuntuk hampir semua baris dalam tabel:

BEGIN TRANSACTION

UPDATE X_TABLE_IX
SET VAL = 'A'
WHERE ID <> 1;

Ini adalah bagian dari rencana permintaan untuk itu:

permintaan 2

Dilingkari dengan warna merah adalah pembaruan ke indeks yang tidak tercakup. Dilingkari dengan warna biru adalah pembaruan ke indeks berkerumun, yang pada dasarnya adalah data tabel. Meskipun transaksi belum dilakukan, kami melihat bahwa data dan indeks diperbarui sebagai bagian dari eksekusi permintaan. Perhatikan bahwa Anda tidak akan selalu melihat ini dalam paket tergantung pada ukuran data yang terlibat bersama dengan faktor-faktor lainnya.

Dengan transaksi yang masih belum dilakukan, mari kembali ke SELECTkueri dari atas.

SELECT *
FROM X_TABLE_IX
WHERE VAL = 'A';

masukkan deskripsi gambar di sini

Pengoptimal kueri masih dapat menggunakan indeks dan kali ini memperkirakan bahwa 999999 baris akan dikembalikan. Mengeksekusi kueri mengembalikan hasil yang diharapkan.

Itu adalah demo sederhana tetapi mudah-mudahan itu sedikit beres.

Selain itu, saya mengetahui beberapa kasus di mana dapat dikatakan bahwa indeks tidak segera diperbarui. Ini dilakukan karena alasan kinerja dan pengguna akhir tidak boleh dapat melihat data yang tidak konsisten. Misalnya, kadang-kadang menghapus tidak akan sepenuhnya diterapkan ke indeks di SQL Server. Proses latar belakang berjalan dan akhirnya membersihkan data. Anda dapat membaca tentang catatan hantu jika Anda penasaran.


Itu jawaban super - dan juga menjawab hal lain yang saya tanyakan: apakah pelanggaran kunci primer (atau yang serupa) akan terjadi pada Sisipan atau Komit. Terima kasih atas tanggapan lengkapnya.
Mark Ireland

Pertanyaan terkait (tentang kapan pelanggaran kendala akan terjadi) terkait dengan apakah Anda menggunakan transaksi yang ditangguhkan atau tidak. SQL Server misalnya, belum menerapkan transaksi yang ditangguhkan, sehingga semua pelanggaran terjadi di akhir laporan. DBMS lain memiliki (Postgres misalnya, meskipun tidak untuk semua jenis kendala), jadi ketika Anda kendala ditangguhkan, pelanggaran akan diperiksa pada tahap komitmen transaksi).
ypercubeᵀᴹ

Oracle juga mendukung batasan yang ditangguhkan
BobC

1

Pengalaman saya adalah bahwa penyisipan 1.000.000 baris sebenarnya akan membutuhkan lebih banyak sumber daya dan membutuhkan waktu lebih lama untuk diselesaikan daripada jika Anda menggunakan sisipan batch. Ini dapat diimplementasikan, sebagai contoh, ke dalam 100 sisipan 10.000 baris.

Ini mengurangi overhead batch yang dimasukkan dan, jika batch gagal, itu adalah rollback yang lebih kecil.

Dalam kasus apa pun, untuk SQL Server ada utilitas bcp atau perintah BULK INSERT yang dapat digunakan untuk melakukan batch insert.

Dan, tentu saja, Anda juga dapat menerapkan kode Anda sendiri untuk menangani pendekatan ini.


1
Secara umum, jika Anda perlu memasukkan sejumlah besar baris pada tabel yang membutuhkan indeks, kemungkinan akan lebih cepat untuk menjatuhkan indeks, memuat data dan kemudian membangun kembali indeks. Oracle juga mendukung opsi load path langsung, menggunakan petunjuk / * + APPEND * /.
BobC
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.