Pergunta

Estou trabalhando em uma loja eletrônica que vende produtos apenas por empréstimos. Eu exibo 10 produtos por página em qualquer categoria, cada produto possui 3 preços diferentes - 3 tipos diferentes de empréstimos. Tudo correu muito bem durante o tempo de teste, o tempo de execução da consulta foi perfeito, mas hoje quando transferiu as alterações no servidor de produção, o site "entrou em colapso" em cerca de 2 minutos. A consulta usada para selecionar tipos de empréstimo às vezes está pendurada por ~ 10 segundos e acontece com frequência e, portanto, não pode acompanhar e seu Hella Slow. A tabela usada para armazenar os dados possui aproximadamente 2 milhões de registros e cada seleção se parece com o seguinte:

SELECT * 
FROM products_loans 
WHERE KOD IN("X17/Q30-10", "X17/12", "X17/5-24") 
AND 369.27 BETWEEN CENA_OD AND CENA_DO;

3 tipos de empréstimos e o preço que precisa estar no alcance entre CENA_OD e CENA_DO, assim 3 linhas são devolvidas.

Mas como preciso exibir 10 produtos por página, preciso executá -lo através de uma seleção modificada usando OU, já que não encontrei nenhuma outra solução para isso. Eu perguntei sobre isso aqui, mas não obteve resposta. Como mencionado no post de referência, isso deve ser feito separadamente, pois existe não coluna que poderia ser usada em uma junção (exceto o preço e o código do curso, mas isso acabou muito, muito mal). Aqui está o show create table, KOD e CENA_OD/CENA_DO muito indexados via índice.

CREATE TABLE `products_loans` (
  `KOEF_ID` bigint(20) NOT NULL,
  `KOD` varchar(30) NOT NULL,
  `AKONTACIA` int(11) NOT NULL,
  `POCET_SPLATOK` int(11) NOT NULL,
  `koeficient` decimal(10,2) NOT NULL default '0.00',
  `CENA_OD` decimal(10,2) default NULL,
  `CENA_DO` decimal(10,2) default NULL,
  `PREDAJNA_CENA` decimal(10,2) default NULL,
  `AKONTACIA_SUMA` decimal(10,2) default NULL,
  `TYP_VYHODY` varchar(4) default NULL,
  `stage` smallint(6) NOT NULL default '1',
 PRIMARY KEY  (`KOEF_ID`),
 KEY `CENA_OD` (`CENA_OD`),
 KEY `CENA_DO` (`CENA_DO`),
 KEY `KOD` (`KOD`),
 KEY `stage` (`stage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

E também selecionar todos os tipos de empréstimos e filtrá -los posteriormente através do PHP não funciona bem, já que cada tipo tem mais de 50 mil registros e a seleção leva muito tempo também ...

Qualquer IDES sobre melhorar a velocidade é apreciado.

Editar:

Aqui está o explicação

+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+
| id | select_type | table          | type  | possible_keys       | key  | key_len | ref  | rows   | Extra       |
+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | products_loans | range | CENA_OD,CENA_DO,KOD | KOD  | 92      | NULL | 190158 | Using where |
+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+

Eu tentei o índice combinado e ele melhorou o desempenho no servidor de teste de 0,44 s para 0,06 s, não posso acessar o servidor de produção em casa, por isso terei que experimentá -lo amanhã.

Foi útil?

Solução

Tente refatorar sua consulta como:

SELECT * FROM products_loans 
WHERE KOD IN("X17/Q30-10", "X17/12", "X17/5-24") 
AND CENA_OD >= 369.27
AND CENA_DO <= 369.27;

(MySQL não é muito inteligente ao escolher índices) e verifique o desempenho.

A próxima tentativa é adicionar uma chave combinada - (kod, cena_od, cena_do)

E a próxima grande tentativa é refatorar sua base para separar os produtos dos preços. Isso deve realmente ajudar.

PS: Você também pode migrar para o PostgreSQL, é mais inteligente que o MySQL ao escolher índices corretos.

Outras dicas

Seu problema é que você está procurando intervalos que contêm um ponto (em vez da consulta mais normal de todos os pontos em um intervalo). Essas consultas não funcionam bem com o índice de árvore B padrão; portanto, você precisa usar um índice R-Tree. Infelizmente, o MySQL não permite que você selecione um índice R-Tree em uma coluna, mas você pode obter o índice desejado alterando seu tipo de coluna para geometria e usando as funções geométricas para verificar se o intervalo contém o ponto.

Ver QuassnoiArtigo Lista de adjacência vs. conjuntos aninhados: MySQL onde ele explica isso com mais detalhes. O caso de uso é diferente, mas as técnicas envolvidas são as mesmas. Aqui está um extrato da parte relevante do artigo:

Há também uma certa classe de tarefas que exigem a pesquisa de todos os intervalos que contêm um valor conhecido:

  • Procurando um endereço IP na lista de proibições de intervalo IP
  • Procurando por uma determinada data dentro de um intervalo

e vários outros. Essas tarefas podem ser aprimoradas usando os recursos R-Tree do MySQL.

O MySQL só pode usar 1 chave. Se você sempre obtiver a entrada pelas 3 colunas, dependendo dos dados reais (intervalo) nas colunas, um dos seguintes pode muito bem adicionar uma quantidade séria de desempenho:

ALTER TABLE products_loans ADD INDEX(KOD, CENA_OD, CENA_DO);
ALTER TABLE products_loans ADD INDEX(CENA_OD, CENA_DO, KOD);

Observe que a ordem das colunas é importante! Se isso não melhorar o desempenho, dê -nos o EXPLAIN saída da consulta.

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