Оптимизация соединения на производной таблице - ОБЪЯСНЯЙТЕ разное на локальном и на сервере - PullRequest
0 голосов
/ 27 февраля 2019

У меня есть следующий уродливый запрос, который работает нормально, но не очень хорошо, на моей локальной машине (1.4 секунды, работает v5.7).На сервере, который я использую, на котором установлена ​​более старая версия MySQL (v5.5), запрос просто зависает.Похоже, что ловится на «Копирование в таблицу tmp»:

SELECT
  SQL_CALC_FOUND_ROWS
  DISTINCT p.parcel_number,
  p.street_number,
  p.street_name,
  p.site_address_city_state,
  p.number_of_units,
  p.number_of_stories,
  p.bedrooms,
  p.bathrooms,
  p.lot_area_sqft,
  p.cost_per_sq_ft,
  p.year_built,
  p.sales_date,
  p.sales_price,
  p.id
  FROM (
    SELECT APN, property_case_detail_id FROM property_inspection AS pi
      GROUP BY APN, property_case_detail_id
      HAVING 
      COUNT(IF(status='Resolved Date', 1, NULL)) = 0
    ) as open_cases
  JOIN property AS p
  ON p.parcel_number = open_cases.APN
  LIMIT 0, 1000;

mysql> show processlist;
+-------+-------------+-----------+--------------+---------+------+----------------------+------------------------------------------------------------------------------------------------------+
| Id    | User        | Host      | db           | Command | Time | State                | Info                                                                                                 |
+-------+-------------+-----------+--------------+---------+------+----------------------+------------------------------------------------------------------------------------------------------+
| 21120 | headsupcity | localhost | lead_housing | Query   |   21 | Copying to tmp table | SELECT
          SQL_CALC_FOUND_ROWS
          DISTINCT p.parcel_number,
          p.street_numbe |
| 21121 | headsupcity | localhost | lead_housing | Query   |    0 | NULL                 | show processlist                                                                                     |
+-------+-------------+-----------+--------------+---------+------+----------------------+------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

Объяснения разные на моей локальной машине и на сервере, и я предполагаю, что единственная причина, по которой мой запрос вообще выполняется на моей локальноймашина из-за ключа, который автоматически создается в производной таблице:

Объяснение (локальное):

+----+-------------+------------+------------+------+---------------+-------------+---------+------------------------------+---------+----------+---------------------------------+
| id | select_type | table      | partitions | type | possible_keys | key         | key_len | ref                          | rows    | filtered | Extra                           |
+----+-------------+------------+------------+------+---------------+-------------+---------+------------------------------+---------+----------+---------------------------------+
|  1 | PRIMARY     | p          | NULL       | ALL  | NULL          | NULL        | NULL    | NULL                         |   40319 |   100.00 | Using temporary                 |
|  1 | PRIMARY     | <derived2> | NULL       | ref  | <auto_key0>   | <auto_key0> | 8       | lead_housing.p.parcel_number |      40 |   100.00 | NULL                            |
|  2 | DERIVED     | pi         | NULL       | ALL  | NULL          | NULL        | NULL    | NULL                         | 1623978 |   100.00 | Using temporary; Using filesort |
+----+-------------+------------+------------+------+---------------+-------------+---------+------------------------------+---------+----------+---------------------------------+

Объяснение (сервер):

+----+-------------+------------+------+---------------+------+---------+------+---------+------------------------------------------+
| id | select_type | table      | type | possible_keys | key  | key_len | ref  | rows    | Extra                                    |
+----+-------------+------------+------+---------------+------+---------+------+---------+------------------------------------------+
|  1 | PRIMARY     | p          | ALL  | NULL          | NULL | NULL    | NULL |   41369 | Using temporary                          |
|  1 | PRIMARY     | <derived2> | ALL  | NULL          | NULL | NULL    | NULL |  122948 | Using where; Distinct; Using join buffer |
|  2 | DERIVED     | pi         | ALL  | NULL          | NULL | NULL    | NULL | 1718586 | Using temporary; Using filesort          |
+----+-------------+------------+------+---------------+------+---------+------+---------+------------------------------------------+

Схемы:

mysql> explain property_inspection;
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
| Field                   | Type         | Null | Key | Default           | Extra                       |
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
| id                      | int(11)      | NO   | PRI | NULL              | auto_increment              |
| lblCaseNo               | int(11)      | NO   | MUL | NULL              |                             |
| APN                     | bigint(10)   | NO   | MUL | NULL              |                             |
| date                    | varchar(50)  | NO   |     | NULL              |                             |
| status                  | varchar(500) | NO   |     | NULL              |                             |
| property_case_detail_id | int(11)      | YES  | MUL | NULL              |                             |
| case_type_id            | int(11)      | YES  | MUL | NULL              |                             |
| date_modified           | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| update_status           | tinyint(1)   | YES  |     | 1                 |                             |
| created_date            | datetime     | NO   |     | NULL              |                             |
+-------------------------+--------------+------+-----+-------------------+-----------------------------+
10 rows in set (0.02 sec)

mysql> explain property; (not all columns, but you get the gist)
+----------------------------+--------------+------+-----+-------------------+-----------------------------+
| Field                      | Type         | Null | Key | Default           | Extra                       |
+----------------------------+--------------+------+-----+-------------------+-----------------------------+
| id                         | int(11)      | NO   | PRI | NULL              | auto_increment              |
| parcel_number              | bigint(10)   | NO   |     | 0                  |                             |
| date_modified              | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| created_date               | datetime     | NO   |     | NULL              |                             |
+----------------------------+--------------+------+-----+-------------------+-----------------------------+

Переменные, которые могут иметь отношение:

tmp_table_size: 16777216
innodb_buffer_pool_size: 8589934592

Есть идеи о том, как это оптимизировать, и идеи, почему объяснения так различны?

Ответы [ 2 ]

0 голосов
/ 08 марта 2019

Поскольку здесь оптимизаторы совершенно разные, давайте попробуем оптимизировать

SELECT APN, property_case_detail_id FROM property_inspection AS pi
  GROUP BY APN, property_case_detail_id
  HAVING 
  COUNT(IF(status='Resolved Date', 1, NULL)) = 0
) as open_cases

Попробуйте:

SELECT ...
    FROM property AS p
    WHERE NOT EXISTS ( SELECT 1 FROM property_inspection
                 WHERE status = 'Resolved Date'
                   AND p.parcel_number = APN )
    ORDER BY ???  -- without this, the `LIMIT` is unpredictable
    LIMIT 0, 1000;

или ...

SELECT ...
    FROM property AS p
    LEFT JOIN  property_inspection AS pi  ON p.parcel_number = pi.APN
    WHERE pi.status = 'Resolved Date'
      AND pi.APN IS NULL
    ORDER BY ???  -- without this, the `LIMIT` is unpredictable
    LIMIT 0, 1000;

Индекс:

property_inspection:  INDEX(status, parcel_number) -- in either order
0 голосов
/ 28 февраля 2019

MySQL 5.5 и 5.7 весьма различны, и у последнего лучше оптимизатор, поэтому неудивительно, что планы объяснения отличаются.

Вы бы лучше предоставили SHOW CREATE TABLE property; и SHOW CREATE TABLE property_inspection; выходные данные, как это покажетиндексы в ваших таблицах.

Ваш подзапрос является проблемой.- Сервер пытается обработать строки 1.6M без индекса и сгруппировать все.- Having довольно дорогая операция, поэтому вам лучше ее избегать, особенно в подзапросах.- Группировка в этом случае плохая идея.Вам не нужно агрегирование / подсчет.Вам нужно проверить, существует ли статус «Дата разрешения»

. На основании предоставленной информации я бы порекомендовал: - Изменить таблицу property_inspection, чтобы уменьшить длину столбца status.- Добавить индекс по столбцу.По возможности используйте индекс покрытия (APN, property_case_detail_id, status) (в этом порядке столбцов).- Измените запрос на что-то вроде этого:

SELECT
    SQL_CALC_FOUND_ROWS
    DISTINCT p.parcel_number,
    ...
    p.id
FROM
    property_inspection AS `pi1`
    INNER JOIN property AS p ON (
        p.parcel_number = `pi1`.APN
    )
    LEFT JOIN (
        SELECT
              `pi2`.property_case_detail_id
            , `pi2`. APN
        FROM
            property_inspection AS `pi2`
        WHERE
            `status` = 'Resolved Date'
    ) AS exclude ON (
        exclude.APN = `pi1`.APN
        AND exclude.property_case_detail_id = `pi1`.property_case_detail_id
    )
WHERE
    exclude.APN IS NULL
LIMIT
    0, 1000;
...