Esta é minha consulta lenta:
SELECT `products_counts`.`cid`
FROM
`products_counts` `products_counts`
LEFT OUTER JOIN `products` `products` ON (
`products_counts`.`product_id` = `products`.`id`
)
LEFT OUTER JOIN `trademarks` `trademark` ON (
`products`.`trademark_id` = `trademark`.`id`
)
LEFT OUTER JOIN `suppliers` `supplier` ON (
`products_counts`.`supplier_id` = `supplier`.`id`
)
WHERE
`products_counts`.product_id IN
(159, 572, 1075, 1102, 1145, 1162, 1660, 2355, 2356, 2357, 3236, 6471, 6472, 6473, 8779, 9043, 9095, 9336, 9337, 9338, 9445, 10198, 10966, 10967, 10974, 11124, 11168, 16387, 16689, 16827, 17689, 17920, 17938, 17946, 17957, 21341, 21352, 21420, 21421, 21429, 21544, 27944, 27988, 30194, 30196, 30230, 30278, 30699, 31306, 31340, 32625, 34021, 34047, 38043, 43743, 48639, 48720, 52453, 55667, 56847, 57478, 58034, 61477, 62301, 65983, 66013, 66181, 66197, 66204, 66407, 66844, 66879, 67308, 68637, 73944, 74037, 74060, 77502, 90963, 101630, 101900, 101977, 101985, 101987, 105906, 108112, 123839, 126316, 135156, 135184, 138903, 142755, 143046, 143193, 143247, 144054, 150164, 150406, 154001, 154546, 157998, 159896, 161695, 163367, 170173, 172257, 172732, 173581, 174001, 175126, 181900, 182168, 182342, 182858, 182976, 183706, 183902, 183936, 184939, 185744, 287831, 362832, 363923, 7083107, 7173092, 7342593, 7342594, 7342595, 7728766)
ORDER BY
products_counts.inflow ASC,
supplier.delivery_period ASC,
trademark.sort DESC,
trademark.name ASC
LIMIT
0, 3;
O tempo médio de consulta é de 4,5 segundos no meu conjunto de dados e isso é inaceitável.
Soluções que eu vejo:
Adicione todas as colunas da cláusula order à products_counts
tabela. Mas eu tenho ~ 10 tipos de pedidos no aplicativo, por isso devo criar muitas colunas e índices. Além disso, products_counts
tenho atualizações / inserções / exclusões muito intensivas, por isso preciso executar imediatamente a atualização de todas as colunas relacionadas a pedidos (usando gatilhos?).
Existe outra solução?
Explicar:
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | products_counts | range | product_id_supplier_id,product_id,pid_count | product_id_supplier_id | 4 | NULL | 227 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | products | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.product_id | 1 | |
| 1 | SIMPLE | trademark | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products.trademark_id | 1 | |
| 1 | SIMPLE | supplier | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.supplier_id | 1 | |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
Estrutura de tabelas:
CREATE TABLE `products_counts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) unsigned NOT NULL,
`supplier_id` int(11) unsigned NOT NULL,
`count` int(11) unsigned NOT NULL,
`cid` varchar(64) NOT NULL,
`inflow` varchar(10) NOT NULL,
`for_delete` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `cid` (`cid`),
UNIQUE KEY `product_id_supplier_id` (`product_id`,`supplier_id`),
KEY `product_id` (`product_id`),
KEY `count` (`count`),
KEY `pid_count` (`product_id`,`count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`category_id` int(11) unsigned NOT NULL,
`trademark_id` int(11) unsigned NOT NULL,
`photo` varchar(255) NOT NULL,
`sort` int(11) unsigned NOT NULL,
`otech` tinyint(1) unsigned NOT NULL,
`not_liquid` tinyint(1) unsigned NOT NULL DEFAULT '0',
`applicable` varchar(255) NOT NULL,
`code_main` varchar(64) NOT NULL,
`code_searchable` varchar(128) NOT NULL,
`total` int(11) unsigned NOT NULL,
`slider` int(11) unsigned NOT NULL,
`slider_title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`),
KEY `category_id` (`category_id`),
KEY `trademark_id` (`trademark_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `trademarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`country_id` int(11) NOT NULL,
`sort` int(11) unsigned NOT NULL DEFAULT '0',
`sort_list` int(10) unsigned NOT NULL DEFAULT '0',
`is_featured` tinyint(1) unsigned NOT NULL,
`is_direct` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `suppliers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`code` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`delivery_period` tinyint(1) unsigned NOT NULL,
`is_default` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Informações do servidor MySQL:
mysqld Ver 5.5.45-1+deb.sury.org~trusty+1 for debian-linux-gnu on i686 ((Ubuntu))
(inflow, product_id)
?innodb_buffer_pool_size
. Normalmente, cerca de 70% da RAM disponível é boa.Respostas:
A revisão das definições da tabela mostra que você possui índices correspondentes nas tabelas envolvidas. Isso deve fazer com que as junções aconteçam o mais rápido possível dentro dos limites da
MySQL's
lógica de junção.No entanto, classificar de várias tabelas é mais complexo.
Em 2007, Sergey Petrunia descreveu os três
MySQL
algoritmos de classificação em ordem de velocidade paraMySQL
: http://s.petrunia.net/blog/?m=201407filesort()
na 1ª tabela não constantefilesort()
nelaNas definições e junções da tabela mostradas acima, você pode ver que nunca obterá a classificação mais rápida . Isso significa que você dependerá
filesort()
dos critérios de classificação que estiver usando.No entanto, se você projetar e usar uma Visualização Materializada , poderá usar o algoritmo de classificação mais rápido.
Para
MySQL 5.5
(neste exemplo) aumentar aORDER BY
velocidade se você não conseguirMySQL
usar índices em vez de uma fase de classificação extra, tente as seguintes estratégias:• Aumente o
sort_buffer_size
valor da variável.• Aumente o
read_rnd_buffer_size
valor da variável.• Use menos RAM por linha declarando as colunas apenas o tamanho necessário para que os valores reais sejam armazenados. [Por exemplo, reduza um varchar (256) para varchar (ActualLongestString)]
• Altere a
tmpdir
variável do sistema para apontar para um sistema de arquivos dedicado com grandes quantidades de espaço livre. (Outros detalhes são oferecidos no link acima.)Vistas materializadas - uma abordagem diferente para classificar tabelas unidas
Você aludiu a Visualizações materializadas com sua pergunta referente ao uso de gatilhos. O MySQL não possui funcionalidade embutida para criar uma Visualização Materializada, mas você possui as ferramentas necessárias. Ao usar gatilhos para espalhar a carga, você pode manter a Visualização Materializada até o momento.
A Visualização Materializada é na verdade uma tabela que é preenchida por meio de código processual para criar ou reconstruir a Visualização Materializada e mantida por gatilhos para manter os dados atualizados.
Como você está criando uma tabela que terá um índice , a Visualização Materializada, quando consultada, pode usar o método de classificação mais rápida : Use o método de acesso baseado em índice que produz saída ordenada
Como
MySQL 5.5
usa acionadores para manter uma Visualização Materializada , você também precisará de um processo, script ou procedimento armazenado para criar a Visualização Materializada inicial .Mas esse é obviamente um processo muito pesado para ser executado após cada atualização nas tabelas base em que você gerencia os dados. É aí que os gatilhos entram em ação para manter os dados atualizados à medida que as alterações são feitas. Dessa forma
insert
, cada ,,update
edelete
propagará suas alterações, usando seus gatilhos, para a Visualização Materializada .A organização FROMDUAL em http://www.fromdual.com/ possui um código de amostra para manter uma Visualização Materializada . Então, ao invés de escrever minhas próprias amostras, vou apontar para as amostras deles:
http://www.fromdual.com/mysql-materialized-views
Exemplo 1: Criando uma vista materializada
Isso fornece a visualização materializada no momento da atualização. No entanto, como você possui um banco de dados em movimento rápido, também deseja manter essa visualização o mais atualizada possível.
Portanto, as tabelas de dados base afetadas precisam ter gatilhos para propagar as alterações de uma tabela base para a tabela Vista materializada . Como um exemplo:
Exemplo 2: Inserindo novos dados em uma visão materializada
Obviamente, você também precisará de gatilhos para manter a Exclusão de dados de uma visão materializada e atualizar dados em uma visão materializada . Também estão disponíveis amostras para esses gatilhos.
NO ÚLTIMO: Como isso torna mais rápida a classificação das tabelas unidas?
A Visualização Materializada está sendo construída constantemente à medida que as atualizações são feitas. Portanto, você pode definir o índice (ou índices ) que deseja usar para classificar os dados na exibição ou tabela materializada .
Se a sobrecarga de manutenção dos dados não for muito pesada, você estará gastando alguns recursos (CPU / IO / etc) para cada alteração de dados relevante para manter a Visualização Materializada e, portanto, os dados do índice estão atualizados e prontamente disponíveis. Portanto, a seleção será mais rápida, pois você:
Dependendo das suas circunstâncias e de como você se sente em relação ao processo geral, convém reconstruir as Visualizações Materializadas todas as noites durante um período lento.
fonte
Não há muito o que fazer aqui, mas acho que o principal problema é que você está criando uma tabela temporária bastante grande e classifica o arquivo no disco a cada vez. O motivo é:
Isso significa que a tabela temporária e o arquivo de classificação podem ser razoavelmente grandes, pois, ao criar a tabela temporária, os campos são criados no comprimento MAX e na classificação dos registros, no comprimento MAX (e UTF8 é de 3 bytes por caractere). Isso provavelmente também impede o uso de uma tabela temporária na memória. Para mais informações, consulte os detalhes das tabelas temporárias internas .
O LIMIT também não nos serve aqui, pois precisamos materializar e ordenar todo o conjunto de resultados antes de saber quais são as três primeiras linhas.
Você já tentou mover seu tmpdir para um sistema de arquivos tmpfs ? Se / tmp ainda não estiver usando tmpfs (o MySQL usa
tmpdir=/tmp
por padrão no * nix), você pode usar / dev / shm diretamente. No seu arquivo my.cnf:Então você precisaria reiniciar o mysqld.
Isso pode fazer uma enorme diferença. Se você estiver sob pressão de memória no sistema, provavelmente desejará limitar o tamanho (normalmente as distribuições linux limitam os tmpfs a 50% da RAM total por padrão) para evitar a troca de segmentos de memória para o disco, ou mesmo pior uma situação OOM . Você pode fazer isso editando a linha em
/etc/fstab
:Você também pode redimensioná-lo "online". Por exemplo:
Você também pode atualizar para o MySQL 5.6 - que possui subconsultas de desempenho e tabelas derivadas - e brincar com a consulta um pouco mais. Não acho que veremos grandes vitórias nesse caminho, pelo que vejo.
Boa sorte!
fonte