ВЫБОР неиндексированного столбца увеличивает «отправку данных» в 25 раз - почему и как улучшить? - PullRequest
2 голосов
/ 19 июня 2011

Учитывая эту таблицу на локальном Экземпляре MySQL 5.1 с отключенным кэшированием запроса:

show create table product_views\G
*************************** 1. row ***************************
       Table: product_views
Create Table: CREATE TABLE `product_views` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `dateCreated` datetime NOT NULL,
  `dateModified` datetime DEFAULT NULL,
  `hibernateVersion` bigint(20) DEFAULT NULL,
  `brandName` varchar(255) DEFAULT NULL,
  `mfrModel` varchar(255) DEFAULT NULL,
  `origin` varchar(255) NOT NULL,
  `price` float DEFAULT NULL,
  `productType` varchar(255) DEFAULT NULL,
  `rebateDetailsViewed` tinyint(1) NOT NULL,
  `rebateSearchZipCode` int(11) DEFAULT NULL,
  `rebatesFoundAmount` float DEFAULT NULL,
  `rebatesFoundCount` int(11) DEFAULT NULL,
  `siteSKU` varchar(255) DEFAULT NULL,
  `timestamp` datetime NOT NULL,
  `uiContext` varchar(255) DEFAULT NULL,
  `siteVisitId` bigint(20) NOT NULL,
  `efficiencyLevel` varchar(255) DEFAULT NULL,
  `siteName` varchar(255) DEFAULT NULL,
  `clicks` varchar(1024) DEFAULT NULL,
  `rebateFormDownloaded` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `siteVisitId` (`siteVisitId`,`siteSKU`),
  KEY `FK52C29B1E3CAB9CC4` (`siteVisitId`),
  KEY `rebateSearchZipCode_idx` (`rebateSearchZipCode`),
  KEY `FIND_UNPROCESSED_IDX` (`siteSKU`,`siteVisitId`,`timestamp`),
  CONSTRAINT `FK52C29B1E3CAB9CC4` FOREIGN KEY (`siteVisitId`) REFERENCES `site_visits` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=32909504 DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

Этот запрос занимает ~ 3 с:

    SELECT pv.id, pv.siteSKU
      FROM product_views pv 
CROSS JOIN site_visits sv 
     WHERE pv.siteVisitId = sv.id 
       AND pv.siteSKU = 'foo' 
       AND sv.siteId = 'bar' 
       AND sv.postProcessed = 1 
       AND pv.timestamp >= '2011-05-19 00:00:00' 
       AND pv.timestamp < '2011-06-18 00:00:00';

Но этот (неиндексированный столбец, добавленный в SELECT), занимает ~ 65 с:

    SELECT pv.id, pv.siteSKU, pv.hibernateVersion 
      FROM product_views pv 
CROSS JOIN site_visits sv 
     WHERE pv.siteVisitId = sv.id 
       AND pv.siteSKU = 'foo' 
       AND sv.siteId = 'bar' 
       AND sv.postProcessed = 1 
       AND pv.timestamp >= '2011-05-19 00:00:00' 
       AND pv.timestamp < '2011-06-18 00:00:00';

Ничто в предложениях 'where' или 'from' не отличается.Все дополнительное время тратится на «отправку данных»:

mysql> show profile for query 1;
+--------------------+-----------+
| Status             | Duration  |
+--------------------+-----------+
| starting           |  0.000155 |
| Opening tables     |  0.000029 |
| System lock        |  0.000007 |
| Table lock         |  0.000019 |
| init               |  0.000072 |
| optimizing         |  0.000032 |
| statistics         |  0.000316 |
| preparing          |  0.000034 |
| executing          |  0.000002 |
| Sending data       | 63.530402 |
| end                |  0.000044 |
| query end          |  0.000005 |
| freeing items      |  0.000091 |
| logging slow query |  0.000002 |
| logging slow query |  0.000109 |
| cleaning up        |  0.000004 |
+--------------------+-----------+
16 rows in set (0.00 sec)

Я понимаю, что использование неиндексированного столбца в выражении where может замедлить ход событий, но почему здесь?Что можно сделать, чтобы улучшить последний случай - учитывая, что я действительно хочу ВЫБРАТЬ (*) из product_views?

ОБЪЯСНИТЕ Выходные данные

explain extended select pv.id, pv.siteSKU from product_views pv cross join site_visits sv where pv.siteVisitId=sv.id and pv.siteSKU='foo' and sv.sit eId='bar' and sv.postProcessed=1 and pv.timestamp>='2011-05-19 00:00:00' and pv.timestamp<'2011-06-18 00:00:00';
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+--------------------------+ | id | select_type | table | type   | possible_keys                          | key                  | key_len | ref | rows  | filt ered | Extra            |
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+--------------------------+ |  1 | SIMPLE      | pv    | ref    | siteVisitId,FK52C29B1E3CAB9CC4,FIND_UNPROCESSED_IDX | FIND_UNPROCESSED_IDX | 258     | const                | 41810 |   10
0.00 | Using where; Using index | |  1 | SIMPLE      | sv    | eq_ref | PRIMARY,post_processed_idx             | PRIMARY              | 8       | clabs.pv.siteVisitId |     1 |   10
0.00 | Using where              |
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+--------------------------+ 2 rows in set, 1 warning (0.00 sec)

mysql> explain extended select pv.id, pv.siteSKU, pv.hibernateVersion from product_views pv cross join site_visits sv where pv.siteVisitId=sv.id and pv.siteSKU= 'foo' and sv.siteId='bar' and sv.postProcessed=1 and pv.timestamp>='2011-05-19 00:00:00' and pv.timestamp<'2011-06-18 00:00:00';
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+-------------+ | id | select_type | table | type   | possible_keys                          | key                  | key_len | ref | rows  | filt ered | Extra       |
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+-------------+ |  1 | SIMPLE      | pv    | ref    | siteVisitId,FK52C29B1E3CAB9CC4,FIND_UNPROCESSED_IDX | FIND_UNPROCESSED_IDX | 258     | const                | 41810 |   10
0.00 | Using where | |  1 | SIMPLE      | sv    | eq_ref | PRIMARY,post_processed_idx             | PRIMARY              | 8       | clabs.pv.siteVisitId |     1 |   10
0.00 | Using where |
+----+-------------+-------+--------+-----------------------------------------------------+----------------------+---------+----------------------+-------+-----
-----+-------------+ 2 rows in set, 1 warning (0.00 sec)

ОБНОВЛЕНИЕ1: Разделение на 2 запроса приводит к сокращению общего времени до диапазона ~ 30 с.

Не знаю почему, но разбиение последнего запроса на следующее уменьшает лат.от 65 до ~ 30 с:

1) SELECT pv.id .... // from, где предложения такие же, как и выше

2) SELECT * FROM product_views где id in (idList);// idList

UPDATE2: РАЗМЕР ТАБЛИЦЫ

  • таблица имеет порядок 10M строк
  • запрос возвращает около 3k строк

Ответы [ 3 ]

4 голосов
/ 19 июня 2011

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

Что касается улучшения, сколько строк в таблице, сколько запросов возвращается и каков размер вашего пула буферов, сколько оперативной памяти доступно.и т.д.?

1 голос
/ 19 июня 2011

Из того, что я прочитал о шоу-профиле, «отправка данных» является частью процесса выполнения и почти не связана с отправкой реальных данных клиенту.Вы можете посмотреть на этот поток
Также, mysql docs говорит о "отправке данных":

Поток читает и обрабатывает строкидля оператора SELECT и отправки данных клиенту.Поскольку операции, происходящие во время этого состояния, имеют тенденцию выполнять большие объемы доступа к диску (чтения), это часто является самым длительным состоянием в течение времени жизни данного запроса.

По моему мнению, mysql лучшене смешивайте вместе «чтение и обработку строк для оператора SELECT» и «отправку данных» в одном состоянии, особенно в состоянии, называемом «отправка» данных », что вызывает много путаницы.

0 голосов
/ 19 июня 2011

Я совсем не знаю, что такое MySQL, но объяснение Дархазера мне кажется победителем. Когда добавляется неиндексированное поле, необходимо извлечь всю строку. И ваши ряды очень в ширину. Я не могу точно сказать по именам, как (если вообще) это денормализовано, но я подозреваю, что это так. site name и site sku пахнут, как будто они принадлежат таблице поиска site с FK. rebates found amount и rebates found count звучат как статистика, которая должна поступать из объединения в отдельную таблицу product rebate. и т.д.

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