Estamos executando o MySQL 5.1 no Windows Server 2008 R2.
Ultimamente, temos feito alguns diagnósticos em nosso banco de dados e encontramos alguns artefatos perturbadores que não podemos explicar . Adicionamos algum código ao log quando tivemos consultas que demoraram muito (> 2000 ms). Os resultados foram surpreendentes (e possivelmente uma explicação para nossos impasses).
Ocasionalmente, consultas, que normalmente levam muito pouco tempo (<10 ms), levam de 4 a 13 segundos. Para ser claro, essas são consultas que estão sendo executadas constantemente (várias vezes por segundo) e que não sofrem com esses picos de tempo de consulta.
Analisamos nossos índices à procura de erros óbvios e não tivemos muita sorte.
Atualizar
A tabela de pessoas:
| people | CREATE TABLE `people` (
`people_id` bigint(20) NOT NULL AUTO_INCREMENT,
`company_id` bigint(20) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`temp_password` varchar(10) DEFAULT NULL,
`reset_password_hash` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`phone` varchar(32) DEFAULT NULL,
`mobile` varchar(32) DEFAULT NULL,
`iphone_device_id` varchar(160) DEFAULT NULL,
`iphone_device_time` datetime DEFAULT NULL,
`last_checkin` datetime DEFAULT NULL,
`location_lat` double DEFAULT NULL,
`location_long` double DEFAULT NULL,
`gps_strength` smallint(6) DEFAULT NULL,
`picture_blob_id` bigint(20) DEFAULT NULL,
`authority` int(11) NOT NULL DEFAULT '0',
`active` tinyint(1) NOT NULL DEFAULT '1',
`date_created` datetime NOT NULL,
`last_login` datetime NOT NULL,
`panic_mode` tinyint(1) NOT NULL DEFAULT '0',
`battery_level` double DEFAULT NULL,
`battery_state` varchar(32) DEFAULT NULL,
PRIMARY KEY (`people_id`),
KEY `email` (`email`),
KEY `company_id` (`company_id`),
KEY `iphone_device_id` (`iphone_device_id`),
KEY `picture_blob_id` (`picture_blob_id`),
CONSTRAINT `people_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `companies` (`company_id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `people_ibfk_2` FOREIGN KEY (`picture_blob_id`) REFERENCES `blobs` (`blob_id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4658 DEFAULT CHARSET=utf8 |
Índices:
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
| people | 0 | PRIMARY | 1 | people_id | A | 3502 | NULL | NULL | | BTREE | |
| people | 1 | email | 1 | email | A | 3502 | NULL | NULL | YES | BTREE | |
| people | 1 | company_id | 1 | company_id | A | 3502 | NULL | NULL | | BTREE | |
| people | 1 | iphone_device_id | 1 | iphone_device_id | A | 3502 | NULL | NULL | YES | BTREE | |
| people | 1 | picture_blob_id | 1 | picture_blob_id | A | 3502 | NULL | NULL | YES | BTREE | |
+--------+------------+------------------+--------------+------------------+-----------+-------------+----------+--------+------+------------+---------+
temos ~ 5000 linhas na tabela no servidor que está nos causando problemas.
fonte
Respostas:
O UPDATE consulta em suas duas perguntas anteriores ( Pergunta1 , Pergunta2 ) estão atingindo a tabela 'pessoas' por PRIMARY KEY com bloqueio no nível da linha. Foi o que afirmei na Pergunta 1 em 6 de junho de 2011 10:03
Todas as transações estão atravessando a chave PRIMARY. Como o PRIMARY é um índice clusterizado no InnoDB, a chave PRIMARY e a própria linha estão juntas. Assim, atravessar uma linha ee a PRIMARY KEY são uma e a mesma. Portanto, qualquer bloqueio de índice na PRIMARY KEY também é um bloqueio de nível de linha.
Ainda não foi considerado algo que possa atribuir lentidão aos índices: O uso de índices NÃO-UNIQUE no InnoDB. Toda pesquisa indexada no InnoDB usando índices não exclusivos também possui o ID da linha de cada linha anexada à chave não exclusiva. O rowID basicamente emina a partir do Clustered Index . A atualização de índices não exclusivos DEVE SEMPRE interagir com o índice clusterizado, MESMO SE A TABELA NÃO TEM UMA CHAVE PRIMÁRIA.
Outra coisa a se pensar é o processo de gerenciamento de nós BTREE em um índice. Às vezes, requer a divisão da página dos nós. Todas as entradas no nó BTREE de índices não exclusivos contêm campos não exclusivos, MAIS o rowID dentro do índice em cluster. Para mitigar adequadamente a divisão dessas páginas BTREE sem perturbar a integridade dos dados, a linha associada ao ID da linha deve experimentar um bloqueio de nível de linha internamente.
Se a tabela 'pessoas' tiver muitos índices não exclusivos, prepare-se para ter um grande número de páginas de índice no espaço de tabela, além de ter pequenas e minúsculas linhas de bloqueio ocultando você periodicamente.
Existe outro fator que não é tão óbvio: população-chave
Às vezes, quando um índice é preenchido, os valores-chave que compõem os índices podem ficar desequilibrados ao longo do tempo e fazer com que o MySQL Query Optimizer mude de pesquisas com chave, para verificações de índice e, finalmente, para verificações de tabela completa. Isso você não pode controlar, a menos que redesenhe a tabela com novos índices para compensar a desatenção das teclas. Por favor, forneça a estrutura da tabela para a tabela 'people', a contagem da tabela 'people' e a saída dos índices show para a tabela 'people' .
Mesmo que as consultas usem apenas a PRIMARY KEY, a desigualdade de chaves em índices não exclusivos ainda precisará do equilíbrio BTREE e da divisão da página. Esse gerenciamento do BTREE produzirá uma desaceleração notável devido a bloqueios intermitentes no nível da linha que você não pretendia que acontecesse.
ATUALIZAÇÃO 14/06/2011 22:19
Consultas da pergunta 1
Imagine a sequência em eventos
Consultas da pergunta 2
Essas duas consultas são ainda mais confusas porque a primeira consulta está atualizando tudo, exceto people_id 666. Centenas de linhas estão sendo dolorosamente bloqueadas apenas com a primeira consulta. A segunda consulta está atualizando people_id 666 executando a sequência 5 de eventos. A primeira consulta está executando as mesmas 5 sequências de eventos em todas as linhas envolvidas, exceto people_id 666, mas o índice para iphone_device_id está em um curso de interecepção com duas consultas diferentes. Alguém precisa bloquear as páginas do BTREE por ordem de chegada.
Diante desses dois pares de consultas em rota de colisão, para possivelmente bloquear as mesmas páginas BTREE em um índice, pode ser uma experiência instigante para o InnoDB ou qualquer RDBMS compatível com ACID. Portanto, uma desaceleração do índice é o destino desses pares de consultas, a menos que você possa garantir que as consultas sejam executadas com AUTOCOMMIT = 1 ou permitindo leituras sujas (embora colisões como essas tornem READ-COMMITTED e READ-UNCOMMITED um pesadelo para o MVCC).
ATUALIZAÇÃO 15/06/2011 10:29
@RedBlueThing: nas consultas da pergunta 2, a primeira consulta é uma consulta de intervalo, portanto, muitos bloqueios de linha estão sendo atingidos. Observe também que ambas as consultas estão tentando bloquear o mesmo ID de espaço 0, página 4611 n bits 152 estão sendo bloqueados na PRIMARY KEY, também conhecida como índice clusterizado.
Para garantir que seu aplicativo seja executado, no mínimo, com base na série de eventos que você espera, existem duas opções diferentes que você pode tentar:
Opção 1) Converta esta tabela para MyISAM (pelo menos em um servidor de desenvolvimento). Cada UPDATE, INSERT e DELETE imporá um bloqueio de tabela completo por ordem de chegada.
Opção 2) Tente usar o nível de isolamento SERIALIZABLE . Isso bloqueará todas as linhas pretendidas no modo COMPARTILHADO.
A sequência de eventos que você espera será interrompida ou terá êxito usando essas duas opções alternativas. Se ambas as opções falharem, será necessário examinar seu aplicativo e priorizar a ordem de execução das suas consultas. Depois de estabelecer essa prioridade, você pode simplesmente desfazer essas opções (na opção 1, volte ao InnoDB, na opção 2, volte ao nível de isolamento padrão [pare de usar SERIALIZABLE]).
fonte
MOSTRAR VARIÁVEIS COMO 'innodb%'; - Em particular, se os dados e índices simplesmente não atingirem o tamanho do buffer pool, você poderá estar atingindo o disco com muito mais força do que antes. A E / S é a grande causa de desempenho.
A maioria dos seus campos é duas vezes maior que o necessário. BIGINT (8 bytes) é um exagero para a maioria dos IDs. 5000 linhas precisam apenas de um SMALLINT UNSIGNED (limite de 65K, apenas 2 bytes). Ou use MEDIUMINT para obter uma margem de segurança.
DOUBLE fornece 16 dígitos significativos a um custo de 8 bytes. O nível da bateria possui mais de 2 dígitos significativos de precisão? FLUTUAR leva 4 bytes.
O que quero dizer aqui é que "menor -> mais armazenável em cache -> mais rápido".
Por favor, mostre-nos as consultas lentas; pelo menos alguns dos que se tornaram subitamente mais lentos. Só podemos fazer suposições sem eles. Ligue o slowlog e defina long_query_time = 1; isso ajudará a encontrar as consultas mais lentas.
Você entende o benefício dos índices "compostos"?
fonte