Como usar o atraso de inserção com o mecanismo InnoDB e usar menos conexão para instruções de inserção?

10

Estou trabalhando em um aplicativo que envolve muitas gravações de banco de dados, aproximadamente ~ 70% inserções e 30% leituras. Essa proporção também incluiria atualizações que considero uma leitura e uma gravação. Através de instruções de inserção, vários clientes inserem dados no banco de dados através da instrução de inserção abaixo:

$mysqli->prepare("INSERT INTO `track` (user, uniq_name, ad_name, ad_delay_time ) values (?, ?, ?, ?)");

A questão é se devo usar o insert_delay ou o mecanismo mysqli_multi_query porque a instrução insert utiliza ~ 100% da CPU no servidor. Estou usando o mecanismo InnoDB no meu banco de dados, portanto, a inserção com atraso não é possível. A inserção no servidor é ~ 36k / h e 99,89% de leitura, também estou usando a instrução select, recupera os dados sete vezes em uma única consulta , essa consulta leva 150 segundos no servidor para executar. Que tipo de técnica ou mecanismo posso usar para esta tarefa? A memória do meu servidor é de 2 GB, devo expandir a memória ?. Dê uma olhada neste problema, qualquer sugestão será grata a mim.

Estrutura da tabela:

+-----------------+--------------+------+-----+-------------------+----------------+
| Field           | Type         | Null | Key | Default           | Extra          |
+-----------------+--------------+------+-----+-------------------+----------------+
| id              | int(11)      | NO   | PRI | NULL              | auto_increment |
| user            | varchar(100) | NO   |     | NULL              |                |
| uniq_name       | varchar(200) | NO   |     | NULL              |                |
| ad_name         | varchar(200) | NO   |     | NULL              |                |
| ad_delay_time   | int(11)      | NO   |     | NULL              |                |
| track_time      | timestamp    | NO   | MUL | CURRENT_TIMESTAMP |                |
+-----------------+--------------+------+-----+-------------------+----------------+

O status atual do meu banco de dados mostra 41k inserções (gravações), o que é muito lento para o meu banco de dados.

status do banco de dados

Shashank
fonte
Você pode fornecer a definição da tabela? (todas as colunas, e tipos de dados de índices)
ypercubeᵀᴹ
Você pode dar um breve trecho do seu SHOW FULL PROCESSLISTquando estiver usando 100% da CPU? Quantas conexões você está permitindo e quantas são feitas durante esse período?
Derek Downey #
Por favor, execute essas duas consultas: SHOW GLOBAL VARIABLES LIKE 'innodb%';e SELECT VERSION();e exibir sua saída.
RolandoMySQLDBA
Forneça o número de inserções por segundo que você está executando.
dabest1
Seu código é muito suscetível à injeção de SQL. Use instruções preparadas e valores parametrizados.
Aaron Brown

Respostas:

11

Como você tem mais gravações do que leituras, gostaria de recomendar o seguinte

O ajuste decente do InnoDB seria a chave

Buffer Pool (dimensionado por innodb_buffer_pool_size )

Como o InnoDB não suporta INSERT DELAYED , o uso de um grande buffer pool do InnoDB é a coisa mais próxima que você pode obter de INSERT DELAYED. Todos os DML (INSERTs, UPDATEs e DELETEs) seriam armazenados em cache no buffer pool do InnoDB. As informações transacionais para as gravações são gravadas imediatamente nos refazer logs (ib_logfile0, ib_logfile1). As gravações postadas no Buffer Pool são periodicamente descarregadas da memória para o disco via ibdata1 (InsertBuffer para índices secundários, buffer de gravação dupla). Quanto maior o pool de buffers, maior a quantidade de INSERTs que podem ser armazenados em cache. Em um sistema com 8 GB ou mais de RAM, use 75-80% da RAM como tamanho innodb_buffer_pool_spool. Em um sistema com muito pouca RAM, 25% (para acomodar o sistema operacional).

CAVEAT: você pode definir innodb_doublewrite como 0 para acelerar ainda mais as gravações, mas com o risco de integridade dos dados. Você também pode acelerar as coisas configurando innodb_flush_method como O_DIRECT para impedir o cache do InnoDB no sistema operacional.

Refazer logs (dimensionado por innodb_log_file_size )

Por padrão, os logs de refazer são denominados ib_logfile0 e ib_logfile1 e teriam 5 MB cada. O tamanho deve ser 25% do tamanho innodb_buffer_pool_spool. Se os logs de refazer já existirem, adicione a nova configuração no my.cnf, encerre o mysql, exclua-os e reinicie o mysql .

Buffer de log (dimensionado por innodb_log_buffer_size )

O buffer de log retém as alterações na RAM antes de liberá-las nos logs de refazer. O padrão é 8M. Quanto maior o buffer de log, menor a E / S de disco. Tenha cuidado com transações muito grandes, pois isso pode diminuir os COMMITs em milissegundos.

Acessando várias CPUs

O MySQL 5.5 e o MySQL 5.1 InnoDB Plugin têm configurações para que o InnoDB Storage Engine acesse várias CPUs. Aqui estão as opções que você precisa definir:

  • innodb_thread_concurrency define o limite superior do número de threads simultâneos que o InnoDB pode manter aberto. Geralmente, é recomendável definir para isso é (2 X número de CPUs) + número de discos. No ano passado, aprendi em primeira mão com a Percona NYC Conference que você deve definir como 0 para alertar o InnoDB Storage Engine para encontrar o melhor número de threads para o ambiente em que está executando.
  • innodb_concurrency_tickets define o número de encadeamentos que podem ignorar a verificação de simultaneidade com impunidade. Depois que esse limite é atingido, a verificação de simultaneidade de encadeamento se torna a norma novamente.
  • innodb_commit_concurrency define o número de transações simultâneas que podem ser confirmadas. Como o padrão é 0, não definir isso permite que qualquer número de transações seja confirmado simultaneamente.
  • innodb_thread_sleep_delay define o número de milissegundos que um encadeamento do InnoDB pode estar inativo antes de entrar novamente na fila do InnoDB. O padrão é 10000 (10 s).
  • innodb_read_io_threads (defina como 3000) e innodb_write_io_threads (defina como 7000) (ambos desde o MySQL 5.1.38) aloque o número especificado de threads para leituras e gravações. O padrão é 4 e o máximo é 64. Defina-os como 64. Além disso, defina o innodb_io_capacity como 10000.

Atualize para o MySQL 5.5

Se você possui o MySQL 5.0, atualize para o MySQL 5.5. Se você possui o MySQL 5.1.37 ou anterior, atualize para o MySQL 5.5. Se você possui o MySQL 5.1.38 ou superior e deseja permanecer no MySQL 5.1, instale o plug-in InnoDB. Dessa forma, você pode tirar proveito de todas as CPUs do InnoDB.

RolandoMySQLDBA
fonte
minha memória do servidor é de 2 GB; portanto, de acordo com a memória, defino o buffer pool do innodb para 500M e os arquivos de log em 25% para o pool, também defino o buffer de log para 64M. Mas o servidor ainda está pesado. Devo atualizar a memória? Além disso, meu servidor está no ubuntu de 32 bits, para que no máximo eu possa configurar a memória para 4 GB.
Shashank 01/12/12
Se o servidor for apenas para MySQL (sem apache, sem PHP), o innodb_buffer_pool_size pode aumentar 75% de 2 GB, ou seja, 1536M. Se você atualizar para 4 GB, o innodb_buffer_pool_size pode ser 3G. Os arquivos de log devem ter 25% do conjunto de buffers, como você afirmou.
RolandoMySQLDBA
O servidor está executando o apache2, mysql e php, devo procurar memória de atualização nessa situação ou existe alguma solução ideal, exceto para o pool de buffers do innodb?
Shashank 02/12/12
Esse cara não concorda com você: percona.com/blog/2008/11/21/… Difícil discutir com Percona.
Zenexer 23/01
Rolando - sugira que você adicione à resposta com atualizações para 5.6 e 5.7. Os padrões foram alterados; outras configurações estão disponíveis; etc. Talvez inclua dicas para Percona e MariaDB e 8.0.
Rick James
2

INT (2) ainda usa 4 bytes - talvez você quis dizer TINYINT UNSIGNED?

Quantos valores diferentes em setno? Se for pequeno, o KEY (setno) nunca será usado. INSERTing precisa atualizar esse índice; remover a tecla acelerará a inserção de alguns.

CHAR (10) - flagSempre tem 10 caracteres? E no utf8? Talvez você possa usar o sinalizador VARCHAR (10) CHARACTER SET ascii

Lote suas inserções - 100 de cada vez serão executadas 10 vezes mais rápido. (Mais de 100 está entrando em 'retornos decrescentes'.)

Qual é o valor da confirmação automática? Você está agrupando cada INSERT em BEGIN ... COMMIT? Qual é o valor de innodb_flush_log_at_trx_commit?

Rick James
fonte
Como faço para inserir em lote se os dados são inseridos via fonte externa, como clientes diferentes com valores diferentes ... é confiável se eu usei: codeinserir em t_name (col1, col2, col3) valores (val1, val2, val3), (val1, val2, val3), (val1, val2, val3), (val1, val2, val3), (val1, val2, val3); code
Shashank
1

Configure uma fila. O aplicativo gravaria uma fila 1 linha por vez e, em seguida, retiraria as linhas e as inseria em um banco de dados em lote com base no número de linhas da quantidade de tempo passado desde a última inserção.

Vi onde o lote das pastilhas 10.000 de cada vez é o mais rápido, então você precisaria testar para encontrar um ponto ideal.

Você pode criar seu próprio sistema de filas simples ou usar um existente. Aqui estão alguns exemplos: HornetQ e File :: Queue . Aqui está um post no SE listando outras boas opções: Filas de mensagens em perl, php, python .

dabest1
fonte
Concordo com essa abordagem: estou enviando lotes ~ 1500 inserções a cada 5 segundos em um aplicativo e ele é sub-segundo. O mysql parece ter algum mecanismo implementado internamente que faz com que as inserções em lote aconteçam muito rapidamente.
11264 Don Wool