Вопрос

У меня есть следующие таблицы:

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `last_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `account_data` text COLLATE utf8_unicode_ci,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `twitter_username` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `crypted_password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `password_salt` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `persistence_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `single_access_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `perishable_token` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `login_count` int(11) NOT NULL DEFAULT '0',
  `failed_login_count` int(11) NOT NULL DEFAULT '0',
  `last_request_at` datetime DEFAULT NULL,
  `current_login_at` datetime DEFAULT NULL,
  `last_login_at` datetime DEFAULT NULL,
  `current_login_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `last_login_ip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `is_admin` tinyint(1) DEFAULT '0',
  `referrer_id` int(11) DEFAULT NULL,
  `partner` tinyint(1) DEFAULT '0',
  `subscription_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT 'free',
  `workflow_state` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `persona_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `persona_index` (`persona_id`)
) ENGINE=InnoDB 

и таблица:

CREATE TABLE `user_actions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `action_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `module` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `data` text COLLATE utf8_unicode_ci,
  `timestamp` datetime DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id_index` (`user_id`),
  KEY `action_type_index` (`action_type`),
  KEY `user_action_type_index` (`user_id`,`action_type`),
  KEY `timestamp_index` (`timestamp`),
  KEY `user_id_timestamp_index` (`user_id`,`timestamp`)
) ENGINE=InnoDB 

Проблема в следующем запросе:

    SELECT user_actions.*, users.twitter_username, users.email FROM `user_actions` 
INNER JOIN users ON (user_actions.user_id=users.id) ORDER BY timestamp DESC LIMIT 0, 30

Вот объяснение:

user_actions    
The table was retrieved with this index: user_id_timestamp_index
You can speed up this query by querying only fields that are within the index. Or you can create an index that includes every field in your query, including the primary key.
Approximately 76 rows of this table were scanned.
users   
This table was retrieved with a full table scan, which is often quite bad for performance, unless you only retrieve a few rows.
The table was retrieved with this index:
No index was used in this part of the query.
A temporary table was created to access this part of the query, which can cause poor performance. This typically happens if the query contains GROUP BY and ORDER BY clauses that list columns differently.
MySQL had to do an extra pass to retrieve the rows in sorted order, which is a cause of poor performance but sometimes unavoidable.
You can speed up this query by querying only fields that are within the index. Or you can create an index that includes every field in your query, including the primary key.
Approximately 3445 rows of this table were scanned.

Этот запрос занимает много времени, чтобы выполнить, какие идеи, как улучшить?

Это было полезно?

Решение

Вот ваш оригинальный запрос:

SELECT
    user_actions.*,
    users.twitter_username,
    users.email
FROM
    `user_actions`  
    INNER JOIN users
    ON (user_actions.user_id=users.id)
    ORDER BY timestamp
    DESC LIMIT 0, 30
;

Во -первых, я замечаю, что вы присоединяетесь к двум целым таблицам. Поскольку тебе нужно только twitter_username а также email от users таблица, вы должны присоединиться только к users Используя три столбца: id, twitter_username а также email.

Во -вторых, это LIMIT пункт. Это выполняется после присоединения. Вы должны выполнить его перед присоединением. В вашем случае вы запрашиваете 30 самых последних действий пользователя. Если вы можете гарантировать, что только 30 строк останавливаются от user_actions, соединение должно работать намного быстрее.

если ты Прочитайте ответ от @dtest, его первые две точки смены уже говорят вам, что не так, запрос из -за действий MySQL приведет к сбору данных из каждой таблицы. Ключ в том, чтобы понять, как будут выглядеть временные таблицы, когда запрос обрабатывается и где будут проживать данные (память или диск).

Что вам нужно сделать, так это рефактор запроса, чтобы обмануть оптимизатор MySQL запроса. Заставьте запрос произвести меньшие временные столы. В большинстве случаев изменения конфигурации в моем.cnf должны иметь драмамтическую разницу. В других случаях, таких как этот, рефакторирование запроса может быть достаточным.

Вот мое предложенное изменение вашего запроса, которое должно работать быстрее:

SELECT
    ua.*,
    u.twitter_username,
    u.email
FROM
    (SELECT * FROM `user_actions`
    ORDER BY timestamp DESC LIMIT 30) ua
    LEFT JOIN
    (SELECT id,twitter_username,email FROM `users`) u
    ON (ua.user_id=u.id)
;

Вот причины для рефакторирования запроса:

Причина № 1

Если вы посмотрите на встроенный стол ua, Я получаю только 30 строк, используя LIMIT. Это произойдет независимо от того, насколько велик user_actions Таблица получает. Анкет Это уже упорядочено, потому что ORDER BY timestamp DESC происходит до LIMIT.

Причина № 2

Если вы посмотрите встроенный таблицу u, оно имеет id,twitter_username,email. Анкет А id необходим для реализации соединения.

Причина № 3

я использую LEFT JOIN вместо INNER JOIN По двум (2) причинам:

  1. Сохранить порядок запроса на основе ua
  2. Отображать все действия пользователя в случае, если user_id в ua больше не существует в users столы.

Делать эти вещи заставит временные таблицы быть меньше. Тем не менее, вам все равно понадобится Реализуйте BulletPoint #3 из ответа @dtest Чтобы превзойти наличие температурных столов, приземленных на диск.

Другие советы

Ну, главная проблема заключается в том, что, поскольку на вашем запросе нет фильтрации (нет WHERE утверждение), он помещает все ряды столбцами user_actions.*, twitter_username, email во временную таблицу, чтобы сделать сортировку.

Поэтому первое, что я сделал бы, это попытаться ограничить количество строк, которые входят в ваш набор результатов. Например, я бы сказал, добавив WHERE timestamp > DATE_SUB(NOW(), INTERVAL 7 DAY) Чтобы получить результаты только в течение последних 7 дней (если это приемлемо для вашего варианта использования).

Далее я бы изменил запрос, чтобы вытащить необходимые столбцы из user_actions Чтобы уменьшить объем информации, необходимой для помещения во временную таблицу.

Теперь, когда вы можете или не могли быть удалены строки/столбцы, которые необходимо размещать во временной таблице, чтобы отсортировать, давайте посмотрим, как MySQL обрабатывает временные таблицы. Из документации по tmp_table_size переменная (акцент добавлен):

Максимальный размер внутренних временных таблиц в памяти. (Фактический предел определяется как минимум TMP_TABLE_SIZE и MAX_HEAP_TABLE_SIZE.)1 Если временная таблица в памяти превышает предел, MySQL автоматически преобразует его в таблицу MyISAM на диске.

Во -первых, позвольте мне указать о предостережении, представленном SuperScript 1: Размер временной таблицы, созданной в памяти, является минимумом любого из них tmp_table_size или же max_heap_table_size, поэтому, если вы увеличиваете один, обязательно увеличите другое.

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

Чтобы подтвердить:

  • Ограничьте количество рядов, которые вы сортируете, используя WHERE. Анкет Даже если вы делаете LIMIT, все ряды все еще помещаются во временный стол для сортировки.

  • Ограничьте количество столбцов, которые вы запрашиваете. Если они вам не нужны, не просите их.

  • Последняя среда, увеличьте размер tmp_table_size а также max_heap_table_size Если запрос увеличивает ваш Created_tmp_disk_tables переменная статуса. Кроме того, не увеличивайте это резко. Это может иметь влияние на производительность, в зависимости от вашего оборудования и количества оперативной памяти, который у вас есть на вашем сервере.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top