Оптимизация запроса MySQL для выбора всех точек в многоугольнике с использованием пространственных индексов - PullRequest
0 голосов
/ 07 ноября 2018

Во-первых, я признаю, что мой опыт работы с пространственными функциями очень минимален. У меня есть таблица в MySQL с 20 полями и 23549187 записей, которые содержат географические данные. Одним из полей является «точка», которая имеет тип данных точки и имеет пространственный индекс. У меня есть запрос, который выбирает все точки в многоугольнике, который выглядит так,

select * from `table_name` where ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((151.186 -23.497,151.207 -23.505,151.178 -23.496,151.174 -23.49800000000001,151.176 -23.496,151.179 -23.49500000000002,151.186 -23.497))'), `point`)

Это хорошо работает, так как многоугольник маленький. Однако, если многоугольник становится массивным, время выполнения становится очень медленным, и самый медленный запрос до сих пор выполнялся в течение 15 минут. Добавление индекса действительно помогло снизить его до 15 минут, которые в противном случае заняли бы около часа. Есть ли что-нибудь, что я могу сделать здесь для дальнейшего улучшения. Этот запрос будет выполняться сценарием PHP, который запускается как демон, и я беспокоюсь, не вызовет ли этот медленный запрос сервер MySQL.

Приветствуются все предложения по улучшению. Спасибо.

EDIT:

show create table;

CREATE TABLE `table_name` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `lat` float(12,6) DEFAULT NULL,
  `long` float(12,6) DEFAULT NULL,
  `point` point NOT NULL,
  PRIMARY KEY (`id`),
  KEY `lat` (`lat`,`long`),
  SPATIAL KEY `sp_index` (`point`)
) ENGINE=MyISAM AUTO_INCREMENT=47222773 DEFAULT CHARSET=utf8mb4

Есть еще несколько полей, которые я не должен раскрывать здесь, однако фильтр выиграл

Объяснить вывод sql для медленного запроса:

+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows     | Extra       |
+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+
|  1 | SIMPLE      | table_name | ALL  | NULL          | NULL | NULL    | NULL | 23549187 | Using where |
+----+-------------+------------+------+---------------+------+---------+------+----------+-------------+

Объяснить вывод sql для запроса с меньшими полигонами,

+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key      | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+
|  1 | SIMPLE      | table_name | range | sp_index      | sp_index | 34      | NULL |    1 | Using where |
+----+-------------+------------+-------+---------------+----------+---------+------+------+-------------+

Похоже, самый большой многоугольник не использует индекс.

1 Ответ

0 голосов
/ 07 ноября 2018

MySQL использует R-Trees для индексации пространственных данных. Как и индексы B-Tree , они оптимальны для запросов, нацеленных на небольшое подмножество общего числа. По мере того, как ваш ограничивающий полигон увеличивается, число возможных совпадений увеличивается, и в какой-то момент оптимизатор решает, что более эффективно перейти к полному сканированию таблицы. Это похоже на сценарий здесь, и я вижу три варианта:

Сначала попробуйте добавить LIMIT к вашему запросу. Обычно MySQL игнорирует индекс, если оптимизатор приходит к выводу, что при полном сканировании таблицы будет происходить меньшее число запросов ввода-вывода. Но, по крайней мере с индексами B-Tree, MySQL будет закорачивать эту логику и всегда выполнять погружение B-Tree, когда присутствует LIMIT. Я предполагаю, что R-Tree имеют аналогичное короткое замыкание.

Во-вторых, и по духу аналогично первому, попробуйте , заставив MySQL использовать индекс . Это указывает MySQL, что сканирование таблицы обходится дороже, чем решит оптимизатор. Поймите, что оптимизатор обладает только эвристикой и не знает, насколько «дорогие» вещи выходят за рамки выводов его внутренней статистики. У нас, людей, есть интуиция, которая иногда - , иногда - знает лучше.

select * force index (`sp_index`) from `table_name` where ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((151.186 -23.497,151.207 -23.505,151.178 -23.496,151.174 -23.49800000000001,151.176 -23.496,151.179 -23.49500000000002,151.186 -23.497))'), `point`)

Наконец, если они не работают, то вам нужно разбить ограничивающий полигон на более мелкие. Например, если ограничивающим полигоном является квадрат 500 км на сторону, разбейте его на 4 квадрата по 250 км с каждой стороны или 16 квадратов по 125 км на сторону и т. Д. Затем UNION все это вместе. Индекс будет использоваться для каждого, и совокупный результат может быть быстрее. (Обратите внимание, что важно UNION их вместе: MySQL не может применять многократное сканирование диапазона к пространственному запросу.)

...