Есть ли способ ускорить этот запрос? - PullRequest
0 голосов
/ 23 октября 2009

Этот запрос действительно медленный. Занимает от 9 до 10 секунд ...

SELECT DISTINCT a.*
FROM addresses a
LEFT JOIN contacts c
ON c.id = a.contact_id
LEFT JOIN organizations o
ON o.id = a.organization_id
ORDER BY c.last_name, c.first_name, o.name
LIMIT 0, 24

Если я закомментирую предложение ORDER BY, запрос будет выполняться намного быстрее - около 5 миллисекунд. Но мне нужно ORDER BY для поддержки подкачки результатов поиска. А пользователям нужно отсортировать адреса по контактам и организации.


Структура таблицы

addresses
---------
id int NOT NULL
contact_id int       # could be NULL
organization_id int  # could be NULL

contacts
--------
id int NOT NULL
first_name varchar(255)
last_name varchar(255)

organizations
-------------
id int NOT NULL
name varchar(255)

Они все таблицы InnoDB.

У меня есть эти индексы в таблице контактов:

  KEY `idx_contacts_first_name` (`first_name`),
  KEY `idx_contacts_last_name` (`last_name`),
  KEY `idx_contacts_first_name_last_name` (`first_name`,`last_name`)

А на столе организации:

  KEY `idx_organization_name` (`name`)

Количество данных

Addresses:     22,271
Contacts:      17,906
Organizations:  8,246

ОПИСАТЬ выход

mysql> DESCRIBE
    -> SELECT DISTINCT a.*
    -> FROM addresses a
    -> LEFT JOIN contacts c
    -> ON c.id = a.contact_id
    -> LEFT JOIN organizations o
    -> ON o.id = a.organization_id
    -> ORDER BY c.last_name, c.first_name, o.name
    -> LIMIT 0, 24;
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                                        | rows  | Extra                           |
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
|  1 | SIMPLE      | a     | ALL    | NULL          | NULL    | NULL    | NULL                                       | 22387 | Using temporary; Using filesort | 
|  1 | SIMPLE      | c     | eq_ref | PRIMARY       | PRIMARY | 4       | contactdb_v2_development.a.contact_id      |     1 | Distinct                        | 
|  1 | SIMPLE      | o     | eq_ref | PRIMARY       | PRIMARY | 4       | contactdb_v2_development.a.organization_id |     1 | Distinct                        | 
+----+-------------+-------+--------+---------------+---------+---------+--------------------------------------------+-------+---------------------------------+
3 rows in set (0.00 sec)

Ответы [ 5 ]

2 голосов
/ 23 октября 2009

Я попробовал ваш пример с аналогичными объемами данных, и на моем непритязательном ноутбуке (Pentium M 1,7 ГГц) запрос занимает меньше секунды (при первом запуске, позже - еще меньше).

Вы случайно забыли PK в столбце id? Вы не упоминаете об этом, поэтому просто спрашиваете ... если вы забудете об этом, производительность, очевидно, будет ужасной - не говоря уже о том, что каждый администратор базы данных будет съеживаться за столами без ПК.

В противном случае попробуйте это:

DESCRIBE <your query>

Это даст вам план запросов MySQL. Опубликуйте это (отредактируйте свой вопрос), и должно быть яснее, что занимает так много времени.

По поводу дальнейших мыслей:

Запрос всегда будет иметь проблемы с производительностью, потому что вы просите базу данных прочитать и отсортировать все адреса и отобразить их. ORDER BY означает, что он должен прочитать все, прежде чем что-то вернуть, поэтому он всегда будет медленным. Какой смысл в таком воспроизведении всей базы данных? Будут ли пользователи просматривать несколько тысяч записей?

Рассмотрим, например, разрешить поисковый запрос. С условием WHERE запрос будет выполняться намного быстрее.

1 голос
/ 23 октября 2009

Попробуйте добавить этот индекс:

idx_contacts_last_name_first_name (last_name, first_name)

Кстати: вы можете удалить idx_contacts_first_name, поскольку оно является дублирующим, и если вы добавите этот индекс, вы можете удалить idx_contacts_last_name.

1 голос
/ 23 октября 2009

Попробуйте изменить свой SQL на что-то вроде следующего:

SELECT a.column1, a.column2, ...
FROM addresses a
LEFT JOIN contacts c
ON c.id = a.contact_id
LEFT JOIN organizations o
ON o.id = a.organization_id
GROUP BY a.column1, a.column2, ...
ORDER BY c.last_name, c.first_name, o.name
LIMIT 0, 24

Я обнаружил, что GROUP BY намного быстрее, чем DISTINCT в целом, хотя я не знаю, почему это будет.

1 голос
/ 23 октября 2009

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

0 голосов
/ 23 октября 2009

Посмотрим.

  • Адреса: 22 271
  • Контакты: 17,906
  • Организации: 8 246

обращается к контактам LEFT JOIN c дает около 20 000 * 20 000 ~ 400 миллионов сравнений для приблизительно 20 000 результатов

LEFT JOIN организации дают около 10 000 * 20 000 ~ 200 миллионов сравнений для примерно 20 000 результатов

, который мы сортируем в основном по контактным строкам, затем отбрасываем все, кроме 24 из них. Кажется, различимость адресов имеет минимальное значение.

Так как мы в основном сортируем по контактам, как насчет подвыбора по контактам, сохраняя несколько больше (скажем, в 4 раза), чем нам нужно:

SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 100

Тогда присоединяй их к их адресам, сохраняя первые сотни или около того

     SELECT a.* 
       FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c
  LEFT JOIN addresses a
         ON c.id = a.contact_id
      LIMIT 0, 100

Затем присоединитесь к этим организациям

 SELECT * 
   FROM (
        SELECT * 
          FROM (SELECT * FROM contacts ORDER BY last_name, first_name LIMIT 0, 100) AS c
     LEFT JOIN addresses a
            ON c.id = a.contact_id
         LIMIT 0, 100
         ) AS ca LEFT JOIN organizations o
      ON o.id = ca.organization_id
ORDER BY ca.last_name, ca.first_name, o.name
   LIMIT 0, 24

Я уверен, что синтаксис облажан, но я также уверен, что принцип сокращения результатов, установленных на каждом этапе, указывает на поучительный путь. Я, вероятно, тоже сделал пару компромиссов, так что результат близко приближается к 10-секундному ответу, но гораздо быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...