Запрос MySQL: долгое время, проведенное в состоянии «инициализация»

Я выполняю обновление таблицы MySQL (механизм myisam), которая, согласно профилировщику, проводит чрезмерное количество времени в состоянии «инициализация»:

mysql> show profile for query 2;
+----------------------+-----------+
| Status               | Duration  |
+----------------------+-----------+
| starting             |  0.000057 |
| checking permissions |  0.000006 |
| Opening tables       |  0.000020 |
| System lock          |  0.000007 |
| Table lock           |  0.000005 |
| init                 | 21.911657 |
| Updating             |  0.002363 |
| end                  |  0.000009 |
| query end            |  0.000004 |
| freeing items        |  0.000051 |
| logging slow query   |  0.000003 |
| logging slow query   |  0.000002 |
| cleaning up          |  0.000005 |
+----------------------+-----------+

Запрос выглядит следующим образом:

mysql> update my_table
    -> set rank = 
    ->    greatest(
    ->       @rank := if(@score = score, @rank, @rank + 1),
    ->       least(0, @score := score)
    ->    )
    -> where game=7 and zone=11 and ladder=2
    -> order by score
    -> limit 100;

Query OK, 100 rows affected (21.92 sec)
Rows matched: 100  Changed: 100  Warnings: 0

У меня есть составной индекс для всех столбцов, перечисленных в предложениях «где» и «упорядочить по» (см. индекс с именем «zone_lad_score» ниже):

mysql> show indexes from my_table;
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| Table              | Non_unique | Key_name        | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| my_table           |          1 | indx_e          |            1 | col_e        | A         |     2937401 |     NULL | NULL   |      | BTREE      |         |
| my_table           |          1 | zone_score      |            1 | zone         | A         |         217 |     NULL | NULL   |      | BTREE      |         |
| my_table           |          1 | zone_score      |            2 | score        | A         |    23499213 |     NULL | NULL   | YES  | BTREE      |         |
| my_table           |          1 | zone_d_score    |            1 | zone         | A         |         217 |     NULL | NULL   |      | BTREE      |         |
| my_table           |          1 | zone_d_score    |            2 | col_d        | A         |      123355 |     NULL | NULL   | YES  | BTREE      |         |
| my_table           |          1 | zone_d_score    |            3 | score        | A         |    46998427 |     NULL | NULL   | YES  | BTREE      |         |
| my_table           |          1 | zone_lad_score  |            1 | zone         | A         |         217 |     NULL | NULL   |      | BTREE      |         |
| my_table           |          1 | zone_lad_score  |            2 | ladder       | A         |         868 |     NULL | NULL   | YES  | BTREE      |         |
| my_table           |          1 | zone_lad_score  |            3 | score        | A         |    23499213 |     NULL | NULL   | YES  | BTREE      |         |
+--------------------+------------+-----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+

У меня также есть таблица, разделенная на «игру», всего 10 разделов. Всего в таблице около 47 миллионов записей. Определение таблицы следующее:

my_table | CREATE TABLE `my_table` (
  `col_e` bigint(20) NOT NULL,
  `zone` bigint(20) NOT NULL,
  `score` int(11) DEFAULT NULL,
  `game` tinyint(4) DEFAULT NULL,
  `ladder` tinyint(4) DEFAULT NULL,
  `col_d` int(11) DEFAULT NULL,
  `rank` int(11) DEFAULT NULL,
  KEY `indx_e` (`col_e`),
  KEY `zone_score` (`zone`,`score`),
  KEY `zone_d_score` (`zone`,`col_d`,`score`),
  KEY `zone_lad_score` (`zone`,`ladder`,`score`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
/*!50100 PARTITION BY LIST (game)
(PARTITION p1 VALUES IN (1) ENGINE = MyISAM,
 PARTITION p2 VALUES IN (2) ENGINE = MyISAM,
 PARTITION p3 VALUES IN (3) ENGINE = MyISAM,
 PARTITION p4 VALUES IN (4) ENGINE = MyISAM,
 PARTITION p5 VALUES IN (5) ENGINE = MyISAM,
 PARTITION p6 VALUES IN (6) ENGINE = MyISAM,
 PARTITION p7 VALUES IN (7) ENGINE = MyISAM,
 PARTITION p8 VALUES IN (8) ENGINE = MyISAM,
 PARTITION p9 VALUES IN (9) ENGINE = MyISAM,
 PARTITION p10 VALUES IN (10) ENGINE = MyISAM) */

Теперь, согласно документации MySQL (http://dev.mysql.com/doc/refman/5.0/en/general-thread-states.html), действия в состоянии «init» включают «очистку двоичного журнала, журнала InnoDB и некоторую очистку кэша запросов». операции». Хорошо... так как я не использую InnoDB, это не похоже на то, что должно занять очень много времени.

Думаю, мне интересно, почему это обновление, которое должно использовать индекс и затрагивает только 100 записей, занимает так много времени? Что конкретно удерживает его в состоянии «инициализация» так долго? Если я выполню выбор в записях таргетинга (выберите * из my_table, где игра = 7, зона = 11 и лестница = 2 в порядке ограничения счета 100), он возвращается почти мгновенно. Выполнение аналогичных обновлений в этой таблице (с использованием индекса zone_d_score) занимает меньше секунды. Что может замедлять работу этого конкретного обновления?

Изменить: добавлено определение таблицы, полный список всех индексов в рассматриваемой таблице и переименованные столбцы, чтобы упростить отслеживание.

Изменить 2. Вот объяснение запроса, наиболее близкого к обновлению:

mysql> explain select * from my_table where game=7 and zone=11 and ladder=2 order by score limit 100;
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+
| id | select_type | table              | type | possible_keys                                  | key             | key_len | ref         | rows  | Extra       |
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+
|  1 | SIMPLE      | my_table           | ref  | zone_score,zone_d_score,zone_lad_score         | zone_lad_score  | 10      | const,const | 53952 | Using where |
+----+-------------+--------------------+------+------------------------------------------------+-----------------+---------+-------------+-------+-------------+
1 row in set (0.00 sec)

person odonnellt    schedule 16.08.2010    source источник
comment
Попробуйте создать составной индекс для col_a, col_b, col_c. Обычно mysql может использовать только 1 индекс для каждой таблицы в запросе, поэтому вы не получите всех преимуществ от наличия 3 отдельных индексов.   -  person nos    schedule 16.08.2010
comment
Показанный выше индекс является составным индексом согласно столбцу seq_in_index команды show indexes. Он был создан с помощью: создать индекс my_index на my_table (col_b, col_c, оценка);   -  person odonnellt    schedule 16.08.2010
comment
Можете ли вы опубликовать EXPLAIN для соответствующего оператора SELECT, чтобы увидеть, какие индексы на самом деле используются в предложениях where/order?   -  person Konerak    schedule 16.08.2010
comment
сколько строк у вас есть в вашей базе данных? после ~ 50000 он будет замедляться из-за слишком большого количества логики, включая ORDER BY ... я бы сказал, что init анализирует функции greater и least ...   -  person Talvi Watia    schedule 07.09.2010


Ответы (2)


После еще нескольких экспериментов я добавил в таблицу индекс, который также включает столбец, по которому я разделил таблицу:

CREATE INDEX game_zone_ladder_score ON my_table(game,zone,ladder,score)

и вдруг ОБНОВЛЕНИЕ работает намного лучше (менее секунды). Я ожидал, что UPDATE будет использовать преимущества разделов так же, как это делают SELECT, но, по-видимому, нет.

Все равно хотелось бы знать, что именно MySQL делает в состоянии «инициализации» во время ОБНОВЛЕНИЙ и/или почему ОБНОВЛЕНИЯ не учитывают разделы.

person odonnellt    schedule 16.08.2010

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

http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitions.html

person Joshua Martell    schedule 28.08.2010
comment
Верно, но только если ваша таблица действительно имеет первичный или уникальный ключ. Если это не так, как в случае с моей таблицей, вы можете разбить ее на другой столбец. - person odonnellt; 28.08.2010