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 / dba / Perguntas / 68548
Accepted
dotancohen
dotancohen
Asked: 2014-06-19 23:38:10 +0800 CST2014-06-19 23:38:10 +0800 CST 2014-06-19 23:38:10 +0800 CST

ON DUPLICATE KEY UPDATE mais rápido que UPDATE

  • 772

Tenho uma tabela com cerca de 17 milhões de linhas:

mysql> describe humans_we_respect;
+---------------------+-------------------------------------------------------------------------+------+-----+---------+-------+
| Field               | Type                                                                    | Null | Key | Default | Extra |
+---------------------+-------------------------------------------------------------------------+------+-----+---------+-------+
| id                  | bigint(20)                                                              | NO   | PRI | NULL    |       |
| name                | varchar(63)                                                             | YES  |     | NULL    |       |
| address             | varchar(127)                                                            | YES  |     | NULL    |       |
| city                | varchar(63)                                                             | YES  |     | NULL    |       |
| state               | varchar(3)                                                              | YES  | MUL | NULL    |       |
| zip                 | varchar(15)                                                             | YES  |     | NULL    |       |
| country             | varchar(15)                                                             | YES  |     | NULL    |       |
| email               | varchar(127)                                                            | YES  |     | NULL    |       |
| website             | varchar(127)                                                            | YES  |     | NULL    |       |
| area_code_state     | varchar(3)                                                              | YES  | MUL | NULL    |       |
| timezone            | set('other','pacific','mountain','central','eastern','alaska','hawaii') | YES  |     | other   |       |
+---------------------+-------------------------------------------------------------------------+------+-----+---------+-------+
12 rows in set (0.01 sec)

Devido à estrita natureza de apenas contactar quem manifestou interesse numa newsletter, e à estrita natureza de nunca contactar alguém que pediu para não ser contactado, antes de um mailing adicionei um campo para o expressed_interest (tinyint) deafult nullqual mudo 1para quem manifestou interesse, e depois mude para nullpara aqueles que pediram para não serem contatados.

A consulta a seguir, na qual 10.000 linhas são atualizadas por consulta, leva muito tempo para ser executada (eliminada após meia hora):

UPDATE humans_we_respect SET expressed_interest=1 WHERE id IN (1,...,10000);

No entanto, a seguinte consulta é concluída em segundos:

INSERT INTO humans_we_respect (id) VALUES (1),...,(10000) ON DUPLICATE KEY UPDATE expressed_interest=1;

Em que condições será ON DUPLICATE KEY UPDATEmais rápido do que UPDATE? Eu gostaria de saber isso para uso futuro com tabelas grandes como esta.

Isso está no MySQL 5.5.33 em execução no Amazon RDS .

mysql update
  • 2 2 respostas
  • 2324 Views

2 respostas

  • Voted
  1. Best Answer
    fgwaller
    2014-08-26T15:30:48+08:002014-08-26T15:30:48+08:00

    Eu sei que não é fácil obter um plano de execução para uma atualização do MySQL, pois ele fornece apenas as SELECTinstruções on. Mas a pista pode estar na ordem em que os registros são atualizados, na avaliação de um WHEREque contém um INcom grande quantidade de dados estáticos , bem como na quantidade de leituras e gravações conectadas, cache intermediário, associado a isso.

    A declaração

    UPDATE humans_we_respect SET expressed_interest=1 WHERE id IN (1,...,10000); 
    

    é um tipo de instrução que tentamos evitar ao atualizar bancos de dados maiores, pois o analisador parece enlouquecer com eles de tempos em tempos. IN ( a,b,c,...,ZZZZ )para mim, tornou-se um estilo de codificação adequado apenas para números de itens muito pequenos nos INdados. Estou trabalhando em um projeto de código aberto onde frequentemente me deparo com o que chamo de "junção de mente remota", a segunda metade geralmente se parece exatamente com o seu problema.

    SELECT id FROM all_our_customers WHERE happytospam=1 AND LENGTH(email) > 6;
    ...
    Storing result on client side as string like 
    LOOP over results
    $all_ids += ",$next_result";
    END_LOOP
    $all_ids = SUBSTRING($all_ids,1); 
    ending up with a string like 
    "1,2,3,4,5,8,10,100,1000,...,100000" in $all_ids
    ...
    UPDATE humans_we_respect SET expressed_interest=1 WHERE id IN ( $all_ids )
    

    Enquanto a primeira parte geralmente é executada na velocidade da luz, a segunda parte leva uma eternidade, que é o que você descreve também. Essas consultas geralmente podem ser aceleradas EXTREMAMENTE reescrevendo-as como:

    UPDATE humans_we_respect,all_our_customers 
    SET humans_we_respect.expressed_interest=1 
    WHERE all_our_customers.id = humans_we_respect.id 
    AND all_our_customers.happytospam=1 
    AND LENGTH(all_our_customers.email) > 6
    

    Nós também usamos

    UPDATE humans_we_respect 
    SET expressed_interest=1 
    WHERE id IN ( 
    SELECT id 
    FROM all_our_customers 
    WHERE happytospam=1 
    AND LENGTH(email) > 6 
    )
    

    que teve um desempenho melhor que o original, mas não tão bom quanto minha versão sugerida.

    Isso tudo pressupõe que você use índices adequados com primário no ID e índices combinados de várias colunas, onde várias colunas são frequentemente usadas juntas ou têm um bom significado juntas e geralmente estão presentes em suas consultas.

    A pista é que grandes quantidades de valores estáticos em INcláusulas aumentam o tempo de execução quase exponencialmente em consultas com muitos registros correspondentes, uma vez que basicamente NÃO USAM NENHUM ÍNDICE OU OTIMIZAÇÃO e geralmente terminam em varreduras completas de tabelas, nas quais IMHO a execução examinará cada registro/linha em sua tabela comparando-o com cada item em sua IN()lista UM por UM.

    A declaração como

    INSERT INTO humans_we_respect (id) VALUES (1),...,(10000) ON DUPLICATE KEY UPDATE expressed_interest=1;
    

    porém está usando o índice para localizar o registro e depois atualiza-lo, mesmo assim não destinado a este uso, ele rodará muito melhor devido ao uso de um índice no ID se houver e só fará uma pesquisa de índice para o registro em vez de milhares de comparações! Trabalhar diretamente com duas tabelas pode, no entanto, ser mais rápido se a lista de IDs for derivada de outra tabela no mesmo servidor, há mais otimizações que podem ser usadas e você não precisa transferir dados de e para o processo do servidor mysql.

    Assim como algumas informações extras:

    UPDATE humans_we_respect SET expressed_interest=1 WHERE id='1' OR id='2' OR ...
    

    é uma boa técnica para otimizar IN()consultas com contagens muito baixas de elementos, pois criará uma consulta de índice paralela para cada elemento, o que é ótimo para os primeiros elementos, mas diminuirá muito o desempenho com mais elementos e, em algum momento, atingirá o limite do analisador para otimização (IMHO, pode haver 255 elementos em uma consulta), ponto em que ele voltará a um ritmo lento ...

    • 3
  2. AbcAeffchen
    2014-08-26T12:43:22+08:002014-08-26T12:43:22+08:00

    Apenas um palpite:

    • Talvez o mysql verifique na primeira consulta o mysql verifica 17 milhões de linhas, se uma couber na INcláusula.
    • na segunda consulta você insere apenas 10000 linhas, verifica a chave por índice e atualiza as linhas.

    Se for esse o caso, você pode tentar reescrever a primeira consulta para UPDATE... WHERE id <= 10000, mas isso só funcionará se você realmente precisar atualizar 10.000 linhas com ids consecutivos. Talvez você também possa tentar UPDATE ... WHERE id = 1 OR id = 2 OR .... Mas isso só seria rápido, se o mysql pudesse otimizar isso internamente.

    • 0

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

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