Pergunta

Como este é o meu primeiro post, parece que só posso postar 1 link, então listei os sites aos quais estou me referindo na parte inferior. Em poucas palavras, meu objetivo é tornar o banco de dados retornar os resultados mais rapidamente, tentei incluir o máximo de informações relevantes possível para ajudar a enquadrar as perguntas na parte inferior da postagem.

Informações da máquina


8 processors
model name      : Intel(R) Xeon(R) CPU           E5440  @ 2.83GHz
cache size      : 6144 KB
cpu cores       : 4 

top - 17:11:48 up 35 days, 22:22, 10 users,  load average: 1.35, 4.89, 7.80
Tasks: 329 total,   1 running, 328 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni, 87.4%id, 12.5%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8173980k total,  5374348k used,  2799632k free,    30148k buffers
Swap: 16777208k total,  6385312k used, 10391896k free,  2615836k cached

No entanto, estamos olhando para mover a instalação do MySQL para uma máquina diferente no cluster que possui 256 GB de RAM

Informações da tabela


Minha tabela mysql se parece

CREATE TABLE ClusterMatches 
(
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    cluster_index INT, 
    matches LONGTEXT,
    tfidf FLOAT,
    INDEX(cluster_index)   
);

Possui aproximadamente 18 milhões de linhas, existem 1M de fósforos exclusivos de cluster_index e 6k. A consulta SQL que estou gerando no PHP se parece.

Consulta SQL


$sql_query="SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM Test2_ClusterMatches WHERE `cluster_index` in (".$clusters.")) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) DESC LIMIT 0, 10;";

onde $ cluster contém uma sequência de aproximadamente 3.000 cluster_index separados por vírgula. Essa consulta utiliza aproximadamente 50.000 linhas e leva aproximadamente 15s para ser executada, quando a mesma consulta é executada novamente, leva aproximadamente 1s para ser executado.

Uso


  1. O conteúdo da tabela pode ser assumido como estático.
  2. Baixo número de usuários simultâneos
  3. A consulta acima é atualmente a única consulta que será executada na mesa

Subconsiva


Com base nesta postagem [StackOverflow: cache/reutilize uma subconsência no MySQL] [1] e a melhoria no tempo de consulta, acredito que minha subconsência pode ser indexada.

mysql> EXPLAIN EXTENDED SELECT `matches`,sum(`tfidf`) FROM 
(SELECT * FROM ClusterMatches WHERE `cluster_index` in (1,2,...,3000) 
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) ASC LIMIT 0, 10;

+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
| id | select_type | table                | type  | possible_keys | key           | key_len | ref  | rows  | Extra                           |
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
|  1 | PRIMARY     |  derived2            | ALL   | NULL          | NULL          | NULL    | NULL | 48528 | Using temporary; Using filesort | 
|  2 | DERIVED     | ClusterMatches       | range | cluster_index | cluster_index | 5       | NULL | 53689 | Using where                     | 
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+

De acordo com este artigo mais antigo [otimizando o MySQL: consultas e índices] [2] em informações extras - as ruins para ver aqui são "usando temporário" e "usando o FileTort"

Informações de configuração do MySQL


O cache de consulta está disponível, mas efetivamente desativado, pois o tamanho está atualmente definido como zero


mysqladmin variables;
+---------------------------------+----------------------+
| Variable_name                   | Value                |
+---------------------------------+----------------------+
| bdb_cache_size                  | 8384512              | 
| binlog_cache_size               | 32768                | 
| expire_logs_days                | 0                    |
| have_query_cache                | YES                  | 
| flush                           | OFF                  |
| flush_time                      | 0                    |
| innodb_additional_mem_pool_size | 1048576              |
| innodb_autoextend_increment     | 8                    |
| innodb_buffer_pool_awe_mem_mb   | 0                    |
| innodb_buffer_pool_size         | 8388608              |
| join_buffer_size                | 131072               |
| key_buffer_size                 | 8384512              |
| key_cache_age_threshold         | 300                  |
| key_cache_block_size            | 1024                 |
| key_cache_division_limit        | 100                  |
| max_binlog_cache_size           | 18446744073709547520 | 
| sort_buffer_size                | 2097144              |
| table_cache                     | 64                   | 
| thread_cache_size               | 0                    | 
| query_cache_limit               | 1048576              |
| query_cache_min_res_unit        | 4096                 |
| query_cache_size                | 0                    |
| query_cache_type                | ON                   |
| query_cache_wlock_invalidate    | OFF                  |
| read_rnd_buffer_size            | 262144               |
+---------------------------------+----------------------+

Com base neste artigo sobre [MySQL Database Performance Turning] [3] Acredito que os valores que preciso para ajustar são

  1. tabela_cache
  2. key_buffer
  3. Sort_Buffer
  4. read_buffer_size
  5. registro_rnd_buffer (para grupo e ordem por termos)

Áreas identificadas para melhoria - ajustes de consulta MySQL


  1. Alterar o tipo de dados para correspondências para um índice que é um apontamento para outra tabela [MySQL realmente usará um formato de linha dinâmica se contiver campos de comprimento variável como texto ou blob, que, neste caso, significa que a classificação precisa ser feita no disco . A solução não é evitar esses tipos de dados, mas sim dividir esses campos em uma tabela associada.] [4
  2. Indexando o novo match_index feild para que o grupo matches Ocorre mais rápido, com base na declaração ["Você provavelmente deve criar índices para qualquer campo no qual esteja selecionando, agrupando, pedindo ou juntando -se."] [5

Ferramentas


Para ajustar o desempenho, pretendo usar

  1. Explique] [6] Fazendo referência ao [formato de saída] [7
  2. AB - Apache HTTP Server Benchmarking Tool] [8
  3. Perfil] [9] com [dados de log] [10

Tamanho futuro do banco de dados


O objetivo é construir um sistema que possa ter 1M de valores exclusivos do cluster_index 1M de correspondência exclusiva, aproximadamente 3.000.000.000 de linhas de tabela com um tempo de resposta à consulta de cerca de 0,5s (podemos adicionar mais RAM conforme necessário e distribuir o banco de dados pelo cluster)

Perguntas


  1. Acho que queremos manter todo o conjunto de registros em RAM para que a consulta não toque no disco, se mantermos todo o banco de dados no cache do MySQL, isso elimina a necessidade de memcachedb?
  2. Tentar manter todo o banco de dados no MySQL Cache é uma estratégia ruim, pois não é projetada para ser persistente? Algo como Memcachedb ou Redis seria uma abordagem melhor, se sim?
  3. A tabela temporária "resultado" é criada pela consulta automaticamente destruída quando a consulta terminar?
  4. Devemos mudar de Innodb para Myisam [como bom para leitura de dados pesados, onde o Innodb é bom para escrever pesado] [11]?
  5. Meu cache não parece estar zero na minha [Configuração do cache de consulta] [12], por que a consulta ocorre atualmente mais rapidamente na segunda vez que a executo?
  6. Posso reestruturar minha consulta para eliminar "usando temporário" e "usando o arquivo de file", devo usar uma junção em vez de uma subconsulta?
  7. Como você vê o tamanho do MySQL [cache de dados] [13]?
  8. Que tipo de tamanhos para os valores tabela_cache, key_buffer, sor_buffer, read_buffer_size, registro_rnd_buffer você sugeriria como ponto de partida?

Links


  • 1: stackoverflow.com/questions/658937/cache-re-use-a-subquery-in-mysql
  • 2: databasejournal.com/features/mysql/article.php/10897_1382791_4/optimizing-mysql-queries-and-indexes.htm
  • 3: Debianhelp.co.uk/mysqlperformance.htm
  • 4: 20bits.com/articles/10-tips-for-otimizing-mysql-queries-that-dont-suck/
  • 5: 20bits.com/articles/10-tips-for-otimizing-mysql-queries-that-dont-suck/
  • 6: dev.mysql.com/doc/refman/5.0/en/explain.html
  • 7: dev.mysql.com/doc/refman/5.0/en/explain-astput.html
  • 8: httpd.apache.org/docs/2.2/programs/ab.html
  • 9: mtop.sourceforge.net/
  • 10: dev.mysql.com/doc/refman/5.0/en/slow-query-log.html
  • 11: 20bits.com/articles/10-tips-for-otimizing-mysql-queries-that-dont-suck/
  • 12: dev.mysql.com/doc/refman/5.0/en/query-cache-configuration.html
  • 13: dev.mysql.com/tech-resources/articles/mysql-query-cache.html
Foi útil?

Solução

Mudando a tabela


Com base nos conselhos deste post sobre Como escolher índices para ordem e grupo por consultas A tabela agora se parece

CREATE TABLE ClusterMatches 
(
    cluster_index INT UNSIGNED, 
    match_index INT UNSIGNED,
    id INT NOT NULL AUTO_INCREMENT,
    tfidf FLOAT,
    PRIMARY KEY (match_index,cluster_index,id,tfidf)
);
CREATE TABLE MatchLookup 
(
    match_index INT UNSIGNED NOT NULL PRIMARY KEY,
    image_match TINYTEXT
);

Eliminando a subconeração

A consulta sem classificar os resultados pela soma (TFIDF) parece

SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;

Que elimina o uso temporário e o uso de arquivos

explain extended SELECT match_index, SUM(tfidf) FROM ClusterMatches 
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table                | type  | possible_keys | key     | key_len | ref  | rows  | Extra                    |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
|  1 | SIMPLE      | ClusterMatches       | range | PRIMARY       | PRIMARY | 4       | NULL | 14938 | Using where; Using index | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+

Problema de classificação

No entanto, se eu adicionar o pedido por soma (tfdif) em

SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index 
ORDER BY total DESC LIMIT 0,10;
+-------------+--------------------+
| match_index | total              |
+-------------+--------------------+
|         868 |   0.11126546561718 | 
|        4182 | 0.0238558370620012 | 
|        2162 | 0.0216601379215717 | 
|        1406 | 0.0191618576645851 | 
|        4239 | 0.0168981291353703 | 
|        1437 | 0.0160425212234259 | 
|        2599 | 0.0156466849148273 | 
|         394 | 0.0155945559963584 | 
|        3116 | 0.0151005545631051 | 
|        4028 | 0.0149106932803988 | 
+-------------+--------------------+
10 rows in set (0.03 sec)

O resultado é adequadamente rápido nessa escala, mas ter o Ordem por SUM (TFIDF) significa que ele usa o FileStort temporário e

explain extended SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches 
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY match_index 
ORDER BY total DESC LIMIT 0,10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
| id | select_type | table                | type  | possible_keys | key     | key_len | ref  | rows  | Extra                                                     |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
|  1 | SIMPLE      | ClusterMatches       | range | PRIMARY       | PRIMARY | 4       | NULL | 65369 | Using where; Using index; Using temporary; Using filesort | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+

Soluções possíveis?

Estou procurando uma solução que não use temporária ou setão, ao longo das linhas de

SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches 
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY cluster_index, match_index 
HAVING total>0.01 ORDER BY cluster_index;
Onde eu não preciso de codificar um limite para o total, alguma idéia?

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