Что я могу сделать, чтобы сделать этот SQL более эффективным? (таблица имеет 850 тыс. строк) - PullRequest
0 голосов
/ 17 октября 2011

Я запускаю медленный журнал на mysql из-за производственных проблем, и запрос номер один:

select * from feeditem feeditem0_ where feeditem0_.importance=0 and feeditem0_.company_id=N limit 21;

Я сократил select (N - это идентификатор FK), так как он генерируется из hibernate и просто выбирает все поля в этой таблице. Когда я делаю MySQL объяснение, я получаю:

explain select * from feeditem feeditem0_ where feeditem0_.importance=0 and    feeditem0_.company_id=5045 limit 21 \G;;
*************************** 1. row ***************************
       id: 1
select_type: SIMPLE
    table: feeditem0_
     type: index_merge
possible_keys: FKF49961B13D5FD8EF,importance
      key: FKF49961B13D5FD8EF,importance
  key_len: 9,5
      ref: NULL
     rows: 2422
    Extra: Using intersect(FKF49961B13D5FD8EF,importance); Using where

В этой таблице около 850 тыс. Строк.

Схема:

CREATE TABLE `feeditem` (
`DTYPE` varchar(31) NOT NULL,
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`dateCreated` datetime DEFAULT NULL,
`endSentance` varchar(255) DEFAULT NULL,
`importance` int(11) DEFAULT NULL,
`startSentance` varchar(255) DEFAULT NULL,
`summary` varchar(255) DEFAULT NULL,
`summaryComplete` bit(1) NOT NULL,
`targetId` bigint(20) DEFAULT NULL,
`targetSentance` text,
`type` varchar(255) NOT NULL,
`hasRead` bit(1) DEFAULT NULL,
`teamProject_id` bigint(20) DEFAULT NULL,
`user_id` bigint(20) DEFAULT NULL,
`usertoread_id` bigint(20) DEFAULT NULL,
`contentType` varchar(255) DEFAULT NULL,
`company_id` bigint(20) DEFAULT NULL,
`updated` int(1) unsigned DEFAULT NULL,
`feedType` varchar(255) DEFAULT NULL,
`extraInfo` varchar(255) DEFAULT NULL,
`extraTargetId` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FKF49961B1B74A2DA5` (`user_id`),  
KEY `FKF49961B17CE9E5EF` (`teamProject_id`),
KEY `FKF49961B137B7D1B4` (`usertoread_id`),
KEY `FKF49961B13D5FD8EF` (`company_id`),
KEY `importance` (`importance`),
KEY `dateCreated` (`dateCreated`)
) ENGINE=InnoDB AUTO_INCREMENT=956498 DEFAULT CHARSET=utf8

Можно ли как-нибудь остановить сканирование 2400 нечетных строк? Это сводка из медленного журнала (с использованием mysqlsla):

Count         : 61  (53.98%)
Time          : 523 s total, 8.57377 s avg, 6 s to 19 s max  (54.03%)
95% of Time : 456 s total, 8 s avg, 6 s to 14 s max
Lock Time (s) : 0 total, 0 avg, 0 to 0 max  (0.00%)
95% of Lock : 0 total, 0 avg, 0 to 0 max
Rows sent     : 34 avg, 21 to 51 max  (38.69%)
Rows examined : 3.49k avg, 40 to 8.89k max  (0.00%)
Users         :100.00% (61) of query, 100.00% (113) of all users

спасибо

ОБНОВЛЕНИЕ 1: Я добавил еще 2 индекса столбца (называемый feedquery), но, похоже, оптимизатор решил не использовать индекс:

mysql> explain select id from feeditem feeditem0_ where feeditem0_.importance=0 and    feeditem0_.company_id=5045  \G;
*************************** 1. row ***************************
       id: 1
  select_type: SIMPLE
    table: feeditem0_
     type: index_merge
possible_keys: FKF49961B13D5FD8EF,importance,feedquery
      key: FKF49961B13D5FD8EF,feedquery
  key_len: 9,14
      ref: NULL
     rows: 2753
    Extra: Using intersect(FKF49961B13D5FD8EF,feedquery); Using where; Using index

Если я игнорирую индекс:

 explain select id from feeditem feeditem0_ ignore index (FKF49961B13D5FD8EF) where feeditem0_.importance=0 and  feeditem0_.company_id=5045  \G;
 *************************** 1. row ***************************
       id: 1
 select_type: SIMPLE
    table: feeditem0_
     type: ref
 possible_keys: importance,feedquery
      key: feedquery
  key_len: 14
      ref: const,const
     rows: 8496
    Extra: Using where; Using index

Таблица:

CREATE TABLE `feeditem` (
.....
PRIMARY KEY  (`id`),
KEY `FKF49961B1B74A2DA5` (`user_id`),
 KEY `FKF49961B17CE9E5EF` (`teamProject_id`),
KEY `FKF49961B137B7D1B4` (`usertoread_id`),
KEY `FKF49961B13D5FD8EF` (`company_id`),
KEY `importance` (`importance`),
KEY `dateCreated` (`dateCreated`),
KEY `feedquery` (`importance`,`company_id`)
) ENGINE=InnoDB AUTO_INCREMENT=999359 DEFAULT CHARSET=utf8 

ОБНОВЛЕНИЕ 2: @Salman A

SHOW profile;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000342 |
| checking permissions | 0.000024 |
| Opening tables       | 0.000053 |
| System lock          | 0.000027 |
| init                 | 0.000166 |
| optimizing           | 0.000068 |
| statistics           | 0.012869 |
| preparing            | 0.000202 |
| executing            | 0.000008 |
| Sending data         | 0.332767 |
| end                  | 0.000022 |
| query end            | 0.000009 |
| closing tables       | 0.000016 |
| freeing items        | 0.000040 |
| logging slow query   | 0.000005 |
| cleaning up          | 0.000014 |
+----------------------+----------+

ibdata1 составляет около 1,5 ГБ

Ответы [ 3 ]

5 голосов
/ 17 октября 2011

Общий ответ:

  1. Не используйте SELECT * , если вам абсолютно не нужны все столбцы.Выберите только нужные вам столбцы.
  2. Добавьте предложение ORDER BY или LIMIT не будет иметь особого смысла.
  3. Создайте составной (т.е. многостолбцовый) индекс, охватывающий
    • и importance и company_id
    • поле, которое вы хотите ORDER BY, в ожидаемом порядке
    • любые дополнительные поля, которые вы хотите SELECT вернуть (вместо *)

Таким образом, механизм БД может искать прямые совпадения с вашим поиском с помощью одной операции поиска по индексу и сортировки покрытия, а также дополнительных столбцовпрямо из индекса.Индекс содержит копию всех столбцов, которые он охватывает;если все запрошенные данные находятся в индексе, нет необходимости переходить через фактическую таблицу.Это повысит эффективность запросов.

Имейте в виду, что это обмен скоростью на пространство.Каждый столбец, который вы добавляете в индекс, будет увеличивать свой физический размер, поэтому выбирайте разумно.

EDIT 1: Кроме того, индекс влияет на скорость операций записи - запросы INSERT, UPDATE и DELETE будут немного медленнееиз-за поддержки индекса - в обмен на то, что SELECT быстрее.(спасибо за комментарий, @ Thor84no)

РЕДАКТИРОВАТЬ 2: Если этот запрос является основным шаблоном использования для таблицы и , таблица не сильно меняется (этот момент очень важен!)можно подумать о создании кластерного индекса.Кластерный индекс указывает на физическую сортировку базовой таблицы, он не существует , кроме базовой таблицы, как другие индексы.Каждый раз, когда вы изменяете определение кластеризованного индекса или добавляете / удаляете строки «между» существующими записями, фактические данные переупорядочиваются физически, т. Е. На диске - дорогая операция, которую вы хотите избежать.

Иногда это может быть разумно, но в вашем случае, скорее всего, это не так.Если ваша таблица представляет собой таблицу журналов некоторых видов, сохраняйте кластеризованный индекс с автоматически увеличивающимся идентификатором.

2 голосов
/ 17 октября 2011

Стандартный select x from y where z (то есть то, что у вас есть, только с несколькими условными обозначениями) - один из самых эффективных запросов, которые вы можете выполнить. Единственное, что вы действительно можете добавить - это индекс, включающий все столбцы, к которым вы обращаетесь; однако это повлияет на производительность при записи в эту таблицу. (Так же, как и высокий один раз, чтобы заполнить указанный индекс в первый раз).

Если вы знаете, что один из столбцов в вашем запросе будет ограничивать результаты гораздо больше, чем другие, вы можете пойти на компромисс и добавить индекс только для этого запроса. Например. если поиск только строк с company_id = x гарантирует, что у вас останется всего несколько строк для фильтрации, то предпочтительнее иметь индекс только для этого столбца.

1 голос
/ 17 октября 2011

По сути, поскольку каждый из ваших индексов включает только одно из двух полей, на которые есть ссылки в вашем предложении WHERE, движку необходимо извлечь записи, удовлетворяющие первой части, и записи, удовлетворяющие второй части (используя индексы «важность» и«FKF49961B13D5FD8EF», соответственно).

Простое правило генерации индекса заключается в том, что вы хотите, чтобы индекс выглядел точно так же, как поля в предложении WHERE.Для этого вы можете создать индекс, используя «важность» и «company_id» в этом порядке.Это выберет точно совпадающие строки, и строки 2,4 КБ больше не будут сканироваться.

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