Pergunta

Eu tive todas as minhas tabelas em myisam mas o bloqueio em nível de tabela foi começando a me matar quando eu tinha longa duração trabalhos de atualização. Eu converti meus tabelas primárias até InnoDB e agora muitos dos meus consultas estão demorando mais de 1 minuto para concluir onde estavam quase instantânea em MyISAM. Eles geralmente estão presos na etapa Sorting result. Eu fiz algo errado?

Por exemplo:

SELECT * FROM `metaward_achiever` 
 INNER JOIN `metaward_alias` ON (`metaward_achiever`.`alias_id` = `metaward_alias`.`id`) 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100  

leva cerca de 90 segundos agora. Aqui está o descrevem:

+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
| id | select_type | table             | type   | possible_keys                                         | key                        | key_len | ref                             | rows  | Extra                       |
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+
|  1 | SIMPLE      | metaward_achiever | ref    | metaward_achiever_award_id,metaward_achiever_alias_id | metaward_achiever_award_id | 4       | const                           | 66424 | Using where; Using filesort | 
|  1 | SIMPLE      | metaward_alias    | eq_ref | PRIMARY                                               | PRIMARY                    | 4       | paul.metaward_achiever.alias_id |     1 |                             | 
+----+-------------+-------------------+--------+-------------------------------------------------------+----------------------------+---------+---------------------------------+-------+-----------------------------+

Parece que agora TONELADAS de minhas consultas ficar preso no passo "Classificando resultado":

mysql> show processlist;
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| Id     | User | Host      | db   | Command | Time | State          | Info                                                                                                 |
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
| 460568 | paul | localhost | paul | Query   |    0 | NULL           | show processlist                                                                                     | 
| 460638 | paul | localhost | paul | Query   |    0 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460710 | paul | localhost | paul | Query   |   79 | Sending data   | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
| 460722 | paul | localhost | paul | Query   |   49 | Updating       | UPDATE `metaward_alias` SET `modified` = '2009-09-15 12:43:50', `created` = '2009-08-24 11:55:24', ` | 
| 460732 | paul | localhost | paul | Query   |   25 | Sorting result | SELECT `metaward_achiever`.`id`, `metaward_achiever`.`modified`, `metaward_achiever`.`created`, `met | 
+--------+------+-----------+------+---------+------+----------------+------------------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)

Qualquer porque é que simples atualização preso por 49 segundos?

Se ele ajuda, aqui estão os esquemas:

| metaward_alias | CREATE TABLE `metaward_alias` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(255) DEFAULT NULL,
  `shortname` varchar(100) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `type_id` int(11) NOT NULL,
  `md5` varchar(32) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_alias_user_id` (`user_id`),
  KEY `metaward_alias_type_id` (`type_id`)
) ENGINE=InnoDB AUTO_INCREMENT=858381 DEFAULT CHARSET=utf8 | 

| metaward_award | CREATE TABLE `metaward_award` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `string_id` varchar(20) NOT NULL,
  `owner_id` int(11) NOT NULL,
  `name` varchar(100) NOT NULL,
  `description` longtext NOT NULL,
  `owner_points` int(11) NOT NULL,
  `url` varchar(500) NOT NULL,
  `remote_image` varchar(500) DEFAULT NULL,
  `image` varchar(100) NOT NULL,
  `parent_award_id` int(11) DEFAULT NULL,
  `slug` varchar(110) NOT NULL,
  `true_points` double DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `string_id` (`string_id`),
  KEY `metaward_award_owner_id` (`owner_id`),
  KEY `metaward_award_parent_award_id` (`parent_award_id`),
  KEY `metaward_award_slug` (`slug`),
  KEY `metaward_award_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=122176 DEFAULT CHARSET=utf8 | 

| metaward_achiever | CREATE TABLE `metaward_achiever` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `modified` datetime NOT NULL,
  `created` datetime NOT NULL,
  `award_id` int(11) NOT NULL,
  `alias_id` int(11) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `metaward_achiever_award_id` (`award_id`),
  KEY `metaward_achiever_alias_id` (`alias_id`)
) ENGINE=InnoDB AUTO_INCREMENT=77175366 DEFAULT CHARSET=utf8 | 

E estes no meu my.cnf

innodb_file_per_table
innodb_buffer_pool_size = 2048M
innodb_additional_mem_pool_size = 16M
innodb_flush_method=O_DIRECT
Foi útil?

Solução

Isso é um grande conjunto de resultados (66,424 linhas) que o MySQL deve tipo manualmente. Tente adicionar um índice para metaward_achiever.modified.

Há uma limitação com o MySQL 4.x que só permite MySQL para usar um índice por tabela. Uma vez que está usando o índice na coluna metaward_achiever.award_id para a seleção WHERE, ele não pode também usar o índice em metaward_achiever.modified para a espécie. Eu espero que você está usando 5.x MySQL, que pode ter melhorado isso.

Você pode ver isso fazendo explicar sobre esta consulta simplificado:

SELECT * FROM `metaward_achiever` 
 WHERE `metaward_achiever`.`award_id` = 1507  
 ORDER BY `metaward_achiever`.`modified` DESC 
 LIMIT 100

Se você pode obter isso usando os índices tanto para a seleção ONDE e classificação, então você está definido.

Você também pode criar um índice composto com tanto metaward_achiever.award_id e metaward_achiever. Se o MySQL não usá-lo, então você pode sugerir-lo ou remover a um em apenas award_id.

Como alternativa, se você pode se livrar de metaward_achiever.id e fazer metaward_achiever.award_id sua chave primária e adicionar uma tecla no metaward_achiever.modified, ou melhor ainda make metaward_achiever.award_id combinado com metaward.modified sua chave primária, então você' vai ser muito bom.

Você pode tentar otimizar o arquivo de classificação, modificando as configurações. Infelizmente, eu não sou experiente com isso, como nosso DBA lida com a configuração, mas você pode querer verificar para fora este grande blog: http://www.mysqlperformanceblog.com/

Aqui está um artigo sobre filesort em particular: http://s.petrunia.net/blog/?p=24

Outras dicas

Como escrito na Se você ? mover de MyISAM para InnoDB (o que é bastante recente):

Innodb Necessidades Sintonia Como nota final sobre MyISAM à migração Innodb eu deveria falar sobre Innodb tuning. Innodb precisa de ajuste. Realmente. MyISAM para muitas aplicações pode trabalhar bem com os padrões. Eu vi centenas de bases de dados GB correu com MyISAM com as configurações padrão e funcionou razoavelmente. Innodb precisa de recursos e não vai funcionar bem com padrões muito. Sintonia MyISAM de padrões raramente dá mais do que 2-3 vezes ganhar enquanto ele pode ser tanto quanto 10-50 vezes para tabelas InnoDB, em particular para escrever cargas de trabalho intensivas. Verifique aqui para mais detalhes.

Assim, cerca de Configurações Innodb MySQL, o autor escreveu em Innodb Performance Optimization Basics :

Maioria das mais importantes são:

innodb_buffer_pool_size 70-80% de Memória é uma aposta segura. Eu defini-lo para 12G na caixa de 16GB.

Atualizar : Se você está procurando mais detalhes, consulte a guia detalhado sobre sintonia innodb tampão piscina

innodb_log_file_size - Isso depende suas necessidades de velocidade de recuperação, mas 256M parece ser um bom equilíbrio entre tempo de recuperação razoável e bom desempenho

innodb_log_buffer_size = 4M 4M é bom para a maioria dos casos a menos que você está tubulação blobs grandes para InnoDB, neste caso, aumentá-lo um pouco.

innodb_flush_log_at_trx_commit = 2 Se você não é preocupação sobre o ácido e pode perder transações para o último segundo ou dois em caso de acidente OS cheia de definir este valor. Ele pode efeito dramático, especialmente em um monte de transações de gravação curtas.

innodb_thread_concurrency = 8 simultaneidade Mesmo com o atual Innodb Escalabilidade Fixes ter limitado ajuda. O número real pode ser maior ou menor dependendo da sua aplicação e padrão que é 8 é bom arranque

innodb_flush_method = O_DIRECT desempenho Evite buffer duplo e reduzir a pressão swap, na maioria dos casos essa configuração melhora. Embora tenha cuidado se você não tem bateria de backup de cache RAID como quando escrever IO pode sofrer.

innodb_file_per_table - Se você não tem muitas mesas usar esta opção, assim você não terá innodb descontrolado crescimento tabela principal que você não pode recuperar. Esta opção foi adicionada no MySQL 4.1 e bastante agora estável para uso.

Além disso, verifique se o seu aplicativo pode ser executado em modo de isolamento READ-COMPROMETIDO - se isso acontecer - conjunto que seja padrão como transação-isolation = LEIA comprometido. Esta opção tem alguns benefícios de desempenho, especialmente no bloqueio em 5,0 e ainda mais para vir com o MySQL 5.1 e replicação de nível de linha.

Apenas para o registro, as pessoas por trás mysqlperformanceblog.com correu um benchmark comparando Falcon, MyISAM e InnoDB. O benchmark foi realmente deveria ser destacando Falcon, exceto que ele era InnoDB que ganhou o dia, superando tanto Falcon e MyISAM em consultas por segundo para quase todos os testes: InnoDB vs MyISAM vs benchmarks Falcon -. parte 1

Meu palpite é que você provavelmente não configurou suas configurações InnoDB além dos padrões. Você deve fazer uma rápida no google para configurar suas opções InnoDB.

O que me causou mais problemas de desempenho perceptível fora da caixa foi innodb_buffer_pool_size. Isso deve ser definido para 50-80% da memória do equipamento. Por padrão, ele é muitas vezes apenas alguns MB. Crank it maneira , e você deve ver um aumento de desempenho perceptível.

Também dê uma olhada innodb_additional_mem_pool_size.

Iniciar aqui , mas também do Google ao redor para "ajuste de desempenho do InnoDB".

otimizador de consulta do MySQL não é bom, da minha memória. Tente um subselect em vez de uma reta participar.

SELECT * FROM (SELECT * FROM `metaward_achiever` 
               WHERE `metaward_achiever`.`award_id` = 1507) a
INNER JOIN `metaward_alias` ON (a.`alias_id` = `metaward_alias`.`id`) 
ORDER BY a.`modified` DESC 
LIMIT 100

Ou algo assim (não testado sintaxe acima).

A classificação é algo feito pelo servidor de banco de dados, não o mecanismo de armazenamento, em MySQL.

Se em ambos os casos, o motor não foi capaz de fornecer os resultados em forma já classificada (que depende do índice utilizado), então as necessidades do servidor para classificá-los.

A única razão que MyISAM / InnoDB pode ser diferente é que a ordem das linhas voltar poderiam afetar como ordenados os dados já são - MyISAM poderia dar os dados em ordem "mais ordenados" em alguns casos (e vice-versa).

Ainda assim, a triagem 60k linhas não vai demorar muito tempo como é um conjunto muito pequeno de dados. Tem certeza de que você tem o seu tipo de buffer definida grande o suficiente?

Usando um filesort on-disco () em vez de um in-memory é muito mais lento. O motor deve, no entanto, não faz qualquer diferença para isso. filesort não é uma função de motor, mas uma função essencial MySQL. filesort faz, de fato, chupar um monte de maneiras, mas não é normalmente tão lento.

Tente adicionar uma chave para campos: metaward_achiever.alias_id, metaward_achiever.award_id e metaward_achiever.modified isso vai ajudar muito. E não use chaves em campos varchar ele irá aumentar o tempo para inserções e atualizações. Também parece que você tem registros de 77M em empreendedor tabela, você pode querer se preocupam com otimizações InnoDB. Há lotes de bons tutores em torno de como os limites de memória definido para ele.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top