Оптимизация запроса Mysql для больших наборов данных - PullRequest
2 голосов
/ 21 февраля 2020

Я относительно новичок в MySQL, и с тех пор я пытаюсь улучшить запрос, который выполняется для большой таблицы (> 70 млн. Строк). Хорошие новости, я получил правильный результат. К сожалению, это занимает около 7 минут для расчета, что, на мой взгляд, способ долго. Я искал способы улучшить запросы и придумал многостолбцовый индекс таблицы, но теперь я как-то застрял и не знаю, как еще улучшить.

Таблица состоит из 5 столбцов, все они представляют интерес. Столбцы state , cur , min и max являются INT. В столбце sample хранятся некоторые имена, которые несколько раз в этом столбце. В каждой строке хранится информация одного состояния одного образца. Каждый образец имеет миллионы разных состояний. Таблица выглядит следующим образом:

samples state   cur min max

Sample1 1       58  12  110
Sample1 0       8   12  110
Sample2 1       70  150 190
Sample4 2       10  1   20
Sample3 2       80  50  70
Sample6 2       3   1   10
Sample5 0       18  21  90
Sample5 1       22  21  90
.
.
.

Теперь я хочу выполнить некоторую статистику для таблицы: Сколько раз будет состояние с cur между мин и макс . Я хочу получить эти цифры для каждого образца. Кроме того, я также хочу вычислить относительное количество состояний 1, 2 и 3. для каждого образца.

Полученная таблица выглядит следующим образом:

        total amount    state 0     state 1 state 2 state 0 %   state 1 %   state 2 %

Sample1 14504366        13199105    961629  343632  91.0009     6.6299      2.3692
Sample2 13873909        12628523    926846  318540  91.0235     6.6805      2.2960
Sample3 10919017        9231997     828767  858253  84.5497     7.5901      7.8602
Sample4 10148540        8604527     768220  775793  84.7859     7.5698      7.6444
Sample5 14130796        12382867    1078724 669205  87.6304     7.6339      4.7358
Sample6 11307051        9947652     871388  488011  87.9774     7.7066      4.3160

Я получил результат по используя следующий код:

# state, cur, min and max are INT
# "samples" is Varchar40

# build the index
ALTER TABLE data_table ADD INDEX `index_name` (state, cur, min, max, samples);

# query
SELECT t.samples, 
COUNT(t.state) AS "total amount", 
amount_0 AS "state 0", 
amount_1 AS "state 1", 
amount_2 AS "state 2", 
amount_0 / COUNT(t.state) * 100 AS "state 0 %", 
amount_1 / COUNT(t.state) * 100 AS "state 1 %", 
amount_2 / COUNT(t.state) * 100 AS "state 2 %"
FROM data_table t
JOIN
(
   SELECT samples, COUNT(state) as amount_0
   FROM data_table
   WHERE state = 0 AND cur > min + 15 AND cur < max -20
   GROUP BY samples
) tmp0 ON tmp0.samples = t.samples
JOIN
(
   SELECT samples, COUNT(state) as amount_1
   FROM data_table
   WHERE state = 1 AND cur > min + 15 AND cur < max -20
   GROUP BY samples
) tmp1 ON tmp1.samples = t.samples
JOIN
(
   SELECT samples, COUNT(state) as amount_2
   FROM data_table
   WHERE state = 2 AND cur > min + 15 AND cur < max -20
   GROUP BY samples
) tmp2 ON tmp2.samples = t.samples
WHERE cur > min + 15 AND cur < max -20
GROUP BY t.samples;

EXPLAIN возвращает следующий вывод:

id  select_type table       partitions  type        possible_keys   key             key_len ref             rows        filtered    Extra   
1   PRIMARY     t           [NULL]      index       index_name      index_name      143     [NULL]          73647812    11.11       Using where; Using index; Using temporary; Using filesort
1   PRIMARY     <derived4>  [NULL]      ref         <auto_key0>     <auto_key0>     123     db.t.Sample     10          100         [NULL]
1   PRIMARY     <derived2>  [NULL]      ref         <auto_key0>     <auto_key0>     123     db.t.Sample     10          100         [NULL]
1   PRIMARY     <derived3>  [NULL]      ref         <auto_key0>     <auto_key0>     123     db.t.Sample     10          100         [NULL]
4   DERIVED     data_table  [NULL]      ref         index_name      index_name      5       const           7547114     11.11       Using where; Using index; Using temporary; Using filesort
3   DERIVED     data_table  [NULL]      ref         index_name      index_name      5       const           15150796    11.11       Using where; Using index; Using temporary; Using filesort
2   DERIVED     data_table  [NULL]      ref         index_name      index_name      5       const           36823906    11.11       Using where; Using index; Using temporary; Using filesort

Я думаю, что большой проблемой являются детали JOIN , где, вероятно, индексы теряются. Вторым этапом, требующим много времени, может быть - если я правильно понимаю - использование WHERE , которое приведет к объединению всего, а затем удалит строки, которые не соответствуют регистру, на втором этапе. Это отнимает много времени и этого можно избежать, используя ON вместо WHERE (?). Моя проблема в том, что я не знаю, как реализовать обходной путь. Поэтому я надеюсь, что вы можете помочь мне.

1 Ответ

1 голос
/ 21 февраля 2020

Можно избежать некоторых подзапросов, используя условное агрегирование

SELECT t.samples, 
COUNT(t.state) AS "total amount", 
amount_0 AS "state 0", 
amount_1 AS "state 1", 
amount_2 AS "state 2", 
amount_0 / COUNT(t.state) * 100 AS "state 0 %", 
amount_1 / COUNT(t.state) * 100 AS "state 1 %", 
amount_2 / COUNT(t.state) * 100 AS "state 2 %"
FROM data_table t
INNER JOIN (
   SELECT samples
    , COUNT(case when state = 0 then 1 else null end) as amount_0
    , COUNT(case when state = 1 then 1 else null end) as amount_1
    , COUNT(case when state = 2 then 1 else null end) as amount_0
   FROM data_table
   WHERE  cur > min + 15 AND cur < max -20
   GROUP BY samples
) tmp ON tmp.samples = t.samples
WHERE cur > min + 15 AND cur < max -20
GROUP BY t.samples;
...