Apakah menggunakan LIMIT meningkatkan kinerja dan apakah itu terlihat?


11

Saya ingin memahami yang berikut ini.
Asumsikan bahwa saya memiliki kueri yang rumit dengan katakanlah gabungan 5 tabel grup dengan penjumlahan dan pesanan oleh.
Mengesampingkan semua optimasi untuk kueri itu sendiri, misalnya indeks, dll.
Apakah ada manfaat kinerja yang signifikan digunakan LIMIT? Saya berasumsi bahwa semua permintaan (dan hasil) harus diproses sebelum LIMIT diterapkan, jadi menggunakan LIMIT untuk mengambil bagian dari hasil, apakah ini menawarkan perbaikan yang signifikan / nyata?


2
Saya sarankan Anda membaca ini, untuk kasus-kasus yang LIMITmeningkatkan efisiensi: Mengoptimalkan Pertanyaan LIMIT
ypercubeᵀᴹ

Jawaban:


10

Jika Anda ingin memanfaatkan LIMITuntuk meningkatkan kinerja, Anda perlu

  • memahami data yang Anda ambil
  • pengindeksan yang tepat urutan kolom yang benar
  • bertanggung jawab untuk refactoring permintaan
  • gunakan LIMITsebelumnyaJOIN

Prinsip-prinsip ini bisa sangat bermanfaat jika Anda dapat mengaturnya.

Saya mempelajari konsep-konsep ini dengan menonton Video YouTube ini (dengarkan baik-baik melalui aksen Prancis)

Saya menggunakan konsep-konsep itu untuk menjawab pertanyaan StackOverflow yang sangat sulit tentang mendapatkan 40 artikel teratas dari beberapa tabel: 12 Mei 2011: Mengambil Satu Baris dari Bergabung dengan Tabel .

Dalam jawaban saya untuk pertanyaan itu (16 Mei 2011) , saya menulis pertanyaan berikut dan mengujinya secara menyeluruh:

SELECT
  AAA.author_id,
  AAA.date_created,
  IFNULL(BBB.title,'<NO_TITLE>') title,
  IFNULL(CCC.filename,'<NO-IMAGE>') filename,
  IFNULL(CCC.date_added,'<NO-IMAGE-DATE>') image_date
FROM
(
  SELECT
    AA.id,
    AA.date_added,
    BB.author_id,
    BB.date_created
  FROM
  (
    SELECT
      A.id,IFNULL(MAX(B.date_added),'1900-01-01 00:00:00') date_added
      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A
      LEFT JOIN article_images B ON A.id = B.article_id
      GROUP BY A.id
  ) AA
  INNER JOIN articles BB USING (id)
) AAA
LEFT JOIN article_contents BBB ON AAA.id=BBB.article_id
LEFT JOIN article_images CCC
ON (AAA.id=CCC.article_id AND AAA.date_added=CCC.date_added)
ORDER BY AAA.date_created DESC;

Harap perhatikan baris dalam kueri dengan LIMIT

      FROM (SELECT id FROM articles ORDER BY date_created DESC LIMIT 40) A

Subquery ini terkubur dalam tiga level. Ini memungkinkan saya untuk menggunakan 40 artikel terakhir LIMIT. Kemudian, saya melakukan GABUNGAN yang diperlukan setelah itu.

PELAJARAN YANG DIPELAJARI

  • Melakukan LIMITsubqueries di dalam mungkin tidak selalu menjadi jawaban karena kardinalitas indeks, konten data, dan ukuran set hasil dari LIMIT. Jika Anda memiliki semua "bebek berturut-turut" (Perhatikan empat prinsip untuk kueri Anda), Anda dapat memperoleh hasil yang sangat bagus.
  • Buat pertanyaan Anda sesederhana mungkin ketika dilakukan LIMITdengan mengumpulkan kunci saja.

Jadi (A [LEFT] JOIN B) LIMIT 100setara dengan (A LIMIT 100) [LEFT] JOIN (B LIMIT 100)? Di mana [LEFT] JOINberarti gabungan luar atau dalam
Jim

Lebih tepatnya (A LIMIT 100) [LEFT] JOIN B. Idenya adalah menggunakan LIMITuntuk menentukan ukuran hasil yang ditetapkan sedini mungkin. Saya juga menggunakan LEFT JOINbukan INNER JOINkarena LEFT JOINakan mempertahankan urutan tombol di sebelah kiri.
RolandoMySQLDBA

@ Jim Tidak, mereka tidak. Kadang-kadang, mereka seperti ini: (A LEFT JOIN B) GROUP BY A.pk LIMIT 100biasanya dapat ditulis ulang sebagai (A LIMIT 100) LEFT JOIN B GROUP BY A.pk(tidak ada INNER BERGABUNG di sini, dengan gabungan batin mereka tidak akan setara.) Contoh Rolando adalah kasus yang persis seperti itu.
ypercubeᵀᴹ

@ ypercube: Jadi dengan inner joins, tidak ada yang bisa dilakukan untuk mendapatkan manfaat dari LIMIT?
Jim

Saya merujuk pada strategi penulisan ulang yang digariskan oleh Rolando. Kueri dengan GABUNGAN dan BATAL mungkin bermanfaat juga. Atau tidak. Tergantung.
ypercubeᵀᴹ

2

Ketika sebuah query dieksekusi, ia pertama-tama akan diterjemahkan ke dalam sebuah rencana yang terdiri dari beberapa operator. Ada dua tipe dasar operator: Blocking dan Non-Blocking. Operator Non-Blocking mengambil baris (atau beberapa baris) dari anak atau anak-anaknya untuk setiap baris yang diminta darinya. Operator Blocking di sisi lain harus membaca dan memproses seluruh baris set semua anak-anaknya sebelum dapat menghasilkan output apa pun.

Sortir adalah Operator Pemblokiran pada umumnya. Jadi pilih dengan pesanan tidak mendapat banyak manfaat dari batas. Namun, ada RDBMS yang dapat memanfaatkan algoritma pengurutan yang membutuhkan lebih sedikit memori dan lebih cepat ketika klausa batas disediakan. Dalam kasus ini cukup untuk hanya menyimpan baris n saat ini dan memindahkannya dari memori karena baris sebelumnya datang. Itu bisa menjadi keuntungan kinerja yang signifikan. Namun, saya tidak 100% yakin bahwa MySQL memiliki kemampuan itu.

Either way, bahkan batas-semacam masih perlu memproses seluruh baris input yang ditetapkan sebelum dapat menghasilkan baris output pertama. Meskipun algoritma ini, jika diterapkan, dapat mempercepat pengurutan, jika sisa kueri adalah bagian yang paling mahal, total waktu eksekusi tidak akan meningkat secara signifikan karena batas yang disediakan.


Saya sedikit bingung dengan jawabannya. Anda menyebutkan tentang jenis tetapi kelompok berdasarkan jenis bukan? Jadi jika misalnya saya menghapus pesanan dan tetap dengan grup, apakah jawaban Anda masih berlaku? Atau diperlukan analisis yang berbeda?
Jim

Bergantung pada permintaan dan indeks yang ada, GROUP BYberpotensi menyebabkan rencana yang berjalan tidak mengandung operator pemblokiran.
Sebastian Meine

0

Dalam kasus saya, saya bisa mengatakan Ya , bahkan jika saya (masih) tidak mengerti mengapa.

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id;

(result set)

8 rows in set (**18.14 sec**)

Catat waktu: 18 detik. Permintaan yang sama dengan LIMIT besar:

SELECT g0_.id AS id_0, COUNT(a1_.id_tarifs) AS sclr_1 
FROM groupe_jardinerie g0_
INNER JOIN articles_tarifs a1_
  ON (a1_.groupe_jardinerie_id = g0_.id)
WHERE g0_.centrale_id = 511 
  AND a1_.date_fin_tarif >= '2018-01-29 10:46:35'
GROUP BY g0_.id
LIMIT 100000000000;

(exact same result set)

8 rows in set (**1.32 sec**)

Lebih dari sepuluh kali lebih cepat !!!

MENJELASKAN memberikan hasil yang sama untuk kedua permintaan.

+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
| id | select_type | table | partitions | type   | possible_keys                                     | key     | key_len | ref                          | rows   | filtered | Extra                                        |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+
|  1 | SIMPLE      | a1_   | NULL       | ALL    | IDX_438010BBC10784EF                              | NULL    | NULL    | NULL                         | 795135 |    33.33 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | g0_   | NULL       | eq_ref | PRIMARY,IDX_9CA5CF6758A1D71F,IDX_9CA5CF67670C757F | PRIMARY | 4       | phs.a1_.groupe_jardinerie_id |      1 |    50.00 | Using where                                  |
+----+-------------+-------+------------+--------+---------------------------------------------------+---------+---------+------------------------------+--------+----------+----------------------------------------------+

LIMIT harus mengganggu hanya untuk membatasi set hasil (yaitu, jika saya melakukan LIMIT 4, saya hanya mendapat 4 baris pertama dari set hasil di atas).


menakutkan, versi apa yang Anda gunakan dan dapatkah Anda membuat test case yang disederhanakan?
Evan Carroll

1
Jawaban Anda tidak membuktikan manfaat baru apa pun untuk LIMIT. Kueri pertama Anda berjalan dalam 18 detik memberikan hasil yang ditetapkan. Semua data dalam kueri ke-2 sudah di-cache di pool buffer InnoDB karena kueri pertama, Jadi tentu saja kueri ke-2 harus lebih cepat, Bahkan jika Anda me-restart mysql, jalankan kueri 1, restart mysql, dan jalankan ke-2 permintaan, Anda akan mendapatkan hasil yang sama. . Memiliki hasil yang lebih baik LIMIThanya dapat diperoleh dari melakukan: 1) LIMITsebelumnya JOIN, 2) LIMIT dalam urutan ASCatau DESC.
RolandoMySQLDBA

Terima kasih atas minat Anda. Membuat test case yang disederhanakan bisa jadi sulit.
Pierre-Olivier Vares
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.