2012-06-07 14 views
9

A causa di questa impostazione:Perché MySQL non utilizza gli indici nella clausola WHERE IF?

mysql> show global variables like '%indexes'; 
+-------------------------------+-------+ 
| Variable_name     | Value | 
+-------------------------------+-------+ 
| log_queries_not_using_indexes | ON | 
+-------------------------------+-------+ 

Le query lente log continuare a ricevere:

# Time: 120607 16:58:30 
# [email protected]: xbtit[xbtit] @ [123.30.53.244] 
# Query_time: 0 Lock_time: 0 Rows_sent: 1 Rows_examined: 16006 
SELECT * FROM xbtit_files WHERE IF(soha_id is null OR soha_id = '', info_hash, soha_id)='6d63dd4ab199190b531752067414d4d6e6568f90'; 

Cercando di spiegare questa query:

mysql> EXPLAIN SELECT * FROM xbtit_files WHERE IF(soha_id is null OR soha_id = '', info_hash, soha_id)='6d63dd4ab199190b531752067414d4d6e6568f90'; 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 
| 1 | SIMPLE  | xbtit_files | ALL | NULL   | NULL | NULL | NULL | 16006 | Using where | 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 

Ciò che mi ha sorpreso è il motivo per MySQL non utilizza gli indici :

mysql> show index from xbtit_files; 
+-------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| xbtit_files |   0 | PRIMARY |   1 | info_hash | A   |  16006 |  NULL | NULL |  | BTREE  |   | 
| xbtit_files |   1 | filename |   1 | filename | A   |  16006 |  NULL | NULL | YES | BTREE  |   | 
| xbtit_files |   1 | category |   1 | category | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| xbtit_files |   1 | uploader |   1 | uploader | A   |   16 |  NULL | NULL |  | BTREE  |   | 
| xbtit_files |   1 | bin_hash |   1 | bin_hash | A   |  16006 |  20 | NULL |  | BTREE  |   | 
| xbtit_files |   1 | ix_sohaid |   1 | soha_id  | A   |  16006 |  NULL | NULL | YES | BTREE  |   | 
+-------------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

FORCE INDEX anche non funziona:

mysql> EXPLAIN SELECT * FROM xbtit_files force index (PRIMARY) WHERE IF(soha_id is null OR soha_id = '', info_hash, soha_id)='6d63dd4ab199190b531752067414d4d6e6568f90'; 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 
| 1 | SIMPLE  | xbtit_files | ALL | NULL   | NULL | NULL | NULL | 16006 | Using where | 
+----+-------------+-------------+------+---------------+------+---------+------+-------+-------------+ 

devo dividere questo query in 2 operazioni?

+0

Quante righe restituisce questa query? –

+1

Vedere il valore 'Rows_sent' nel log delle query lente. – quanta

+0

È possibile eliminare la funzione if, ad esempio SELECT * FROM xbtit_files WHERE soha_id = '6d63dd4ab199190b531752067414d4d6e6568f90' O info_hash = '6d63dd4ab199190b531752067414d4d6e6568f90'? –

risposta

3

In MySQL, non è possibile creare indici su espressioni, e l'ottimizzatore non è abbastanza intelligente per dividere la query contro due indici.

Utilizzare questa:

SELECT * 
FROM xbtit_files 
WHERE soha_id = '6d63dd4ab199190b531752067414d4d6e6568f90' 
UNION ALL 
SELECT * 
FROM xbtit_files 
WHERE soha_id = '' 
     AND info_hash = '6d63dd4ab199190b531752067414d4d6e6568f90' 
UNION ALL 
SELECT * 
FROM xbtit_files 
WHERE soha_id IS NULL 
     AND info_hash = '6d63dd4ab199190b531752067414d4d6e6568f90' 

Ogni query utilizza il proprio indice.

Si può solo combinare in una singola query:

SELECT * 
FROM xbtit_files 
WHERE (
     soha_id = '6d63dd4ab199190b531752067414d4d6e6568f90' 
     OR 
     (soha_id = '' AND info_hash = '6d63dd4ab199190b531752067414d4d6e6568f90') 
     OR 
     (soha_id IS NULL AND info_hash = '6d63dd4ab199190b531752067414d4d6e6568f90') 
     ) 

e creare un indice composito su (soha_id, info_hash) per questo di lavorare velocemente.

MySQL è anche in grado di unire i risultati di due indici insieme, utilizzando index_merge, quindi c'è una possibilità che si vedrebbe questo nel piano per la seconda query anche se non si crea un indice composito.

2

Perché funzioni sono scatole nere: http://use-the-index-luke.com/sql/where-clause/functions/case-insensitive-search

Modifica - ti ha dato troppo poco contesto, mi spiace.

La parte rilevante è:

It is a trap we all fall into. We instantly recognize the relation between 
LAST_NAME and UPPER(LAST_NAME) and expect the database to “see” it as well. 
In fact, the optimizer’s picture is more like that: 

SELECT first_name, last_name, phone_number 
    FROM employees 
WHERE BLACKBOX(...) = 'WINAND'; 

The UPPER function is just a black box. The parameters to the function are 
not relevant because there is no general relationship between the function’s 
parameters and the result. 

Ciò vale per tutte le funzioni: UPPER, se, indipendentemente ...

MySQL è attraversato fuori perché la soluzione a questo problema, che è descritto più in basso la pagina, non funziona con MySQL.

+0

Vedo la croce rossa su MySQL. Cosa intendi? – quanta

2

È possibile leggere this per sapere perché l'operatore OR non si applica al DB di indicizzazione.

1

funzioni d'uso in cui si potrebbero rallentare le prestazioni (ad eccezione LEFT funzione) .try questa query

SELECT * FROM xbtit_files WHERE 
((soha_id is null OR soha_id = '') AND (info_hash='6d63dd4ab199190b531752067414d4d6e6568f90')) OR 
((soha_id='6d63dd4ab199190b531752067414d4d6e6568f90')) 
0

La chiave primaria si basa sulla hash del torrente, ma è possibile aggiungere un campo ID e definire con la chiave primaria
Ti piace questa:

ALTER TABLE `xbtit_files` DROP PRIMARY KEY; 
ALTER TABLE `xbtit_files` ADD `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; 
ALTER TABLE `xbtit_files` ADD UNIQUE (`info_hash`); 

Non dimenticate di mettere i campi info_hash per UNIQUE

Problemi correlati