Estou com dificuldades para importar em massa uma tabela InnoDB bastante grande, composta por aproximadamente 10 milhões de linhas (ou 7 GB) (que para mim é a maior tabela com a qual trabalhei até agora).
Eu fiz algumas pesquisas sobre como melhorar a velocidade de importação do Inno e, no momento, minha configuração fica assim:
/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8
import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Os dados são fornecidos em um CSV
arquivo.
Atualmente, testei minhas configurações com 'dumps de teste' menores, com 2 milhões, 3 milhões de ... linhas cada e uso time import_script.sh
para comparar o desempenho.
A desvantagem é que eu só tenho um tempo de execução geral, portanto, tenho que esperar a importação completa terminar para obter um resultado.
Meus resultados até agora:
- 10 000 linhas: <1 segundo
- 100 000 linhas: 10 segundos
- 300 000 linhas: 40 segundos
- 2 milhões de linhas: 18 minutos
- 3 milhões de linhas: 26 minutos
- 4 milhões de linhas: (cancelada após 2 horas)
Parece que não existe uma solução para o 'livro de receitas' e é preciso descobrir a combinação ideal de configurações por conta própria.
Além de sugestões sobre o que mudar na minha configuração, eu também gostaria muito de obter mais informações sobre como eu poderia avaliar melhor o processo de importação / obter mais informações sobre o que está acontecendo e onde pode estar o gargalo.
Tentei ler a documentação para as configurações que estou alterando, mas, novamente, não conheço nenhum efeito colateral e se posso diminuir o desempenho com um valor mal escolhido.
No momento, gostaria de tentar uma sugestão do chat para usar MyISAM
durante a importação e alterar o mecanismo da tabela posteriormente.
Gostaria de tentar isso, mas, no momento, minha DROP TABLE
consulta também leva horas para terminar. (Que parece outro indicador, minha configuração é menor que a ideal).
Informações adicionais:
A máquina que estou usando no momento possui 8 GB de RAM e um disco rígido híbrido de estado sólido com 5400 RPM.
Embora também pretendamos remover dados obsoletos da tabela em questão, ainda preciso de uma importação um pouco rápida para
a) testar automatic data cleanup feature
durante o desenvolvimento
eb) no caso de nosso servidor travar, gostaríamos de usar nosso segundo servidor como um substituto (que precisa ser dados atualizados, a última importação levou mais de 24 horas)
mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
Table: monster
Create Table: CREATE TABLE `monster` (
`monster_id` int(11) NOT NULL AUTO_INCREMENT,
`ext_monster_id` int(11) NOT NULL DEFAULT '0',
`some_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(250) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`postcode` varchar(20) NOT NULL,
`city` varchar(100) NOT NULL,
`country` int(11) NOT NULL DEFAULT '0',
`address_hash` varchar(250) NOT NULL,
`lon` float(10,6) NOT NULL,
`lat` float(10,6) NOT NULL,
`ip_address` varchar(40) NOT NULL,
`cookie` int(11) NOT NULL DEFAULT '0',
`party_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '2',
`creation_date` datetime NOT NULL,
`someflag` tinyint(1) NOT NULL DEFAULT '0',
`someflag2` tinyint(4) NOT NULL,
`upload_id` int(11) NOT NULL DEFAULT '0',
`news1` tinyint(4) NOT NULL DEFAULT '0',
`news2` tinyint(4) NOT NULL,
`someother_id` int(11) NOT NULL DEFAULT '0',
`note` varchar(2500) NOT NULL,
`referer` text NOT NULL,
`subscription` int(11) DEFAULT '0',
`hash` varchar(32) DEFAULT NULL,
`thumbs1` int(11) NOT NULL DEFAULT '0',
`thumbs2` int(11) NOT NULL DEFAULT '0',
`thumbs3` int(11) NOT NULL DEFAULT '0',
`neighbours` tinyint(4) NOT NULL DEFAULT '0',
`relevance` int(11) NOT NULL,
PRIMARY KEY (`monster_id`),
KEY `party_id` (`party_id`),
KEY `creation_date` (`creation_date`),
KEY `email` (`email`(4)),
KEY `hash` (`hash`(8)),
KEY `address_hash` (`address_hash`(8)),
KEY `thumbs3` (`thumbs3`),
KEY `ext_monster_id` (`ext_monster_id`),
KEY `status` (`status`),
KEY `note` (`note`(4)),
KEY `postcode` (`postcode`),
KEY `some_id` (`some_id`),
KEY `cookie` (`cookie`),
KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
fonte
SHOW CREATE TABLE yourtable\G
para nos mostrar a estrutura da tabela dessa tabela de 10 milhões de linhas.innodb_doublewrite = 0
), sua instalação do MySQL não é protegida contra falhas: se você tiver uma falha de energia (não uma falha no MySQL), seus dados poderão ser corrompidos silenciosamente.Respostas:
Primeiro, você precisa saber o que está fazendo com o InnoDB ao inserir milhões de linhas em uma tabela do InnoDB. Vamos dar uma olhada na arquitetura do InnoDB.
No canto superior esquerdo, há uma ilustração do InnoDB Buffer Pool. Observe que há uma seção dedicada ao buffer de inserção. O que isso faz? É recomendado migrar alterações para índices secundários do Buffer Pool para o Insert Buffer dentro do espaço de tabela do sistema (também conhecido como ibdata1). Por padrão, innodb_change_buffer_max_size está definido como 25. Isso significa que até 25% do Buffer Pool pode ser usado para processar índices secundários.
No seu caso, você tem 6,935 GB para o buffer pool InnoDB. Um máximo de 1,734 GB será usado para processar seus índices secundários.
Agora, olhe para a sua mesa. Você tem 13 índices secundários. Cada linha que você processa deve gerar uma entrada de índice secundária, associá-la à chave primária da linha e enviá-las como um par do Insert Buffer no Buffer Pool para o Insert Buffer no ibdata1. Isso acontece 13 vezes com cada linha. Multiplique isso por 10 milhões e você quase sentirá um gargalo chegando.
Não esqueça que importar 10 milhões de linhas em uma única transação acumulará tudo em um segmento de reversão e preencherá o espaço UNDO no ibdata1.
SUGESTÕES
SUGESTÃO # 1
Minha primeira sugestão para importar essa tabela bastante grande seria
SUGESTÃO # 2
Livre-se de índices duplicados. No seu caso, você tem
Ambos os índices começam com
party_id
, você pode aumentar o processamento do índice secundário em pelo menos 7,6%, livrando um índice de 13. Você precisa executar eventualmenteSUGESTÃO # 3
Livre-se dos índices que você não usa. Examine o código do seu aplicativo e veja se suas consultas usam todos os índices. Você pode examinar o uso do pt-index para permitir sugerir quais índices não estão sendo usados.
SUGESTÃO # 4
Você deve aumentar o innodb_log_buffer_size para 64M, pois o padrão é 8M. Um buffer de log maior pode aumentar o desempenho de E / S de gravação do InnoDB.
EPÍLOGO
Colocando as duas primeiras sugestões, faça o seguinte:
party_id
índiceTalvez o seguinte possa ajudar
Importe os dados para
monster
. Então, execute issoDE UMA CHANCE !!!
ALTERNATIVO
Você pode criar uma tabela chamada
monster_csv
como uma tabela MyISAM sem índices e fazer o seguinte:Importe seus dados para
monster_csv
. Em seguida, use o mysqldump para criar outra importaçãoO arquivo mysqldump
data.sql
estenderá os comandos INSERT, importando 10.000 a 20.000 linhas por vez.Agora, basta carregar o mysqldump
Por fim, livre-se da tabela MyISAM
fonte
monster
tabela) em menos de 20 minutos quando não havia chaves nas tabelas do InnoDB. A adição de chaves levou aprox. outros 20 min. Eu diria que isso praticamente resolve meu problema neste caso. Muito obrigado!Eu queria escrever um comentário (como essa não é uma resposta definitiva), mas ficou muito tempo:
Vou dar-lhe vários conselhos amplos e podemos entrar em detalhes para cada um, se você quiser:
Lembre-se de que alguns deles não são seguros ou aconselháveis para não importações (operação normal).
fonte
SET SESSION tx_isolation='READ-UNCOMMITTED';
(útil apenas se você importar com vários encadeamentos em paralelo) e o @ypercube comenta sobre a inserção em lotes. Você tem um exemplo completo aqui: mysqlperformanceblog.com/2008/07/03/… Verifique se você está aproveitando todos os recursos das versões mais recentes do InnoDB: mysqlperformanceblog.com/2011/01/07/…A maioria das boas dicas foi dada até agora, mas sem muitas explicações para as melhores. Vou dar mais detalhes.
Primeiro, atrasar a criação do índice é bom, com detalhes suficientes em outras respostas. Eu não voltarei a isso.
Um arquivo de log InnoDB maior ajudará bastante (se você estiver usando o MySQL 5.6, pois não é possível aumentá-lo no MySQL 5.5). Você está inserindo 7 GB de dados, eu recomendaria um tamanho total de log de pelo menos 8 GB (mantenha
innodb_log_files_in_group
no padrão (2) e bumpinnodb_log_file_size
em 4 GB). Esses 8 GB não são precisos: devem ter pelo menos o tamanho da importação no log do REDO e provavelmente dobrar ou quadruplicar esse tamanho. O raciocínio por trás do tamanho do log do InnoDB aumenta que, quando o log ficar quase cheio, o InnoDB começará a liberar agressivamente seu buffer pool em disco para evitar o log de preenchimento (quando o log estiver cheio, o InnoDB não poderá gravar nenhum banco de dados até que algum páginas do buffer pool são gravadas no disco).Um arquivo de log InnoDB maior ajudará você, mas você também deve inserir na ordem da chave primária (classifique seu arquivo antes de inseri-lo). Se você inserir na ordem da chave primária, o InnoDB preencherá uma página, depois outra e assim por diante. Se você não inserir na ordem da chave primária, sua próxima inserção poderá acabar em uma página cheia e ocorrerá uma "divisão de página". Essa divisão de página será cara para o InnoDB e retardará sua importação.
Você já tem um buffer pool tão grande quanto sua RAM permite e, se sua tabela não couber nele, não há muito o que fazer, exceto comprar mais RAM. Porém, se a tabela se encaixar no buffer pool, mas for maior que 75% do buffer pool, tente aumentar
innodb_max_dirty_pages_pct
para 85 ou 95 durante a importação (o valor padrão é 75). Esse parâmetro de configuração informa ao InnoDB para iniciar a limpeza agressiva do buffer pool quando a porcentagem de páginas sujas atingir esse limite. Ao aumentar esse parâmetro (e se você tiver sorte com o tamanho dos dados), poderá evitar E / S agressivas durante a importação e atrasar essas E / S para mais tarde.Talvez (e isso seja um palpite) importar seus dados em muitas transações pequenas o ajude. Não sei exatamente como o log do REDO é criado, mas se ele estiver armazenado em buffer na RAM (e no disco quando seria necessária muita RAM) enquanto a transação estiver progredindo, você poderá acabar com E / S desnecessárias. Você pode tentar o seguinte: assim que seu arquivo for classificado, divida-o em vários blocos (tente com 16 MB e outros tamanhos) e importe-os um por um. Isso também permitiria controlar o andamento da sua importação. Se você não deseja que seus dados fiquem parcialmente visíveis para outro leitor enquanto importa, você pode importar usando um nome de tabela diferente, criar os índices posteriormente e renomear a tabela.
Sobre o seu disco híbrido SSD / 5400RPM, eu não sei sobre eles e como otimizar isso. O 5400RPM parece lento para um banco de dados, mas talvez o SSD esteja evitando isso. Talvez você esteja preenchendo a parte SSD do seu disco com gravações sequenciais no log do REDO e o SSD está prejudicando o desempenho. Eu não sei.
Uma dica ruim que você não deve tentar (ou tenha cuidado) é a seguinte: não use multi-thread: será muito difícil otimizar para evitar divisões de página no InnoDB. Se você deseja usar multithread, insira em tabelas diferentes (ou em diferentes partições da mesma tabela).
Se você está pensando em multithread, talvez tenha um computador com soquete múltiplo (NUMA). Nesse caso, evite o problema de insanidade de troca do MySQL .
Se você estiver usando o MySQL 5.5, atualize para o MySQL 5.6: ele tem a opção de aumentar o tamanho do log REDO e possui melhores algoritmos de liberação do buffer pool.
Boa sorte com sua importação.
fonte