Subconsiadas e cache mysql para mesa de linha de 18m+
-
27-09-2019 - |
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
- O conteúdo da tabela pode ser assumido como estático.
- Baixo número de usuários simultâneos
- 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
- tabela_cache
- key_buffer
- Sort_Buffer
- read_buffer_size
- registro_rnd_buffer (para grupo e ordem por termos)
Áreas identificadas para melhoria - ajustes de consulta MySQL
- 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
- 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
- Explique] [6] Fazendo referência ao [formato de saída] [7
- AB - Apache HTTP Server Benchmarking Tool] [8
- 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
- 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?
- 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?
- A tabela temporária "resultado" é criada pela consulta automaticamente destruída quando a consulta terminar?
- Devemos mudar de Innodb para Myisam [como bom para leitura de dados pesados, onde o Innodb é bom para escrever pesado] [11]?
- 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?
- 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?
- Como você vê o tamanho do MySQL [cache de dados] [13]?
- 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
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?