AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / user-288771

msbit's questions

Martin Hope
msbit
Asked: 2024-03-28 23:01:30 +0800 CST

Muitos itens na cláusula `IN` causam varredura completa da tabela antes do esperado

  • 6

Temos uma tabela de índice de pesquisa, fornecida por um CMS, que tem o formato:

CREATE TABLE `craft_searchindex` (
  `elementId` int NOT NULL,
  `attribute` varchar(25) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
  `fieldId` int NOT NULL,
  `locale` char(12) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
  `keywords` text CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci NOT NULL,
  PRIMARY KEY (`elementId`,`attribute`,`fieldId`,`locale`),
  FULLTEXT KEY `craft_searchindex_keywords_idx` (`keywords`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci

Como parte da funcionalidade de pesquisa fornecida pelo CMS, é formada a seguinte consulta:

SELECT * FROM `craft_searchindex` WHERE (`keywords` LIKE '% tom %') AND `locale` = 'en_au' AND `elementId` IN (<ids>)

onde idsé uma lista filtrada de elementIds com base em uma consulta anterior.

O que observamos é que quando atingimos um determinado número de itens naquela INcláusula, o desempenho cai e o tempo de execução da consulta torna-se constante, conforme mostrado em um gráfico coletado antes da migração para o InnoDB (tempo de execução do eixo y em milissegundos, x- número do eixo de itens na INcláusula, série min/média/máx):

insira a descrição da imagem aqui

A última linha de uma EXPLAIN ANALYZEconsulta mostra o seguinte para aproximadamente 33.500 itens:

-> Index range scan on craft_searchindex using PRIMARY over (elementId = 1) OR (elementId = 128) OR (33500 more)  (cost=55355 rows=234514) (actual time=0.0376..537 rows=469028 loops=1)

e para aproximadamente 34.000 itens:

-> Table scan on craft_searchindex  (cost=335885 rows=3.27e+6) (actual time=0.0303..3237 rows=3.72e+6 loops=1)

Minha interpretação disso (que está de acordo com o gráfico) é que:

  • até certo limite, o índice de chave primária é verificado (um por um, fornecendo o crescimento linear) e
  • além desse limite, a tabela completa é sempre verificada (fornecendo o valor constante)

Minha pergunta é, dado que há uma diferença significativa entre o tempo de execução imediatamente abaixo desse limite e o tempo acima dele, por que o planejador de consultas opta por abandonar a verificação do índice[*], e algo pode ser feito a respeito (com a restrição que a preparação da consulta é realizada pelo CMS, portanto, na maior parte fora do nosso controle)?

Ingressando em uma mesa temporária

Embora não seja uma solução para nós, dadas as restrições das consultas tratadas pelo CMS, segui a sugestão de @Akina de usar uma tabela temporária indexada. Isso altera a consulta de (efetivamente):

SET SESSION group_concat_max_len = 1 << 19;
PREPARE ids_stmt FROM 'SELECT GROUP_CONCAT(`id`)
INTO @ids_clause
FROM (
  SELECT `elements`.`id`
  FROM `craft_elements` `elements`
  JOIN `craft_elements_i18n` `elements_i18n` ON elements_i18n.elementId = elements.id
  JOIN `craft_content` `content` ON content.elementId = elements.id
  JOIN `craft_users` `users` ON users.id = elements.id
  WHERE ((elements_i18n.locale = ?) AND (content.locale = ?)) AND (elements.archived = 0)
) AS `ids`';
SET @l = 'en_au';
EXECUTE ids_stmt USING @l, @l;  
DEALLOCATE PREPARE ids_stmt;

SET @search_query = CONCAT("SELECT `craft_searchindex`.* FROM `craft_searchindex` WHERE (`keywords` LIKE '% tom %') AND `locale` = 'en_au' AND `elementId` IN (", @ids_clause, ')');

PREPARE search_stmt FROM @search_query;
EXECUTE search_stmt;
DEALLOCATE PREPARE search_stmt;

para:

PREPARE ids_stmt FROM 'CREATE TEMPORARY TABLE ids_table
(PRIMARY KEY(`id`))
SELECT `elements`.`id`
FROM `craft_elements` `elements`
JOIN `craft_elements_i18n` `elements_i18n` ON elements_i18n.elementId = elements.id
JOIN `craft_content` `content` ON content.elementId = elements.id
JOIN `craft_users` `users` ON users.id = elements.id
WHERE ((elements_i18n.locale = ?) AND (content.locale = ?)) AND (elements.archived = 0)
LIMIT 1';
SET @l = 'en_au';
EXECUTE ids_stmt USING @l, @l; 
DEALLOCATE PREPARE ids_stmt;

SELECT `craft_searchindex`.*
FROM `craft_searchindex`
JOIN `ids_table` ON `craft_searchindex`.`elementId` = `ids_table`.`id`
WHERE (`keywords` LIKE '% tom %') AND `locale` = 'en_au';

e isso mantém o crescimento linear do tempo de execução bem além do limite anterior.

Curiosamente (e talvez porque a tabela temporária tenha apenas uma coluna) a eliminação da chave primária não afeta o tempo de execução.

Abaixo está um gráfico comparando estes (os tempos agora estão em segundos): insira a descrição da imagem aqui

[*]: A partir de alguns cálculos breves, continuar a varredura do índice em vez de realizar uma varredura completa da tabela ainda estaria abaixo do tempo de execução da varredura completa da tabela até que o número de itens na INcláusula excedesse aproximadamente 200.000

query-performance
  • 1 respostas
  • 25 Views

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve