Pregunta

Como este es mi primer post, parece que sólo puedo publicar 1 enlace por lo que he enumerado los sitios que me refiero en la parte inferior. En pocas palabras mi objetivo es hacer que la base de datos devuelve los resultados más rápido, he tratado de incluir toda la información relevante que pude pensar en al marco de ayuda a las preguntas en la parte inferior del poste.

Información de la 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

Sin embargo estamos buscando a mover la instalación de MySQL a una máquina diferente en el clúster que tiene 256 GB de RAM

Información de Tabla


Mi tabla MySQL parece

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

Tiene aproximadamente 18 millones de filas, hay 1M de 6K cluster_index único y partidos únicos. La consulta SQL estoy generando en 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;";

donde $ clúster contiene una cadena de aproximadamente 3,000 separados por comas de cluster_index. Esta consulta hace uso de aproximadamente 50.000 filas y tarda aproximadamente 15 segundos para correr, cuando la misma consulta se ejecuta de nuevo se tarda aproximadamente 1 s para funcionar.

Uso


  1. El contenido de la tabla se puede suponer que ser estático.
  2. bajo número de usuarios concurrentes
  3. La consulta anterior es actualmente la única consulta que se ejecuta en la tabla

Subconsulta

Sobre la base de este post [stackoverflow: Caché / Re-Uso de una subconsulta en MySQL]. [1] y la mejora en el tiempo de consulta Creo que mi subconsulta puede ser indexado

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 acuerdo con este artículo mayores [Optimización de MySQL: Consultas e índices] [2] en información adicional - los malos que ver aquí están "usando temporal" y "usando filesort"

Configuración de MySQL Info


caché de consultas está disponible, pero con eficacia apagado como el tamaño se establece actualmente en cero


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               |
+---------------------------------+----------------------+

Sobre la base de este artículo en [Rendimiento de inflexión base de datos MySQL] [3] Creo que los valores que tenga que modificar son

  1. table_cache
  2. key_buffer
  3. sort_buffer
  4. read_buffer_size
  5. record_rnd_buffer (por GROUP BY y ORDER BY términos)

ámbitos de mejora identificados - MySQL Query ajustes


  1. Cambiar el tipo de datos para los partidos a un índice que es un señalador int a otra mesa [MySQL efectivamente utilizar un formato de filas dinámico si contiene campos de longitud variable como TEXT o BLOB, que, en este caso, medios de clasificación necesidades para ser realizado en el disco. La solución no es a evitar estos tipos de datos, sino más bien para dividir off tales campos en una tabla asociada.] [4]
  2. Indexación de la nueva match_index FEILD para que el GROUP BY matches se produce más rápido, basado en la declaración [ "Probablemente debería crear índices para cualquier campo en el que está Selección, agrupación de pedidos, o se reúnan."] [5]

Herramientas


Para realizar pellizco planeo usar

  1. [Explicar] [6] haciendo referencia a [el formato de salida] [7]
  2. [ab - servidor Apache HTTP herramienta de evaluación comparativa] [8]
  3. [Profiling] [9] con [datos de registro] [10]

Futuro de base de datos Tamaño


El objetivo es construir un sistema que puede tener valores únicos partidos 1M valores cluster_index única 1M, aproximadamente 3000 millones filas de la tabla con un tiempo de respuesta a la consulta de alrededor de 0,5 s (podemos añadir más memoria RAM como sea necesario y distribuir la base de datos a través de el cluster)

Preguntas


  1. creo que queremos mantener todo el conjunto de registros en la memoria RAM por lo que la consulta duerma toca el disco, si mantenemos la base de datos en la caché de MySQL que eliminan la necesidad de memcachedb?
  2. ¿Está tratando de mantener la base de datos en la memoria caché de MySQL una mala estrategia, ya que no diseñado para ser persistente? ¿Podría algo así como memcachedb o Redis ser un mejor enfoque, si es así por qué?
  3. ¿Es la tabla temporal "número" que se crea por la consulta destruye automáticamente cuando termina la consulta?
  4. ¿Hay que cambiar de InnoDB a MyISAM [como su buena lectura para Heavy DATA donde como InnoDB es bueno para la pesada escritura] [11]?
  5. mi caché duerma parece estar en cero como en mi [Configuración de la caché de consultas] [12], ¿por qué la consulta actualmente ocurren más rápido la segunda vez que lo ejecuto?
  6. ¿Puedo reestructurar mi consulta para eliminar "el uso temporal" y "usando filesort" occuring, debo utilizar una unión en lugar de una subconsulta?
  7. ¿cómo ve el tamaño de la MySQL [caché de datos] [13]?
  8. ¿qué tipo de tamaños para los valores table_cache, key_buffer, sort_buffer, read_buffer_size, record_rnd_buffer sugeriría usted como punto de partida?

Enlaces


  • 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-optimizing-mysql-queries-that-dont-suck /
  • 5: 20bits.com/articles/10-tips-for-optimizing-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-output.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-optimizing-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
¿Fue útil?

Solución

Cambio de la tabla


Sobre la base de los consejos de este mensaje el ¿Cómo escoger los índices para el fin por el grupo y por las consultas la mesa ahora 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
);

La eliminación de Subconsulta

La consulta sin ordenar los resultados por la suma (TFIDF) parece

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

Lo cual elimina el uso temporal y utilizando filesort

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 | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+

Sorting Problema

Sin embargo, si añado el ORDER BY SUM (tfdif) en

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)

El resultado es adecuadamente rápido en esta escala pero tener la ORDER BY suma (TFIDF) significa que utiliza temporal y filesort

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 | 
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+

Posibles soluciones?

Estoy buscando una solución que no utiliza filesort temporal o, a lo largo de las líneas 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;
donde no necesita codificar un umbral para el total, alguna idea?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top