Apa yang lebih cepat, satu permintaan besar atau banyak permintaan kecil?

68

Saya telah bekerja untuk perusahaan yang berbeda, dan saya perhatikan bahwa beberapa dari mereka lebih suka memiliki pandangan yang akan bergabung dengan sebuah meja dengan semua "kerabat" nya. Tetapi kemudian pada aplikasi beberapa kali, kita hanya perlu menggunakan 1 kolom saja.

Jadi apakah lebih cepat membuat pilihan sederhana, dan kemudian "bergabung" dengan kode sistem?

Sistem bisa berupa php, java, asp, bahasa apa pun yang terhubung ke database.

Jadi pertanyaannya adalah, apa yang lebih cepat dari Sisi Server (php, java, asp, ruby, python ...) ke database menjalankan satu permintaan yang mendapatkan semua yang kita butuhkan atau pergi dari sisi server ke database dan menjalankan permintaan yang hanya mendapatkan kolom dari satu tabel pada saat itu?

sudo.ie
sumber
2
Implementasi 'SQL' manakah yang Anda gunakan? MySQL, Microsoft SQL Server, Oracle, Postgresql, dll? Harap perbarui tag Anda.
RLF
1
Mysql dan Postgresql
sudo.ie
6
Pengalaman saya adalah bahwa MySQL tidak suka query yang rumit dan biasanya lebih cepat dengan query yang sangat sederhana (tetapi lebih). Pengoptimal permintaan Postgres jauh lebih baik dan biasanya lebih efisien untuk menjalankan satu permintaan besar.
a_horse_with_no_name
3
@a_horse_with_no_name Itu generalisasi yang sangat luas, khususnya dalam konteks pertanyaan ini. MySQL optimizer memang sangat sederhana dengan desain, dan dapat menyebabkan masalah dengan bergabung dan sub-pertanyaan - terutama pada versi MySQL yang lebih lama - yang telah menghasilkan rencana lebih cepat di PostgreSQL, sementara MySQL bisa sangat cepat untuk beban OLTP murni. Namun, dalam konteks pertanyaan, satu permintaan besar akan lebih cepat, katakanlah - dalam skenario yang lebih buruk - SELECT di dalam loop pemrograman (tidak peduli RDBMS digunakan).
jynus
2
@jynus: baik, pertanyaannya adalah sangat luas (ditambah: Aku berkata "dalam pengalaman saya" - orang lain mungkin memiliki pengalaman yang berbeda). Permintaan di dalam LOOP tidak pernah merupakan ide yang baik dan hampir selalu merupakan hasil dari desain yang buruk atau kurangnya pemahaman bagaimana bekerja dengan database relasional.
a_horse_with_no_name

Jawaban:

69

Apa yang akan menjawab pertanyaan Anda adalah subjek JOIN DECOMPOSITION.

Menurut Halaman 209 dari Buku itu

MySQL Kinerja Tinggi

Anda bisa menguraikan gabung dengan menjalankan beberapa kueri tabel tunggal alih-alih gabung multitable, dan kemudian melakukan gabung dalam aplikasi. Misalnya, alih-alih permintaan tunggal ini:

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

Anda dapat menjalankan kueri ini:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

Kenapa kamu melakukan ini? Sekilas terlihat boros, karena Anda telah meningkatkan jumlah kueri tanpa mendapatkan balasan apa pun. Namun, restrukturisasi semacam itu sebenarnya dapat memberikan keuntungan kinerja yang signifikan:

  • Caching bisa lebih efisien. Banyak aplikasi cache "objek" yang memetakan langsung ke tabel. Dalam contoh ini, jika objek dengan tag mysqlsudah di-cache, aplikasi akan melewati kueri pertama. Jika Anda menemukan posting dengan ID 123, 567, atau 908 di cache, Anda dapat menghapusnya dari IN()daftar. Tembolok kueri mungkin juga mendapat manfaat dari strategi ini. Jika hanya satu tabel yang sering berubah, penguraian gabungan dapat mengurangi jumlah cacat cache.
  • Menjalankan query secara individual terkadang dapat mengurangi pertikaian kunci
  • Melakukan bergabung dalam aplikasi memudahkan untuk skala database dengan menempatkan tabel pada server yang berbeda.
  • Pertanyaan itu sendiri bisa lebih efisien. Dalam contoh ini, menggunakan IN()daftar alih-alih bergabung memungkinkan MySQL mengurutkan ID baris dan mengambil baris lebih optimal daripada yang mungkin dilakukan dengan bergabung.
  • Anda dapat mengurangi akses baris yang berlebihan. Melakukan join dalam aplikasi berarti mengambil setiap baris hanya sekali., Sedangkan join dalam query pada dasarnya adalah denormalisasi yang mungkin berulang kali mengakses data yang sama. Untuk alasan yang sama, restrukturisasi tersebut juga dapat mengurangi total lalu lintas jaringan dan penggunaan memori.
  • Sampai batas tertentu, Anda dapat melihat teknik ini sebagai pengimplementasian hash join secara manual, alih-alih algoritma nested loop yang digunakan MySQL untuk menjalankan join. Gabung hash mungkin lebih efisien.

Akibatnya, melakukan penggabungan dalam aplikasi bisa lebih efisien ketika Anda melakukan cache dan menggunakan kembali banyak data dari kueri sebelumnya, Anda mendistribusikan data di beberapa server, Anda mengganti IN()gabung dengan daftar, atau gabungan merujuk ke tabel yang sama beberapa kali.

PENGAMATAN

Saya suka bulletpoint pertama karena InnoDB agak berat ketika crosscheck cache kueri.

Sedangkan untuk bulletpoint terakhir, saya menulis posting kembali pada 11 Maret 2013 ( Apakah ada perbedaan eksekusi antara kondisi JOIN dan kondisi WHERE? ) Yang menjelaskan algoritma nested loop. Setelah membacanya, Anda akan melihat seberapa bagus dekomposisi gabungan.

Adapun semua poin lain dari buku ini , para pengembang benar-benar mencari kinerja sebagai garis bawah. Beberapa mengandalkan cara eksternal (di luar aplikasi) untuk peningkatan kinerja seperti menggunakan disk cepat, mendapatkan lebih banyak CPU / Core, menyetel mesin penyimpanan, dan menyetel file konfigurasi. Orang lain akan bekerja keras dan menulis kode yang lebih baik. Beberapa mungkin menggunakan pengkodean semua intelijen bisnis dalam Prosedur yang Disimpan tetapi masih belum menerapkan dekomposisi gabungan (Lihat Apa argumen yang menentang atau untuk menempatkan logika aplikasi di lapisan basis data? Bersama dengan postingan lain). Semuanya tergantung pada budaya dan toleransi masing-masing toko pengembang.

Beberapa mungkin puas dengan kinerja dan tidak menyentuh kode lagi. Lainnya tidak menyadari ada manfaat besar yang bisa dipetik orang jika mereka mencoba bergabung komposisi.

Bagi para pengembang yang bersedia ...

COBALAH !!!

RolandoMySQLDBA
sumber
3
Adapun tautan tentang perubahan ke 3 pertanyaan ... Saya tahu dan menghormati Baron, Vadim, dan Peter, tapi saya tidak setuju dengan saran yang menyesatkan ini. Sebagian besar argumen yang mendukung perpecahan sangat jarang sehingga tidak layak disebutkan. Tetap dengan satu permintaan dengan GABUNG, lalu mari kita bekerja untuk memperbaikinya.
Rick James
2
@ RickJames Saya setuju dengan semangat komentar Anda. Selama bertahun-tahun, saya telah melihat pekerjaan dekomposisi bergabung untuk beberapa dan gagal untuk yang lain. Bahkan dengan skillset SQL yang tepat, itu bisa bekerja melawan Anda jika dekomposisi gabungan tidak dilakukan dengan benar. Di tempat kerja saya saat ini, banyak dept yang suka meningkatkan skala, terutama ketika kode warisan terlibat dan kantong yang dalam tersedia. Dengan mereka yang memiliki rasa kaviar tetapi anggaran salad telur, bergabung dengan dekomposisi bisa sepadan dengan risikonya tetapi harus dilakukan dengan benar.
RolandoMySQLDBA
Saya ingin melihat bagaimana ini bekerja di lingkungan Oracle jika saya punya hak dan waktu.
Rick Henderson
Satu cara lain bisa lebih cepat adalah bahwa jika Anda melakukan pemesanan, akan lebih sedikit perhitungan secara keseluruhan untuk memesan daftar yang lebih kecil daripada memesan satu daftar besar.
Evan Siroky
24

Dalam Postgres (dan mungkin RDBMS pada tingkat yang sama, MySQL pada tingkat yang lebih rendah), lebih sedikit kueri yang hampir selalu jauh lebih cepat.

Overhead dari penguraian dan perencanaan beberapa kueri sudah lebih dari keuntungan yang mungkin dalam kebanyakan kasus.

Belum lagi pekerjaan tambahan yang harus dilakukan di klien, menggabungkan hasilnya, yang biasanya jauh lebih lambat. RDBMS berspesialisasi dalam tugas dan operasi semacam itu didasarkan pada tipe data asli. Tidak ada casting ke textdan kembali untuk hasil antara atau mengubah ke jenis asli klien, yang bahkan dapat menyebabkan hasil yang kurang benar (atau salah!). Pikirkan angka floating point ...

Anda juga mentransfer lebih banyak data antara server DB dan klien. Ini mungkin diabaikan untuk tangan yang penuh dengan nilai, atau membuat perbedaan besar.

Jika beberapa kueri berarti beberapa round trip ke server database, Anda juga mengumpulkan beberapa kali latensi jaringan dan overhead transaksi, bahkan mungkin koneksi overhead. Kerugian besar.

Tergantung pada pengaturan Anda, latensi jaringan saja dapat memakan waktu lebih lama dari yang lainnya dengan perintah besarnya.

Pertanyaan terkait pada SO:

Mungkin ada titik balik untuk kueri berjalan sangat besar dan panjang karena transaksi mengumpulkan kunci pada baris DB di jalan. Kueri yang sangat besar dapat menahan banyak kunci untuk periode waktu yang lama yang dapat menyebabkan gesekan dengan permintaan bersamaan .

Erwin Brandstetter
sumber
Hanya karena penasaran, apa yang Anda anggap sangat besar ?
Sablefoste
@Sablefoste: Sangat tergantung pada pola akses Anda. Poin penting adalah di mana transaksi bersamaan mulai mengantri, menunggu kunci dirilis. Atau jika Anda mengumpulkan cukup kunci untuk memakan sebagian besar sumber daya Anda. Atau jika pertanyaan Anda berjalan cukup lama untuk mengganggu autovacuum ...
Erwin Brandstetter
Tetapi jika kita mengambil situasi yang agak khas - kueri yang menggunakan gabungan luar dan mengembalikan banyak data berlebihan untuk tabel "induk", yang kemudian harus diuraikan dan disortir oleh aplikasi (kemungkinan besar, beberapa perpustakaan ORM) versus pilih kecil yang mengambil semua ID yang diperlukan terlebih dahulu lalu pilih yang lebih kecil dengan IN () dan bukan gabungan luar? Bukankah pendekatan kedua akan lebih efisien (mengingat DB dan aplikasi menggunakan CPU dan bandwidth komunikasi)?
JustAMartin
1
@JustAMartin: Kedengarannya seperti jenis kueri yang hampir pasti lebih cepat ketika ditangani oleh perencana kueri dari RDBMS - dengan asumsi permintaan yang benar. Mengenai returns lots of redundant data for "parent" table: Mengapa Anda mengembalikan data yang berlebihan? Hanya kembalikan data yang Anda butuhkan.
Erwin Brandstetter
1
Dengan gabungan luar, RDBMS mengembalikan data dari tabel induk yang diduplikasi untuk setiap anak yang bergabung, yang berarti beberapa overhead jaringan & memori, dan kemudian beberapa penguraian tambahan dalam alat ORM untuk membuang nilai-nilai induk duplikat dan menjaga hanya satu induk dengan n anak. Jadi, dengan satu kueri, kami menghemat pekerjaan efisien perencana kueri RDBMS, lebih sedikit permintaan jaringan (atau pipa lokal) tetapi kehilangan muatan tambahan yang tidak dibutuhkan dan memindahkan data di perpustakaan ORM. Saya kira, itu seperti biasa - mengukur sebelum mengoptimalkan.
JustAMartin