Kapan saya harus menggunakan indeks komposit?

133
  1. Kapan saya harus menggunakan indeks komposit dalam database?
  2. Apa percabangan kinerja dengan menggunakan indeks komposit)?
  3. Mengapa saya harus menggunakan indeks komposit?

Misalnya, saya punya homestabel:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  PRIMARY KEY  (`home_id`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
) ENGINE=InnoDB  ;

Apakah masuk akal bagi saya untuk menggunakan indeks komposit untuk keduanya geolatdan geolng, sehingga:

Saya ganti:

  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),

dengan:

KEY `geolat_geolng` (`geolat`, `geolng`)

Jika begitu:

  • Mengapa?
  • Apa percabangan kinerja dengan menggunakan indeks komposit)?

MEMPERBARUI:

Karena banyak orang telah menyatakan itu sepenuhnya tergantung pada kueri yang saya lakukan, di bawah ini adalah permintaan yang paling umum dilakukan:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

PEMBARUAN 2:

Dengan skema database berikut:

CREATE TABLE IF NOT EXISTS `homes` (
  `home_id` int(10) unsigned NOT NULL auto_increment,
  `primary_photo_group_id` int(10) unsigned NOT NULL default '0',
  `customer_id` bigint(20) unsigned NOT NULL,
  `account_type_id` int(11) NOT NULL,
  `address` varchar(128) collate utf8_unicode_ci NOT NULL,
  `city` varchar(64) collate utf8_unicode_ci NOT NULL,
  `state` varchar(2) collate utf8_unicode_ci NOT NULL,
  `zip` mediumint(8) unsigned NOT NULL,
  `price` mediumint(8) unsigned NOT NULL,
  `sqft` smallint(5) unsigned NOT NULL,
  `year_built` smallint(5) unsigned NOT NULL,
  `num_of_beds` tinyint(3) unsigned NOT NULL,
  `num_of_baths` decimal(3,1) unsigned NOT NULL,
  `num_of_floors` tinyint(3) unsigned NOT NULL,
  `description` text collate utf8_unicode_ci,
  `geolat` decimal(10,6) default NULL,
  `geolng` decimal(10,6) default NULL,
  `display_status` tinyint(1) NOT NULL,
  `date_listed` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `contact_email` varchar(100) collate utf8_unicode_ci NOT NULL,
  `contact_phone_number` varchar(15) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`home_id`),
  KEY `customer_id` (`customer_id`),
  KEY `city` (`city`),
  KEY `num_of_beds` (`num_of_beds`),
  KEY `num_of_baths` (`num_of_baths`),
  KEY `geolat` (`geolat`),
  KEY `geolng` (`geolng`),
  KEY `account_type_id` (`account_type_id`),
  KEY `display_status` (`display_status`),
  KEY `sqft` (`sqft`),
  KEY `price` (`price`),
  KEY `primary_photo_group_id` (`primary_photo_group_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=8 ;

Menggunakan SQL berikut:

EXPLAIN SELECT  homes.home_id,
                    address,
                    city,
                    state,
                    zip,
                    price,
                    sqft,
                    year_built,
                    account_type_id,
                    num_of_beds,
                    num_of_baths,
                    geolat,
                    geolng,
                    photo_id,
                    photo_url_dir
            FROM homes
            LEFT OUTER JOIN home_photos ON homes.home_id = home_photos.home_id
                AND homes.primary_photo_group_id = home_photos.home_photo_group_id
                AND home_photos.home_photo_type_id = 2
            WHERE homes.display_status = true
            AND homes.geolat BETWEEN -100 AND 100
            AND homes.geolng BETWEEN -100 AND 100

EXPLAIN mengembalikan:

id  select_type  table        type  possible_keys                                    key                  key_len  ref     rows  Extra
----------------------------------------------------------------------------------------------------------
1   SIMPLE       homes        ref   geolat,geolng,display_status                     display_status       1        const   2     Using where
1  SIMPLE        home_photos  ref   home_id,home_photo_type_id,home_photo_group_id   home_photo_group_id  4        homes.primary_photo_group_id   4  

Saya tidak begitu mengerti cara membaca perintah EXPLAIN. Apakah ini terlihat baik atau buruk. Saat ini, saya TIDAK menggunakan indeks komposit untuk geolat dan geolng. Haruskah saya menjadi seperti itu?

Teddy
sumber

Jawaban:

111

Anda harus menggunakan indeks komposit saat Anda menggunakan kueri yang mendapat manfaat darinya. Indeks komposit yang terlihat seperti ini:

index( column_A, column_B, column_C )

akan mendapat manfaat kueri yang menggunakan bidang itu untuk bergabung, memfilter, dan terkadang memilih. Ini juga akan menguntungkan kueri yang menggunakan subset kolom paling kiri di komposit itu. Jadi indeks di atas juga akan memenuhi permintaan yang perlu

index( column_A, column_B, column_C )
index( column_A, column_B )
index( column_A )

Tetapi itu tidak akan (setidaknya tidak secara langsung, mungkin dapat membantu sebagian jika tidak ada indeks yang lebih baik) membantu untuk pertanyaan yang membutuhkan

index( column_A, column_C )

Perhatikan bagaimana column_B hilang.

Dalam contoh asli Anda, indeks gabungan untuk dua dimensi sebagian besar akan menguntungkan kueri yang meminta pada kedua dimensi atau dimensi paling kiri dengan sendirinya, tetapi bukan dimensi paling kanan dengan sendirinya. Jika Anda selalu bertanya dua dimensi, indeks komposit adalah cara untuk melakukannya, tidak masalah yang mana yang lebih dulu (paling mungkin).

Mark Canlas
sumber
1
Mark, saya telah memperbarui posting asli saya (pembaruan 2). Ini adalah permintaan saya yang sebenarnya. Skema db saya yang sebenarnya. Dan apa perintah EXPLAIN kembali. Jadi, dengan informasi ini - saya harus menggunakan indeks komposit. Saya masih belum jelas. Terima kasih sebelumnya.
Teddy
Mark, apakah indeks gabungan dalam jawaban Anda memenuhi indeks (kolom_C)?
Boris D. Teoharov
Saya tidak yakin saya mengerti pertanyaan Anda. Tetapi, jika Anda bertanya apakah indeks (A, B, C) akan membantu kueri yang memfilter pada kolom C, jawabannya biasanya tidak, itu tidak akan menggunakan indeks untuk memfilter. Namun bisa menggunakan indeks untuk menghilangkan pemindaian tabel jika Anda hanya memilih pada subset dari ABC. Jadi, itu berbeda, tetapi terkait. Tetapi untuk penggunaan indeks yang umum untuk memungkinkan penyaringan, jawabannya adalah tidak.
Mark Canlas
1
-1 karena indeks komposit tidak membantu WHERE geolat BETWEEN ??? AND ??? AND geolng BETWEEN ??? AND ???. Ini akan berhenti setelah bidang pertama. Jawaban dari "Question Overflow" menjelaskan alasannya.
Rick James
1
@felwithe MySQL hanya dapat menggunakan satu indeks per masing-masing tabel dalam kueri (Ada pengecualian, mis. Penggabungan indeks). Yang idealnya berarti sebuah tabel dalam kueri, harus menggunakan indeks tunggal untuk semua di mana-klausa, tabel bergabung, grup-oleh dan urutan-oleh. Jadi indeks terpisah pada setiap kolom mungkin tidak selalu berfungsi tetapi indeks komposit dapat melakukan keajaiban.
AKHIL MATHEW
56

Bayangkan Anda memiliki tiga pertanyaan berikut:

Kueri I:

SELECT * FROM homes WHERE `geolat`=42.9 AND `geolng`=36.4

Kueri II:

SELECT * FROM homes WHERE `geolat`=42.9

Kueri III:

SELECT * FROM homes WHERE `geolng`=36.4

Jika Anda memiliki indeks terpisah per kolom, ketiga kueri menggunakan indeks. Di MySQL, jika Anda memiliki indeks komposit ( geolat, geolng), hanya kueri I dan kueri II (yang menggunakan bagian pertama dari indeks komposit) menggunakan indeks. Dalam kasus ini, permintaan III membutuhkan pencarian tabel penuh.

Pada bagian Multiple-Column Indexes manual, secara jelas dijelaskan bagaimana beberapa indeks kolom bekerja, jadi saya tidak ingin mengetik ulang manual.

Dari halaman Manual Referensi MySQL :

Indeks multi-kolom dapat dianggap sebagai array yang diurutkan yang berisi nilai-nilai yang dibuat dengan menggabungkan nilai-nilai kolom yang diindeks .

Jika Anda menggunakan indeks terpisah untuk kolom geolat dan geolng, Anda memiliki dua indeks berbeda di tabel yang bisa Anda cari sendiri.

INDEX geolat
-----------
VALUE RRN
36.4  1
36.4  8
36.6  2
37.8  3
37.8  12
41.4  4

INDEX geolng
-----------
VALUE RRN
26.1  1
26.1  8
29.6  2
29.6  3
30.1  12
34.7  4

Jika Anda menggunakan indeks komposit, Anda hanya memiliki satu indeks untuk kedua kolom:

INDEX (geolat, geolng)
-----------
VALUE      RRN
36.4,26.1  1
36.4,26.1  8
36.6,29.6  2
37.8,29.6  3
37.8,30.1  12
41.4,34.7  4

RRN adalah nomor rekaman relatif (untuk menyederhanakan, Anda bisa mengatakan ID). Dua indeks pertama dihasilkan secara terpisah dan indeks ketiga adalah komposit. Seperti yang Anda lihat, Anda dapat mencari berdasarkan geolng pada komposit karena diindeks oleh geolat, namun dimungkinkan untuk mencari berdasarkan geolat atau "geolat AND geolng" (karena geolng adalah indeks level kedua).

Juga, lihat bagian Bagaimana Cara Menggunakan Indeks MySQL .

Emre Yazici
sumber
1
Sebenarnya, saya tidak punya pertanyaan itu. Permintaan saya tercantum dalam posting asli. Permintaan saya adalah mengembalikan rumah dalam kotak persegi. Saya tahu spasial dan saya tidak mencoba menghitung jarak. Saya hanya ingin tahu apakah menggunakan indeks komposit masuk akal ketika saya mencoba untuk menampilkan semua rumah dalam geo grid tertentu (misalnya lingkungan / kota / kabupaten)
Teddy
Eyazici, saya telah memperbarui posting asli saya (pembaruan 2). Ini adalah permintaan saya yang sebenarnya. Skema db saya yang sebenarnya. Dan apa perintah EXPLAIN kembali. Jadi, dengan informasi ini - saya harus menggunakan indeks komposit. Saya masih belum jelas. Terima kasih sebelumnya
Teddy
@ "Sebenarnya, saya tidak punya pertanyaan itu.". Sebenarnya sudah, saya telah menggunakan kondisi WHERE sederhana untuk menjelaskan logika dasar. Saat menggunakan conditional (mis. WHERE) pada kolom, MySQL mencoba menggunakan indeks kapan pun memungkinkan. "x ANTARA AND AND" mirip dengan "x> AND AND <<b". Anda telah menggunakan kolom geolng dan geolat di persyaratan kueri Anda. Jika Anda menggunakan indeks komposit "(geolat, geolng)" Anda "DAN geolng ANTARA ??? DAN ???" bersyarat tidak mendapatkan keuntungan dari indeks (ini untuk MySQL). Jadi, Anda harus menggunakan indeks per kolom terpisah untuk skenario Anda.
Emre Yazici
Saya tidak mengerti. Mengapa saya harus menggunakan indeks terpisah untuk geolat dan geolng ketika saya akan SELALU melakukan kueri yang mencakup kedua kolom
Teddy
1
Tidak. Ketika "kisaran" dijumpai (seperti dengan BETWEEN), tidak ada bidang indeks lebih lanjut yang dipertimbangkan! Jadi indeks komposit tidak lebih baik.
Rick James
19

Mungkin ada kesalahpahaman tentang apa yang dilakukan indeks komposit. Banyak orang berpikir bahwa indeks komposit dapat digunakan untuk mengoptimalkan permintaan pencarian selamawhere klausa tersebut mencakup kolom yang diindeks, dalam kasus Anda geolatdan geolng. Mari kita selami lebih dalam:

Saya percaya data Anda pada koordinat rumah adalah desimal acak seperti itu:

home_id  geolat  geolng
   1    20.1243  50.4521
   2    22.6456  51.1564
   3    13.5464  45.4562
   4    55.5642 166.5756
   5    24.2624  27.4564
   6    62.1564  24.2542
...

Karena geolatdan geolngnilai sulit terulang. Indeks komposit menyala geolatdan geolngakan terlihat seperti ini:

index_id  geolat  geolng
   1     20.1243  50.4521
   2     20.1244  61.1564
   3     20.1251  55.4562
   4     20.1293  66.5756
   5     20.1302  57.4564
   6     20.1311  54.2542
...

Oleh karena itu kolom kedua dari indeks komposit pada dasarnya tidak berguna ! Kecepatan kueri Anda dengan indeks komposit mungkin akan mirip dengan indeks hanya pada geolatkolom.

Seperti yang disebutkan oleh Will, MySQL menyediakan dukungan ekstensi spasial . Titik spasial disimpan dalam satu kolom, bukan dua lat lngkolom terpisah . Indeks spasial dapat diterapkan ke kolom seperti itu. Namun, efisiensinya bisa dibesar-besarkan berdasarkan pengalaman pribadi saya. Bisa jadi indeks spasial tidak menyelesaikan masalah dua dimensi tetapi hanya mempercepat pencarian menggunakan R-Trees dengan pemisahan kuadrat .

Yang menarik adalah titik spasial menghabiskan lebih banyak memori karena menggunakan angka presisi ganda delapan byte untuk menyimpan koordinat. Koreksi saya jika saya salah.

Pertanyaan Melimpah
sumber
5

Indeks komposit sangat kuat karena:

  • Menegakkan integritas struktur
  • Aktifkan pengurutan pada id yang difilter

LAKUKAN INTEGRITAS STRUKTUR

Indeks komposit bukan hanya tipe indeks lainnya; mereka dapat menyediakan struktur PERLU untuk tabel dengan menegakkan integritas sebagai Kunci Utama.

Innodb Mysql mendukung pengelompokan dan contoh berikut menggambarkan mengapa indeks komposit mungkin diperlukan.

Untuk membuat teman tabel (yaitu untuk jaringan sosial) kita perlu 2 kolom: user_id, friend_id.

Tabel Strcture

user_id (medium_int)
friend_id (medium_int)

Primary Key -> (user_id, friend_id)

Berdasarkan kunci utama (PK) adalah unik dan dengan membuat PK komposit, Innodb akan secara otomatis memeriksa bahwa tidak ada duplikat user_id, friend_idsaat ada catatan baru ditambahkan. Ini adalah perilaku yang diharapkan karena tidak ada pengguna yang memiliki lebih dari 1 catatan (hubungan hubungan) friend_id = 2misalnya.

Tanpa PK komposit, kami dapat membuat skema ini menggunakan kunci pengganti:

user_friend_id
user_id
friend_id

Primary Key -> (user_friend_id)

Sekarang, setiap kali catatan baru ditambahkan, kami harus memeriksa bahwa catatan sebelumnya dengan kombinasi user_id, friend_idtersebut belum ada.

Dengan demikian, indeks komposit dapat menegakkan integritas struktur.

Aktifkan penyortiran pada ID yang difilter

Sangat umum untuk mengurutkan satu set catatan berdasarkan waktu posting (timestamp atau datetime). Biasanya, ini berarti memposting pada id yang diberikan. Berikut ini sebuah contoh

Tabel User_Wall_Posts (pikirkan jika posting dinding Facebook)

user_id (medium_int)
timestamp (timestamp)
author_id (medium_int)
comment_post (text)

Primary Key -> (user_id, timestamp, author_id)

Kami ingin meminta dan menemukan semua posting untuk user_id = 10dan mengurutkan posting komentar berdasarkan timestamp(tanggal).

SQL QUERY

SELECT * FROM User_Wall_Posts WHERE user_id = 10 ORDER BY timestamp DES

PK komposit memungkinkan Mysql untuk memfilter dan mengurutkan hasil menggunakan indeks; Mysql tidak harus menggunakan file sementara atau filesort untuk mengambil hasilnya. Tanpa kunci komposit, ini tidak akan mungkin dan akan menyebabkan permintaan yang sangat tidak efisien.

Dengan demikian, kunci komposit sangat kuat dan cocok lebih dari masalah sederhana "Saya ingin mencari column_a, column_bjadi saya akan menggunakan kunci komposit. Untuk skema database saya saat ini, saya memiliki banyak kunci komposit sebagai kunci tunggal. Jangan mengabaikan Penggunaan kunci komposit!

ProfilTwist
sumber
5

Indeks komposit berguna untuk

  • 0 atau lebih "=" klausa, plus
  • paling banyak satu rentang klausa.

Indeks komposit tidak dapat menangani dua rentang. Saya membahas hal ini lebih lanjut dalam buku resep saya .

Cari terdekat - Jika pertanyaannya adalah benar-benar tentang optimalisasi

WHERE geolat BETWEEN ??? AND ???
  AND geolng BETWEEN ??? AND ???

maka tidak ada indeks yang benar-benar dapat menangani kedua dimensi.

Sebaliknya, seseorang harus 'berpikir di luar kotak'. Jika satu dimensi diimplementasikan melalui partisi dan yang lain diimplementasikan dengan memilihnya dengan hati-hati PRIMARY KEY, yang satu bisa mendapatkan efisiensi yang jauh lebih baik untuk tabel lat / lng lookup yang sangat besar. Blog latlng saya membahas detail tentang bagaimana menerapkan "find terdekat" di dunia. Ini termasuk kode.

The PARTITIONsgaris-garis rentang lintang. Yang PRIMARY KEYsengaja dimulai dengan garis bujur sehingga baris yang berguna cenderung berada di blok yang sama. Stored Rutin mengatur kode berantakan untuk melakukan order by... limit...dan untuk menumbuhkan 'kotak' di sekitar target sampai Anda memiliki cukup kedai kopi (atau apa pun). Ini juga menangani perhitungan lingkaran besar dan menangani garis data dan kutub.

Lebih

Saya telah menulis blog lain; itu membandingkan 5 cara melakukan pencarian lat / lng: http://mysql.rjweb.org/doc.php/latlng#representation_choices (Ini merujuk tautan yang diberikan di atas sebagai salah satu dari 5.) Salah satu cara lain adalah ini, dan itu menunjukkan bahwa mereka optimal untuk kasus tertentu :

INDEX(geolat, geolng),
INDEX(geolng, geolat)

Artinya, memiliki kedua kolom dalam dua indeks, dan tidak memiliki indeks satu kolom pada geolat dan geolng adalah penting.

Rick James
sumber
1

Tidak ada Hitam dan Putih, satu ukuran cocok untuk semua jawaban.

Anda harus menggunakan indeks komposit, ketika beban pekerjaan kueri Anda akan mendapat manfaat dari satu.

Anda perlu membuat profil beban pekerjaan kueri Anda untuk menentukan ini.

Indeks komposit berperan ketika kueri dapat dipenuhi sepenuhnya dari indeks itu.

UPDATE (sebagai tanggapan untuk mengedit pertanyaan diposting): Jika Anda memilih * dari tabel indeks komposit dapat digunakan, mungkin tidak. Anda harus menjalankan EXPLAIN PLAN untuk memastikan.

Mitch Wheat
sumber
Apakah masuk akal untuk menggunakan indeks komposit untuk data lokasi geografis (lintang & bujur)?
Teddy
1
Itu sepenuhnya tergantung pada pertanyaan apa yang dibuat terhadap tabel itu.
Mitch Wheat
Saya telah memperbarui posting asli saya untuk memasukkan permintaan paling umum yang dilakukan. Lihat di atas.
Teddy
1

Untuk melakukan pencarian spasial, Anda memerlukan algoritma R-Tree , yang memungkinkan pencarian area geografis dengan sangat cepat. Apa yang Anda butuhkan untuk pekerjaan ini.

Beberapa basis data memiliki indeks spasial. Pencarian Google yang cepat menunjukkan bahwa MySQL 5 memilikinya (yang melihat SQL Anda, saya rasa Anda menggunakan MySQL).

Akan
sumber
1

Indeks komposit dapat berguna ketika Anda ingin mengoptimalkan group byklausa (lihat artikel ini http://dev.mysql.com/doc/refman/5.0/id/group-by-optimization.html ). Mohon perhatian:

Prasyarat yang paling penting untuk menggunakan indeks untuk GROUP BY adalah bahwa semua atribut GROUP BY kolom referensi dari indeks yang sama, dan bahwa indeks menyimpan kunci-nya secara berurutan (misalnya, ini adalah indeks BTREE dan bukan indeks HASH)

Alexander
sumber
GROUP BYtidak disebutkan.
Rick James
Tidak disebutkan di mana? :) Jelas disebutkan dalam artikel yang saya referensikan. Dan itu menjawab pertanyaan yang diajukan: Kapan saya harus menggunakan indeks komposit dalam database? Apa percabangan kinerja dengan menggunakan indeks komposit)? Mengapa saya harus menggunakan indeks komposit?
Alexander
Koreksi: GROUP BYtidak disebutkan oleh OP.
Rick James
Tentu, itulah jawabannya - salah satu kasus ketika kita akan menggunakan indeks komposit dalam database.
Alexander
0

Saya dengan @Mitch, sepenuhnya bergantung pada pertanyaan Anda. Untungnya, Anda dapat membuat dan menjatuhkan indeks kapan saja, dan Anda dapat menambahkan kata kunci EXPLAIN ke pertanyaan Anda untuk melihat apakah penganalisa permintaan menggunakan indeks.

Jika Anda akan mencari pasangan lat / long yang tepat indeks ini kemungkinan akan masuk akal. Tetapi Anda mungkin akan mencari rumah dalam jarak tertentu dari suatu tempat tertentu, sehingga pertanyaan Anda akan terlihat seperti ini (lihat sumber ):

select *, sqrt(  pow(h2.geolat - h1.geolat,  2) 
               + pow(h2.geolng - h1.geolng, 2) ) as distance
from homes h1, homes h2
where h1.home_id = 12345 and h2.home_id != h1.home_id
order by distance

dan indeks sangat mungkin tidak akan membantu sama sekali. Untuk pertanyaan geospasial, Anda memerlukan sesuatu seperti ini .

Perbarui: dengan permintaan ini:

SELECT * FROM homes
WHERE geolat BETWEEN ??? AND ???
AND geolng BETWEEN ??? AND ???

Penganalisis kueri dapat menggunakan indeks pada geolat saja, atau indeks pada geolng saja, atau mungkin kedua indeks. Saya tidak berpikir itu akan menggunakan indeks komposit. Tetapi mudah untuk mencoba masing-masing permutasi ini pada set data nyata dan kemudian (a) melihat apa yang EXPLAIN katakan kepada Anda dan (b) mengukur waktu kueri yang benar-benar diperlukan.

Jim Ferrans
sumber
Saya hanya menggunakan keinginan untuk mengembalikan rumah dalam kotak persegi. Saya tahu spasial, jadi saya tidak mencoba menghitung jarak. Saya hanya ingin mengembalikan rumah di dalam kotak persegi dan ingin itu bekerja dengan cepat. Karena itu, saya ingin memastikan saya memiliki pengaturan indeks dengan benar. Apakah itu membantu?
Teddy