2015-08-06 2 views
1

У меня есть база данных MySQL InnoDB, доступ к которой осуществляется с сервера Django. У меня есть эта таблица:Почему мой запрос неоптимальный?

+--------------+--------------+------+-----+---------+----------------+ 
| Field  | Type   | Null | Key | Default | Extra   | 
+--------------+--------------+------+-----+---------+----------------+ 
| id   | int(11)  | NO | PRI | NULL | auto_increment | 
| areasymbol | varchar(255) | NO |  | NULL |    | 
| spatialver | int(11)  | YES |  | NULL |    | 
| lkey   | int(11)  | YES |  | NULL |    | 
| musym  | varchar(255) | NO |  | NULL |    | 
| mukey  | int(11)  | YES |  | NULL |    | 
| featsym  | varchar(255) | NO |  | NULL |    | 
| featkey  | int(11)  | YES |  | NULL |    | 
| north  | double  | YES | MUL | NULL |    | 
| south  | double  | YES | MUL | NULL |    | 
| east   | double  | YES | MUL | NULL |    | 
| west   | double  | YES | MUL | NULL |    | 
| soil_type_id | int(11)  | YES | MUL | NULL |    | 
+--------------+--------------+------+-----+---------+----------------+ 

В таблице в настоящее время содержит ~ 7-8 миллионов строк, и я ожидаю, что это будет по крайней мере в 3 раза, что многие, когда я закончу. Это статическая таблица. Мы импортируем, чтобы добавлять к нему вещи каждый раз в то время, но ничто не может быть изменено или удалено.

+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table   | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| soil_soilregion |   0 | PRIMARY       |   1 | id   | A   |  7657769 |  NULL | NULL |  | BTREE  |   |    | 
| soil_soilregion |   1 | soil_soilregion_e733fdfc   |   1 | soil_type_id | A   |   15 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | north_soilregion     |   1 | north  | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | south_soilregion     |   1 | south  | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | east_soilregion     |   1 | east   | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | west_soilregion     |   1 | west   | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | north_south_east_west_soilregion |   1 | north  | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | north_south_east_west_soilregion |   2 | south  | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | north_south_east_west_soilregion |   3 | east   | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
| soil_soilregion |   1 | north_south_east_west_soilregion |   4 | west   | A   |  7657769 |  NULL | NULL | YES | BTREE  |   |    | 
+-----------------+------------+----------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 

У меня есть квадратный ящик на север/юг/восток/запад координаты, и я ищу для любой из этих областей, которые могут перекрываться, что окно

При запуске этого запроса к базе данных:

select * 
from soil_soilregion 
where east > -86.8379775155 AND north > 40.3782334957 AND 
     south < 40.3817576747 AND west < -86.8240119179; 

это займет ~ 10 секунд, что неприемлемо. Когда я использую это объяснить то, что он говорит мне:

+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 
| id | select_type | table   | type | possible_keys                      | key | key_len | ref | rows | Extra  | 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | soil_soilregion | ALL | north_soilregion,south_soilregion,east_soilregion,west_soilregion,north_south_east_west_soilregion | NULL | NULL | NULL | 7657769 | Using where | 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 

Когда я запускаю этот запрос в базе данных:

select * 
from soil_soilregion 
where east > -86.8379775155 AND east < -85.8379775155 AND 
     north > 40.3782334957 AND north < 41.3782334957 AND 
     south < 40.3817576747 AND south > 39.3817576747 AND 
     west < -86.8240119179 AND west > -87.8040119189; 

Это занимает больше похоже на 6-7 секунд. Это лучше, но все еще субоптимально. Этот код по-прежнему будет работать, так как ни один объект не превышает 1 высокий или широкий (поэтому я дал ему максимальное расстояние в каждом направлении 1).

У меня есть несколько вопросов:

  1. Почему первый запрос не используя индексы? (Я предполагаю, что это связано с тем, что в диапазоне слишком много потенциальных элементов)
  2. Почему он никогда не использует мой составной индекс? Разве это не было бы оптимальным?
  3. Есть ли что-нибудь, что я могу сделать, чтобы улучшить этот запрос или мои индексы?

Примечание: Использование индекса силы имеет только отрицательные последствия.

Спасибо!

Edit 1: В соответствии с предложениями, я изменил запрос в том же порядке, как составной индекс, и это то, что я получил:

explain select * from soil_soilregion where north > 40.3782334957 AND south < 40.3817576747 AND east > -86.8379775155 AND west < -86.8240119179; 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 
| id | select_type | table   | type | possible_keys                      | key | key_len | ref | rows | Extra  | 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 
| 1 | SIMPLE  | soil_soilregion | ALL | north_soilregion,south_soilregion,east_soilregion,west_soilregion,north_south_east_west_soilregion | NULL | NULL | NULL | 7657769 | Using where | 
+----+-------------+-----------------+------+----------------------------------------------------------------------------------------------------+------+---------+------+---------+-------------+ 
+0

вы пытаетесь изменить порядок утверждений в том месте? север Юг Восток Запад? – JamieD77

+0

@ JamieD77 - Оптимизатор ничего не изменит, если вы измените порядок предложения WHERE. –

+0

Пожалуйста, предоставьте 'SHOW CREATE TABLE'; он более описателен, чем 'DESCRIBE'. –

ответ

1

Проблема с запросом ваши неравенства. Увы, это ограничивает использование индексов - не более одного неравенства для каждого индекса.

Структура данных, необходимая для решения этой проблемы, представляет собой многомерный индекс. В SQL-базах данных это обычно предоставляется с использованием расширений GIS, которые документированы here.

Без этих расширений вы можете попробовать тайный ум. Я могу думать об одном пути этой проблемы, но это делает таблицу и запрос более сложными. Добавьте новый столбец для востока и севера, который является целым числом: easti и northi. Затем постройте индекс на easti, northi.И, пишите запрос как:

select * 
from ((select sr.* 
     from soil_soilregion sr 
     where easti = -86 and northi in (40, 41) 
    ) union all 
     (select sr.* 
     from soil_soilregion sr 
     where easti = -85 and northi in (40, 41) 
    ) 
    ) sr 
where east > -86.8379775155 AND north > 40.3782334957 AND 
     south < 40.3817576747 AND west < -86.8240119179; 

Подзапрос получит все в относительно небольшой коробке. Затем он фильтруется внешним запросом. Подзапрос должен использовать индексы, поэтому он должен быть довольно быстрым.

Учитывая размер того, что вы ищете, использование доли степени будет работать даже лучше, чем целая степень для целочисленного преобразования.

+0

Можете ли вы объяснить, что вы подразумеваете под «Увы, это ограничивает использование индексов - не более одного неравенства на поиск индекса» – Erix

+0

@Erix. , , Разумным местом для начала является документация MySQL по индексам с несколькими столбцами: https://dev.mysql.com/doc/refman/5.6/en/multiple-column-indexes.html. –

0

A Краткосрочное, но частичное исправление должно иметь «индекс покрытия». То есть, создайте индекс, который имеет ограничивающий прямоугольник, плюс id (и, возможно, тип почвы?). то сделать это:

SELECT b.* 
    FROM (
     SELECT id FROM soilregion 
      WHERE east... AND west ... AND ... 
     ) AS a 
    JOIN soilregion AS b ON b.id = a.id; 

Это, вероятно, ускорит запрос из-за:

  • Индекс это все, что необходимо в подзапроса
  • Индекс меньше, чем данные
  • Когда подзапрос закончен, он имеет короткий список ids, который легко и быстро просматривается в реальной таблице (через JOIN).

Некоторые из ваших «почему» вопросы:

  • Отдельные показатели просто устранить некоторую долю строк 7М (как в «все к востоку отсюда»). Это не очень помогает. Кроме того, когда индекс является «бесполезным», он не используется - быстрее сканировать таблицу.

  • Комбинированный индекс (с севера на юг ...) не улучшается. Это происходит потому, что начинается с теста диапазона на north и не может пройти мимо этого.

  • Вторая попытка «казалась» быстрее - это может быть из-за кеширования, а не из-за того, что оно лучше.

Solutions ...

План A: Пространственный индекс как упомянуто Гордон.

План B: Перестройте данные для работы с методом псевдо-2D-индексации, описанным в моем блоге "find the nearest pizza parlors". Проблема: я не думал, как адаптироваться для «перекрытия» вместо «ближайшего».