Это мой медленный запрос:
SELECT `products_counts`.`cid`
FROM
`products_counts` `products_counts`
LEFT OUTER JOIN `products` `products` ON (
`products_counts`.`product_id` = `products`.`id`
)
LEFT OUTER JOIN `trademarks` `trademark` ON (
`products`.`trademark_id` = `trademark`.`id`
)
LEFT OUTER JOIN `suppliers` `supplier` ON (
`products_counts`.`supplier_id` = `supplier`.`id`
)
WHERE
`products_counts`.product_id IN
(159, 572, 1075, 1102, 1145, 1162, 1660, 2355, 2356, 2357, 3236, 6471, 6472, 6473, 8779, 9043, 9095, 9336, 9337, 9338, 9445, 10198, 10966, 10967, 10974, 11124, 11168, 16387, 16689, 16827, 17689, 17920, 17938, 17946, 17957, 21341, 21352, 21420, 21421, 21429, 21544, 27944, 27988, 30194, 30196, 30230, 30278, 30699, 31306, 31340, 32625, 34021, 34047, 38043, 43743, 48639, 48720, 52453, 55667, 56847, 57478, 58034, 61477, 62301, 65983, 66013, 66181, 66197, 66204, 66407, 66844, 66879, 67308, 68637, 73944, 74037, 74060, 77502, 90963, 101630, 101900, 101977, 101985, 101987, 105906, 108112, 123839, 126316, 135156, 135184, 138903, 142755, 143046, 143193, 143247, 144054, 150164, 150406, 154001, 154546, 157998, 159896, 161695, 163367, 170173, 172257, 172732, 173581, 174001, 175126, 181900, 182168, 182342, 182858, 182976, 183706, 183902, 183936, 184939, 185744, 287831, 362832, 363923, 7083107, 7173092, 7342593, 7342594, 7342595, 7728766)
ORDER BY
products_counts.inflow ASC,
supplier.delivery_period ASC,
trademark.sort DESC,
trademark.name ASC
LIMIT
0, 3;
Среднее время запроса составляет 4,5 с в моем наборе данных, и это недопустимо.
Решения, которые я вижу:
Добавьте все столбцы из предложения заказа в products_counts
таблицу. Но у меня есть ~ 10 типов заказов в приложении, поэтому я должен создать много столбцов и индексов. Плюс products_counts
очень интенсивно обновляет / вставляет / удаляет, поэтому мне нужно немедленно обновить все столбцы, связанные с заказами (используя триггеры?).
Есть ли другое решение?
Объясните:
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | products_counts | range | product_id_supplier_id,product_id,pid_count | product_id_supplier_id | 4 | NULL | 227 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | products | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.product_id | 1 | |
| 1 | SIMPLE | trademark | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products.trademark_id | 1 | |
| 1 | SIMPLE | supplier | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.supplier_id | 1 | |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
Структура таблиц:
CREATE TABLE `products_counts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) unsigned NOT NULL,
`supplier_id` int(11) unsigned NOT NULL,
`count` int(11) unsigned NOT NULL,
`cid` varchar(64) NOT NULL,
`inflow` varchar(10) NOT NULL,
`for_delete` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `cid` (`cid`),
UNIQUE KEY `product_id_supplier_id` (`product_id`,`supplier_id`),
KEY `product_id` (`product_id`),
KEY `count` (`count`),
KEY `pid_count` (`product_id`,`count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`category_id` int(11) unsigned NOT NULL,
`trademark_id` int(11) unsigned NOT NULL,
`photo` varchar(255) NOT NULL,
`sort` int(11) unsigned NOT NULL,
`otech` tinyint(1) unsigned NOT NULL,
`not_liquid` tinyint(1) unsigned NOT NULL DEFAULT '0',
`applicable` varchar(255) NOT NULL,
`code_main` varchar(64) NOT NULL,
`code_searchable` varchar(128) NOT NULL,
`total` int(11) unsigned NOT NULL,
`slider` int(11) unsigned NOT NULL,
`slider_title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`),
KEY `category_id` (`category_id`),
KEY `trademark_id` (`trademark_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `trademarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`country_id` int(11) NOT NULL,
`sort` int(11) unsigned NOT NULL DEFAULT '0',
`sort_list` int(10) unsigned NOT NULL DEFAULT '0',
`is_featured` tinyint(1) unsigned NOT NULL,
`is_direct` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `suppliers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`code` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`delivery_period` tinyint(1) unsigned NOT NULL,
`is_default` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Информация о сервере MySQL:
mysqld Ver 5.5.45-1+deb.sury.org~trusty+1 for debian-linux-gnu on i686 ((Ubuntu))
(inflow, product_id)
?innodb_buffer_pool_size
. Обычно около 70% доступной оперативной памяти - это хорошо.Ответы:
Просмотр определений таблиц показывает, что у вас есть индексы, совпадающие для всех задействованных таблиц. Это должно привести к тому, что объединения происходят как можно быстрее в рамках
MySQL's
логики объединения.Однако сортировка из нескольких таблиц является более сложной.
В 2007 году Сергей Петруня описал 3
MySQL
алгоритма сортировки в порядке скорости поMySQL
адресу: http://s.petrunia.net/blog/?m=201407filesort()
на 1-й непостоянной таблицеfilesort()
ееИз приведенных выше определений и объединений таблиц видно, что вы никогда не получите самую быструю сортировку . Это означает, что вы будете зависеть от
filesort()
критериев сортировки, которые вы используете.Однако, если вы разработаете и используете материализованное представление, вы сможете использовать самый быстрый алгоритм сортировки.
Чтобы
MySQL 5.5
(в этом примере) увеличитьORDER BY
скорость, если вы не можетеMySQL
использовать индексы вместо дополнительной фазы сортировки, попробуйте следующие стратегии:• Увеличьте
sort_buffer_size
значение переменной.• Увеличьте
read_rnd_buffer_size
значение переменной.• Используйте меньше ОЗУ на строку, объявляя столбцы настолько большими, насколько это необходимо для хранения фактических значений. [Например, уменьшить varchar (256) до varchar (ActualLongestString)]
• Измените
tmpdir
системную переменную так, чтобы она указывала на выделенную файловую систему с большим количеством свободного места. (Другие детали предлагаются по ссылке выше.)Материализованные представления - другой подход к сортировке объединенных таблиц
Вы сослались на материализованные представления с вашим вопросом, касающимся использования триггеров. MySQL не имеет встроенной функциональности для создания материализованного представления, но у вас есть необходимые инструменты. Используя триггеры для распределения нагрузки, вы можете поддерживать материализованное представление до момента.
Материализованное представление на самом деле таблица , которая заполняется через процедурный код , чтобы построить или восстановить материализованное представление и поддерживается триггерами , чтобы сохранить данные до современных.
Поскольку вы создаете таблицу, которая будет иметь индекс , то при выполнении запроса в Materialized View может использоваться самый быстрый метод сортировки : используйте метод доступа на основе индекса, который производит упорядоченный вывод
Поскольку
MySQL 5.5
для поддержки Материализованного представления используются триггеры , вам также потребуется процесс, сценарий или хранимая процедура для создания исходного Материализованного представления .Но это, очевидно, слишком тяжелый процесс для запуска после каждого обновления базовых таблиц, в которых вы управляете данными. Именно здесь вступают в действие триггеры, чтобы обновлять данные по мере внесения изменений. Таким образом, каждый
insert
,update
иdelete
будет распространять свои изменения, используя ваши триггеры, в Материализованное представление .У организации FROMDUAL по адресу http://www.fromdual.com/ есть пример кода для поддержки материализованного представления . Поэтому вместо того, чтобы писать свои собственные образцы, я укажу вам их образцы:
http://www.fromdual.com/mysql-materialized-views
Пример 1: Построение материализованного представления
Это дает вам материализованное представление в момент обновления. Однако, поскольку у вас есть быстро движущаяся база данных, вы также хотите поддерживать этот вид как можно более актуальным.
Поэтому для затронутых таблиц базовых данных необходимо наличие триггеров для распространения изменений из базовой таблицы в таблицу с материализованным представлением . В качестве одного примера:
Пример 2. Вставка новых данных в материализованное представление
Конечно, вам также понадобятся триггеры для поддержки удаления данных из материализованного представления и обновления данных в материализованном представлении . Образцы также доступны для этих триггеров.
ПОСЛЕДНЕЕ: Как это делает сортировку соединенных таблиц быстрее?
Материализованное представление строится постоянно , как обновления сделаны к нему. Поэтому вы можете определить индекс (или индексы ), который вы хотите использовать для сортировки данных в материализованном представлении или таблице .
Если затраты на обслуживание данных не слишком велики, то вы тратите некоторые ресурсы (CPU / IO / и т. Д.) На каждое соответствующее изменение данных, чтобы поддерживать материализованное представление, и, таким образом, данные индекса являются актуальными и легко доступными. Поэтому выбор будет быстрее, так как вы:
В зависимости от ваших обстоятельств и вашего отношения к процессу в целом, вы можете захотеть перестраивать Материализованные представления каждую ночь в течение медленного периода.
источник
Здесь не так много, но я предполагаю, что основная проблема заключается в том, что вы создаете довольно большую временную таблицу и каждый раз сортируете файл на диске. Причина в том, что:
Это означает, что ваша временная таблица и файл сортировки могут быть довольно большими, так как при создании временной таблицы поля создаются с длиной MAX, а при сортировке все записи имеют длину MAX (а UTF8 составляет 3 байта на символ). Это также, вероятно, исключает использование временной таблицы в памяти. Для получения дополнительной информации см. Детали внутренних временных таблиц .
LIMIT также не приносит нам пользы, так как нам нужно материализовать и упорядочить весь набор результатов, прежде чем мы узнаем, каковы первые 3 строки.
Вы пытались переместить ваш tmpdir в файловую систему tmpfs ? Если / tmp еще не использует tmpfs (MySQL
tmpdir=/tmp
по умолчанию использует * nix), то вы можете использовать / dev / shm напрямую. В вашем файле my.cnf:Тогда вам нужно будет перезапустить mysqld.
Это может иметь огромное значение. Если вы, вероятно, испытываете нехватку памяти в системе, вы, возможно, захотите ограничить размер (обычно linux distros cap tmpfs на 50% от общего объема ОЗУ по умолчанию), чтобы избежать выгрузки сегментов памяти на диск, или даже хуже ситуации с OOM . Вы можете сделать это, отредактировав строку в
/etc/fstab
:Вы можете изменить его размер "онлайн" тоже. Например:
Вы также можете перейти на MySQL 5.6 - с производительными подзапросами и производными таблицами - и поэкспериментировать с запросом. Я не думаю, что мы увидим большие победы на этом пути из того, что я вижу.
Удачи!
источник