Файловая сортировка исправлена ​​/ добавлена ​​при добавлении / удалении 2 бит кода - PullRequest
0 голосов
/ 19 марта 2020
SELECT test2.*, account.*
FROM test2
     JOIN account on test2.account_id = account.id
where test2.id > 0
and test2.active = TRUE
and account.age BETWEEN 18 AND 80 AND account.gender = 'MALE'
ORDER BY test2.id DESC
LIMIT 20
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `email` varchar(45) NOT NULL,
  `username` varchar(30) NOT NULL,
  `password` varchar(100) NOT NULL,
  `location_id` int(11) NOT NULL DEFAULT '0',
  `country` varchar(40) DEFAULT NULL,
  `gender` varchar(11) NOT NULL,
  `role` varchar(10) NOT NULL DEFAULT 'USER',
  `bio` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
  `main_profile_pic_uuid` char(32) CHARACTER SET ascii COLLATE ascii_general_ci DEFAULT NULL,
  `picture_verified` tinyint(1) NOT NULL DEFAULT '0',
  `looking_for` varchar(20) DEFAULT NULL,
  `include_in_search` tinyint(1) NOT NULL DEFAULT '1',
  `age` tinyint(4) NOT NULL,
  `latitude` double DEFAULT '0',
  `longitude` double DEFAULT '0',
  `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `last_active` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `account_username_uindex` (`username`),
  KEY `account_email_index` (`email`),
  KEY `account_location_id_fk` (`location_id`),
  KEY `account_multi_index` (`include_in_search`,`location_id`,`gender`,`looking_for`,`age`,`picture_verified`),
  KEY `account_multi2_index` (`include_in_search`,`looking_for`,`age`,`location_id`,`gender`,`picture_verified`),
  KEY `account_multi3_index` (`include_in_search`,`looking_for`,`age`,`gender`,`picture_verified`,`location_id`),
  KEY `account_available_for_chat_index` (`available_for_chat`),
  CONSTRAINT `account_location_id_fk` FOREIGN KEY (`location_id`) REFERENCES `location` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=710538 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `test2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) NOT NULL,
  `duration` smallint(6) NOT NULL DEFAULT '30',
  `began` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `ending` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `active` tinyint(1) NOT NULL DEFAULT '1',
  `latitude` double NOT NULL,
  `longitude` double NOT NULL,
  PRIMARY KEY (`id`),
  KEY `test2_active_ending_index` (`active`,`ending`),
  KEY `test2_account_id_fk` (`account_id`,`id` DESC),
  CONSTRAINT `test2_account_id_fk` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1002031 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Кстати, test2 имеет около 1 миллиона строк ПОСЛЕ фильтрации, учетная запись имеет около 700k также после фильтрации.

Выше работает примерно в 2se c с filesort, слишком медленно:

1   SIMPLE  account     ALL PRIMARY             702300  1.11    Using where; Using temporary; Using filesort
1   SIMPLE  test2       ref PRIMARY,test2_active_ending_index,test2_account_id_fk   test2_account_id_fk 4   db.account.id   2   25  Using index condition; Using where

Если я достану бит "and account.age МЕЖДУ 18 И 80 И account.gender = 'MALE'", НО оставлю "ЗАКАЗАТЬ BY test2.id DES C ", работает за 10 мсек, нет filesort.

Если я достану " ORDER BY test2.id DES C " бит, НО сохранять " "и account.age между 18 и 80 AND account.gender = 'MALE'" , работает в 10 мс, нет filesort. Нет заказа по:

1   SIMPLE  account     ALL PRIMARY             702300  1.11    Using where
1   SIMPLE  test2       ref PRIMARY,test2_active_ending_index,test2_account_id_fk   test2_account_id_fk 4   db.account.id   2   25  Using index condition; Using where

Все поля имеют индивидуальные или составные индексы.

Кто-нибудь знает, как сократить время выполнения или избавиться от filesort ?

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

1   SIMPLE  new_table       range   PRIMARY,new_table_account_id_fk PRIMARY 4       498496  100 Using where; Backward index scan
1   SIMPLE  account     eq_ref  PRIMARY PRIMARY 4   db.new_table.account_id 1   5   Using where

Обратите внимание на разница по типу & ref & extra, по сравнению с test2. Я попытался ограничить количество строк аккаунта для test2 до 300k, при той же скорости и результате.

1 Ответ

0 голосов
/ 22 марта 2020

Сначала несколько общих утверждений:

  • Фильтрация по двум таблицам (предложение WHERE затрагивает две таблицы) заставляет оптимизатор угадать, с какой таблицы начинать. Иногда решение состоит в том, чтобы переместить столбцы, используемые для фильтрации, в одну таблицу. Это может не применяться в вашем случае.
  • При использовании LIMIT часто лучше получить идентификаторы для ограниченного числа строк, а затем вернуться в таблицу (таблицы), чтобы получить остальную часть столбцы.
  • Не используйте SELECT *, если вам не нужны все столбцы. Большие (TEXT / BLOB) столбцы могут быть очень дорогими для извлечения, но могут быть выброшены позже.
  • Если вы не используете MySQL 8.0, не смешивайте указания, как в `ORDER ПО AS C, b DES C. Иногда «достаточно хорошо» изменить оба направления в одном и том же направлении.
  • Если вы не делаете что-то странное с AUTO_INCREMENT, значение всегда будет > 0.

Попробуйте:

SELECT  test2.*,
        account.*
    FROM (
        SELECT  test2.id AS t_id, account.id AS a_id
            FROM  test2
            JOIN  account  ON test2.account_id = account.id
            WHERE  test2.active = TRUE
              AND  EXISTS (
                SELECT  1
                    FROM  account
                    WHERE  age BETWEEN 18 AND 80
                      AND  gender = 'MALE'
                      AND  id = test2.account_id 
                          )
            ORDER BY  test2.id
            LIMIT  20 
         ) AS x
    JOIN  test2    ON test2.id   = x.t_id
    JOIN  account  ON account.id = x.a_id
    ORDER BY  test2.id -- Yes, repeated

С этим составным индексом:

test2:  (active, id, account_id)  -- in this order (covering)

Пожалуйста, укажите EXPLAIN SELECT ... для любых формулировок, которые вы хотите обсудить.

...