Bagaimana saya bisa memaksa MySQL ke IGNORE ALL indexes?


12

Saya telah membaca artikel tentang FORCEindeks, tetapi bagaimana saya bisa memaksa MySQL ke IGNORE ALLindeks?

Saya mencoba SELECT * FROM tbl IGNORE INDEX(*), tetapi saya tidak berhasil.

Adapun mengapa saya (dan lainnya) perlu melakukan ini: Misalnya, saya perlu merangkum statistik referensi dengan tld seperti ini:

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
    IGNORE INDEX(domain_name)
GROUP BY tld
ORDER BY c desc
LIMIT 100

... tapi saya selalu harus melihat indeks apa yang didefinisikan atau menentukan indeks apa yang akan digunakan melalui Jelaskan. Akan sangat berguna untuk sekadar menulis IGNORE INDEX ALLdan tidak peduli.

Adakah yang tahu sintaks atau hack? (Puluhan baris melalui tabel definisi MySQL benar-benar bukan jalan pintas).

Ditambahkan dari diskusi obrolan :

Bechmark:

  • tidak ada indeks = 148,5 detik

  • dengan indeks = 180 detik dan masih berjalan dengan Mengirim data Array SSD sangat kuat, sehingga Anda hampir tidak peduli dengan cache data ...

Definisi untuk tolok ukur:

CREATE TABLE IF NOT EXISTS `domains_import` (
`domain_id` bigint(20) unsigned NOT NULL,
`domain_name` varchar(253) CHARACTER SET ascii COLLATE ascii_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `domains_import`
ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

ALTER TABLE `domains_import`
MODIFY `domain_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT;

InnoDB, tes dengan indeks (tanpa USE INDEX () atau serupa) masih berjalan, 250 detik, saya baru saja membunuhnya.

Jawaban:


24

Sama sekali tidak jelas mengapa Anda menginginkan ini, tetapi Anda dapat menggunakan petunjuk USE INDEX ()untuk memberi tahu pengoptimal untuk tidak menggunakan indeks apa pun. Dari MySQL docs: petunjuk indeks

Hal ini sintaksis yang valid untuk menghilangkan index_listuntukUSE INDEX , yang berarti “tidak menggunakan indeks.” Menghilangkan index_list untuk FORCE INDEXatau IGNORE INDEXmerupakan kesalahan sintaksis.

Kueri Anda menjadi:

SELECT count(*) AS c, 
       substring_index(domain_name, '.', -1) AS tld
FROM domains_import 
       USE INDEX ()        -- use no indexes
GROUP BY tld
ORDER BY c DESC
LIMIT 100 ;

Catatan samping: ekspresi kompleks:

SUBSTRING(domain_name, LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2) 

dapat disederhanakan dari 4 panggilan fungsi menjadi 1:

SUBSTRING_INDEX(domain_name, '.', -1)

1
Itu berguna bagi saya ketika pengoptimal MySQL 5.7.10 mengubah rencana kueri untuk yang terburuk ketika menghapus beberapa yang LEFT JOINsaya miliki. `USE INDEX ()` membuat MySQL melakukan pemindaian tabel pada tabel 20K dan 1-ke-1 JOINalih-alih melintasi 500 baris di antara dua indeks. Mendapat 20x lebih cepat.
Xenos

2

Anda juga bisa menanamkan WHERE 1=1

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE 1=1
GROUP BY tld
ORDER BY c desc
LIMIT 100

ypercube baru saja bertanya padaku

Rolando, apakah pengoptimal MySQL begitu bodoh sehingga kondisi sederhana selalu benar akan melarang penggunaan indeks?

Ya, tetapi Anda telah memberikan MySQL permintaan yang benar-benar bodoh. 1=1akan kembali ke Indeks Clustered. Meskipun demikian, masih ada cara lain, tetapi harus sedikit berbahaya bagi Pengoptimal.

SELECT 
    count(*) as c, 
    SUBSTRING
    (
        domain_name, 
        LENGTH(domain_name) - LOCATE('.', REVERSE(domain_name)) + 2
    ) as tld
FROM `domains_import` 
WHERE domain_name = domain_name
GROUP BY tld
ORDER BY c desc
LIMIT 100

Ini akan membuang setiap indeks di bawah bus pasti karena nilai setiap baris untuk domain_namebanyak diperiksa. Jika domain_namediindeks, Anda harus memilih kolom untuk WHERE column_name=column_nameyang tidak diindeks sama sekali.

Saya baru saja mencoba ini di atas meja besar di server Pementasan

mysql > explain SELECT COUNT(1) FROM VIDEO WHERE EMBEDDED_FLG=EMBEDDED_FLG;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | VIDEO | ALL  | NULL          | NULL | NULL    | NULL | 354327 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

Tidak ada indeks yang dipilih


Rolando, apakah pengoptimal MySQL begitu bodoh sehingga kondisi sederhana selalu benar akan melarang penggunaan indeks?
ypercubeᵀᴹ

@ ypercube ya, tapi Anda harus cukup menurunkan permintaan agar hal itu terjadi.
RolandoMySQLDBA

1
Hei, aku membenarkan sendiri jawaban yercube. Jawaban saya hanyalah cara lain dan menjelaskan celah Pengoptimal.
RolandoMySQLDBA

1
Rolando, tidak benar: Indeks akan digunakan: SQLfiddle . Bahkan jika Anda membuat sesuatu yang lebih rumit, seperti WHERE id+0 = id*1indeks akan tetap digunakan, dan tambahan Using whereakan muncul.
ypercubeᵀᴹ

4
@ PaulWhite itu akan. (Ini bodoh tapi tidak sebodoh itu;) Dan itu mungkin mengapa permintaan Roalndo tidak menggunakan indeks, kolom harus didefinisikan sebagai NULL.
ypercubeᵀᴹ

0

Dengan asumsi Anda memiliki dua indeks ini:

ADD PRIMARY KEY (`domain_id`),
ADD UNIQUE KEY `domain_name` (`domain_name`);

Maka tidak masalah apa yang dilakukan pengoptimal; itu harus memindai pada dasarnya jumlah barang yang identik.

Kasus 1: Ini melakukan pemindaian tabel (atau menggunakan domain_id): Ini akan memindai pasangan (id, nama), menemukan semua nama, melakukan SUBSTRING..LOKASI, GROUP BY, dan akhirnya ORDER BY. GROUP BY dan ORDER BY mungkin masing-masing membutuhkan tabel dan file tmp. Periksa EXPLAIN SELECT ...apakah itu benar.

Kasus 2: Itu melakukan pemindaian indeks (dari domain_name): Indeks itu sebenarnya mengandung pasangan (nama, id) - karena InnoDB secara implisit menempatkan PK di akhir setiap kunci sekunder. Sisa dari paralel pemrosesan Kasus 1.

Satu hal bisa berbeda - ukuran dua BTrees. Lakukan SHOW TABLE STATUS LIKE domains_importuntuk melihat Data_length (untuk Kasus 1) dan Index_length (untuk Kasus 2). BTree yang lebih besar akan lebih lambat.

Hal lain dapat berbeda - caching. Apa nilainya innodb_buffer_pool_size? Berapa banyak RAM yang Anda miliki? Bisakah Data (atau Indeks) terkandung dalam buffer pool. (Atau 37%, karena ini adalah pemindaian tabel / indeks?) Jika cocok, maka jalankan kueri dua kali. Waktu kedua akan sekitar 10 kali lebih cepat karena tidak memukul disk (caching).

Jika ini adalah tugas satu kali, SSD akan membantu. Jika tidak, dan Anda dapat men-cache seluruh tabel, maka itu tidak akan membantu setelah buffer_pool dimuat.

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.