MySQL count (*), Group BY и INNER JOIN - PullRequest
       27

MySQL count (*), Group BY и INNER JOIN

2 голосов
/ 13 июля 2011

У меня действительно плохое время с запросом на MySQL 5.1.Я упростил 2 таблицы, в которых я создал JOIN:

CREATE TABLE  `jobs` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`title` VARCHAR( 255 ) NOT NULL
) ENGINE = MYISAM ;

AND

CREATE TABLE `jobsCategories` (
 `jobID` int(11) NOT NULL,
 `industryID` int(11) NOT NULL,
 KEY `jobID` (`jobID`),
 KEY `industryID` (`industryID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

Запрос прост:

SELECT count(*) as nb,industryID 
FROM  jobs J 
INNER JOIN jobsCategories C ON C.jobID=J.id 
GROUP BY industryID 
ORDER BY nb DESC;

Я получил около 150000 записейв таблицу заданий и 350000 записей в таблицу jobsCategories, и у меня есть 30 отраслей;

Выполнение запроса занимает приблизительно 50 секунд !!!

У вас есть идеи, почему он занимаетпока?Как я мог оптимизировать структуру этой базы данных?Профилирование запроса показывает, что 99% времени выполнения тратится на копирование в таблицы tmp.

EXPLAIN <query> gives me : 


*************************** 1. row ***************************
       id: 1
select_type: SIMPLE
    table: J
     type: index
possible_keys: PRIMARY
      key: PRIMARY
  key_len: 4
      ref: NULL
     rows: 178950
    Extra: Using index; Using temporary; Using filesort
*************************** 2. row ***************************
       id: 1
 select_type: SIMPLE
    table: C
     type: ref
possible_keys: jobID
      key: jobID
  key_len: 8
      ref: J.id
     rows: 1
    Extra: Using where
2 rows in set (0.00 sec)

О памяти:

free -m  : 

total       used       free     shared    buffers     cached
Mem:          2011       1516        494          0          8       1075
-/+ buffers/cache:        433       1578
Swap:         5898        126       5772

С ИНДЕКСОМ СИЛЫ, предложенным ниже

select count(*) as nb, industryID 
from 
    jobs J 
    inner join jobsCategories C force index (industryID) on (C.jobID = J.id )
group by industryID 
order by nb DESC;

SHOW PROFILE;

дает мне:

+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000095 |
| Opening tables       | 0.000014 |
| System lock          | 0.000008 |
| Table lock           | 0.000007 |
| init                 | 0.000032 |
| optimizing           | 0.000011 |
| statistics           | 0.000032 |
| preparing            | 0.000016 |
| Creating tmp table   | 0.000031 |
| executing            | 0.000003 |
| Copying to tmp table | 3.301305 |
| Sorting result       | 0.000028 |
| Sending data         | 0.000024 |
| end                  | 0.000003 |
| removing tmp table   | 0.000009 |
| end                  | 0.000004 |
| query end            | 0.000003 |
| freeing items        | 0.000029 |
| logging slow query   | 0.000003 |
| cleaning up          | 0.000003 |
+----------------------+----------+

Я думаю, мой RAM (2Gb) недостаточно велик.Как я могу быть уверен, что это так?

Ответы [ 3 ]

4 голосов
/ 13 июля 2011

Во-первых, я думаю, что вам не нужно присоединяться к таблице jobs , чтобы получить тот же результат (если у вас нет данных для мусора в таблице jobsCategories ):

select count(*) as nb, industryID 
from jobsCategories
group by industryID 
order by nb DESC;

В противном случае вы можете попытаться форсировать индекс для industryID :

select count(*) as nb, industryID 
from 
    jobs J 
    inner join jobsCategories C force index (industryID) on (C.jobID = J.id )
group by industryID 
order by nb DESC;
0 голосов
/ 13 июля 2011

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

select IndustryID,
       count(*) JobsPerIndustry
   from JobCategories
   group by IndustryID

РЕДАКТИРОВАТЬ ЗА КОММЕНТАРИЙ / ОБРАТНАЯ СВЯЗЬ ...

Это определенно имеет значение ... добавление критерия, связанного с заданием ... Убедитесь, что в вашей таблице заданий есть индекс элемента, который вы хотите разрешить на основе ограничения ... Затем выполните аналогичный запрос, как у вас изначально , Убедитесь, что ваша таблица заданий имеет индекс CountryID.

SELECT
      count(*) as nb,
      industryID 
   FROM  jobs J 
      JOIN jobsCategories C 
         ON J.ID = C.jobID
   WHERE 
      J.countryID=1234
   GROUP BY 
      industryID 
   ORDER BY 
      nb DESC;
0 голосов
/ 13 июля 2011

измените ваши таблицы на InnoDB =) InnoDB хорошо управляет большими таблицами и COUNT (*), чтобы сделать это быстрее

http://www.mysqlperformanceblog.com/2009/01/12/should-you-move-from-myisam-to-innodb/

Удачи

РЕДАКТИРОВАТЬ: после тестирования, кажется, что MyISAM быстрее, чем InnoDB, когда используется COUNT(*), когда нет условия WHERE:

http://www.mysqlperformanceblog.com/2006/12/01/count-for-innodb-tables/

в любом случае, яВы проверили ваш точный запрос, имитируя таблицы, которые у вас есть (150k Jobs и 300k JobsCategories) с использованием таблиц MyISAM, и это заняло 1,5 секунды, так что, возможно, ваша проблема в другом месте ... это все, что я могу вам сказать = P

...