Ketika datang untuk memperbarui baris, banyak alat ORM mengeluarkan pernyataan UPDATE yang mengatur setiap kolom yang terkait dengan entitas tertentu .
Keuntungannya adalah Anda dapat dengan mudah mengelompokkan pernyataan pembaruan karena UPDATE
pernyataan itu sama, apa pun atribut entitas yang Anda ubah. Selain itu, Anda bahkan dapat menggunakan caching pernyataan sisi-server dan sisi klien.
Jadi, jika saya memuat entitas dan hanya menetapkan satu properti:
Post post = entityManager.find(Post.class, 1L);
post.setScore(12);
Semua kolom akan diubah:
UPDATE post
SET score = 12,
title = 'High-Performance Java Persistence'
WHERE id = 1
Sekarang, dengan asumsi bahwa kita memiliki indeks pada title
properti juga, bukankah seharusnya DB menyadari bahwa nilainya tidak berubah?
Dalam artikel ini , Markus Winand mengatakan:
Pembaruan pada semua kolom menunjukkan pola yang sama yang telah kita amati di bagian sebelumnya: waktu respons tumbuh dengan setiap indeks tambahan.
Saya bertanya-tanya mengapa overhead ini karena database memuat halaman data terkait dari disk ke memori dan sehingga dapat mengetahui apakah nilai kolom perlu diubah atau tidak.
Bahkan untuk indeks, itu tidak untuk menyeimbangkan kembali apa pun karena nilai indeks tidak berubah untuk kolom yang belum berubah, namun mereka dimasukkan dalam UPDATE.
Apakah itu karena indeks B + Tree yang terkait dengan kolom tidak berubah yang mubazir perlu dinavigasi juga, hanya agar database menyadari bahwa nilai daunnya masih sama?
Tentu saja, beberapa alat ORM memungkinkan Anda untuk UPDATE hanya properti yang diubah:
UPDATE post
SET score = 12,
WHERE id = 1
Tetapi jenis UPDATE ini mungkin tidak selalu mendapat manfaat dari pembaruan batch atau caching pernyataan ketika properti yang berbeda diubah untuk baris yang berbeda.
sumber
UPDATE
praktis setara denganDELETE
+INSERT
(karena Anda benar-benar membuat yang baru V ersion baris). Overheadnya tinggi, dan tumbuh dengan jumlah indeks , khususnya jika banyak kolom yang menyusunnya benar-benar diperbarui, dan pohon (atau apa pun) yang digunakan untuk mewakili indeks memerlukan perubahan yang signifikan. Itu bukan jumlah kolom yang diperbarui yang relevan, tetapi apakah Anda memperbarui bagian kolom dari indeks.Jawaban:
Saya tahu Anda sebagian besar mengkhawatirkan
UPDATE
dan sebagian besar tentang kinerja, tetapi sebagai sesama pengelola "ORM", izinkan saya memberi Anda perspektif lain tentang masalah membedakan antara nilai "berubah" , "nol" , dan "default" , yang merupakan tiga hal berbeda dalam SQL, tetapi mungkin hanya satu hal di Java dan di sebagian besar ORM:Menerjemahkan alasan Anda ke
INSERT
pernyataanArgumen Anda yang mendukung batchability dan pernyataan cacheability berlaku dengan cara yang sama untuk
INSERT
pernyataan seperti yang mereka lakukan untukUPDATE
pernyataan. Tetapi dalam kasusINSERT
pernyataan, menghilangkan kolom dari pernyataan memiliki semantik yang berbeda dari padaUPDATE
. Itu artinya melamarDEFAULT
. Dua berikut ini setara secara semantik:Ini tidak benar untuk
UPDATE
, di mana dua yang pertama secara semantik setara, dan yang ketiga memiliki makna yang sama sekali berbeda:Sebagian besar API klien basis data, termasuk JDBC, dan akibatnya, JPA, tidak memungkinkan untuk mengikat
DEFAULT
ekspresi ke variabel terikat - sebagian besar karena server juga tidak mengizinkan ini. Jika Anda ingin menggunakan kembali pernyataan SQL yang sama untuk alasan batchability dan cacheability pernyataan tersebut di atas, Anda akan menggunakan pernyataan berikut dalam kedua kasus (dengan asumsi(a, b, c)
semua kolom dit
):Dan karena
c
tidak disetel, Anda mungkin akan mengikat Javanull
ke variabel mengikat ketiga, karena banyak ORM juga tidak dapat membedakan antaraNULL
danDEFAULT
( jOOQ , misalnya menjadi pengecualian di sini). Mereka hanya melihat Javanull
dan tidak tahu apakah ini artinyaNULL
(seperti dalam nilai yang tidak diketahui) atauDEFAULT
(seperti dalam nilai yang tidak diinisialisasi).Dalam banyak kasus, perbedaan ini tidak masalah, tetapi dalam kasus kolom Anda c menggunakan salah satu dari fitur berikut, pernyataan itu salah :
DEFAULT
klausaKembali ke
UPDATE
pernyataanSementara hal di atas berlaku untuk semua database, saya dapat meyakinkan Anda bahwa masalah pemicunya juga berlaku untuk database Oracle. Pertimbangkan SQL berikut:
Ketika Anda menjalankan di atas, Anda akan melihat output berikut:
Seperti yang Anda lihat, pernyataan yang selalu memperbarui semua kolom akan selalu memicu pemicu untuk semua kolom, sedangkan pernyataan yang memperbarui hanya kolom yang berubah akan memecat hanya pemicu yang mendengarkan perubahan spesifik tersebut.
Dengan kata lain:
Perilaku Hibernate saat ini yang Anda gambarkan tidak lengkap dan bahkan dapat dianggap salah di hadapan pemicu (dan mungkin alat lain).
Saya pribadi berpikir bahwa argumen optimisasi cache kueri Anda dinilai terlalu tinggi dalam kasus SQL dinamis. Tentu, akan ada beberapa pertanyaan lagi di cache seperti itu, dan sedikit pekerjaan parsing yang harus dilakukan, tetapi ini biasanya bukan masalah untuk
UPDATE
pernyataan dinamis , apalagi untukSELECT
.Batching tentu saja merupakan masalah, tetapi menurut pendapat saya, satu pembaruan tidak seharusnya dinormalisasi untuk memperbarui semua kolom hanya karena ada sedikit kemungkinan pernyataan tersebut dapat dikelompokkan. Kemungkinannya adalah, ORM dapat mengumpulkan sub-batch dari pernyataan identik yang berurutan dan mengelompokkannya sebagai ganti dari "seluruh batch" (jika ORM bahkan mampu melacak perbedaan antara "berubah" , "null" , dan "default"
sumber
DEFAULT
penggunaan dapat diatasi oleh@DynamicInsert
. Situasi TRIGGER juga dapat diatasi menggunakan cek sukaWHEN (NEW.b <> OLD.b)
atau hanya beralih ke@DynamicUpdate
.Saya kira jawabannya - rumit . Saya mencoba menulis bukti cepat menggunakan
longtext
kolom di MySQL, tetapi jawabannya sedikit tidak meyakinkan. Bukti pertama:Jadi ada perbedaan waktu yang kecil antara nilai lambat + yang diubah, dan lambat + tidak ada nilai yang berubah. Jadi saya memutuskan untuk melihat metrik lain, yaitu halaman yang ditulis:
Jadi sepertinya waktu meningkat karena harus ada perbandingan untuk mengkonfirmasi bahwa nilai itu sendiri belum dimodifikasi, yang dalam kasus longtext 1G membutuhkan waktu (karena terbagi di banyak halaman). Tetapi modifikasi itu sendiri tampaknya tidak berputar melalui redo log.
Saya menduga bahwa jika nilai adalah kolom reguler yang ada di halaman perbandingan hanya menambahkan sedikit overhead. Dan dengan asumsi optimasi yang sama berlaku, ini adalah no-ops ketika datang ke pembaruan.
Jawaban yang lebih panjang
Saya benar-benar berpikir ORM seharusnya tidak menghilangkan kolom yang telah dimodifikasi ( tetapi tidak diubah ), karena optimasi ini memiliki efek samping yang aneh.
Pertimbangkan hal berikut dalam kode pseudo:
Hasilnya jika ORM ingin "Optimalkan keluar" modifikasi tanpa perubahan:
Hasilnya jika ORM mengirim semua modifikasi ke server:
Kasing uji coba di sini bergantung pada
repeatable-read
isolasi (default MySQL), tetapi ada time-window untukread-committed
isolasi di mana session2 membaca terjadi sebelum session1 komit.Dengan kata lain: optimasi hanya aman jika Anda mengeluarkan
SELECT .. FOR UPDATE
untuk membaca baris diikuti olehUPDATE
.SELECT .. FOR UPDATE
tidak menggunakan MVCC dan selalu membaca versi baris terbaru.Sunting: Memastikan set data kasus uji memiliki memori 100%. Hasil pengaturan waktu yang disesuaikan.
sumber