La cuestión:
Tenemos un sitio social donde los miembros pueden clasificarse entre sí por compatibilidad o coincidencia. Esta user_match_ratings
tabla contiene más de 220 millones de filas (9 datos de conciertos o casi 20 conciertos en índices). Las consultas en esta tabla aparecen de forma rutinaria en slow.log (umbral> 2 segundos) y es la consulta lenta registrada con más frecuencia en el sistema:
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 1051
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 395357 group by rating;"
Query_time: 4 Lock_time: 0 Rows_sent: 3 Rows_examined: 1294
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 4182969 group by rating;"
Query_time: 3 Lock_time: 0 Rows_sent: 3 Rows_examined: 446
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 630148 group by rating;"
Query_time: 5 Lock_time: 0 Rows_sent: 3 Rows_examined: 3788
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1835698 group by rating;"
Query_time: 17 Lock_time: 0 Rows_sent: 3 Rows_examined: 4311
"select rating, count(*) as tally from user_match_ratings where rated_user_id = 1269322 group by rating;"
Versión de MySQL:
- versión de protocolo: 10
- versión: 5.0.77-log
- versión bdb: Sleepycat Software: Berkeley DB 4.1.24: (29 de enero de 2009)
- máquina de compilación de versiones: x86_64 versión_compilación_os: redhat-linux-gnu
Información de la mesa:
SHOW COLUMNS FROM user_match_ratings;
Da:
╔═══════════════╦════════════╦════╦═════╦════════╦════════════════╗
║ id ║ int(11) ║ NO ║ PRI ║ NULL ║ auto_increment ║
║ rater_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rated_user_id ║ int(11) ║ NO ║ MUL ║ NULL ║ ║
║ rating ║ varchar(1) ║ NO ║ ║ NULL ║ ║
║ created_at ║ datetime ║ NO ║ ║ NULL ║ ║
╚═══════════════╩════════════╩════╩═════╩════════╩════════════════╝
Consulta de muestra:
select * from mutual_match_ratings where id=221673540;
da:
╔═══════════╦═══════════════╦═══════════════╦════════╦══════════════════════╗
║ id ║ rater_user_id ║ rated_user_id ║ rating ║ created_at ║
╠═══════════╬═══════════════╬═══════════════╬════════╬══════════════════════╣
║ 221673540 ║ 5699713 ║ 3890950 ║ N ║ 2013-04-09 13:00:38 ║
╚═══════════╩═══════════════╩═══════════════╩════════╩══════════════════════╝
Índices
La tabla tiene 3 índices configurados:
- índice único en
rated_user_id
- índice compuesto en
rater_user_id
ycreated_at
- índice compuesto en
rated_user_id
yrater_user_id
muestra el índice de user_match_ratings;
da:
╔════════════════════╦════════════╦═══════════════════════════╦══════════════╦═══════════════╦═══════════╦═════════════╦══════════╦════════╦═════════════════════════╦════════════╦══════════════════╗
║ Table ║ Non_unique ║ Key_name ║ Seq_in_index ║ Column_name ║ Collation ║ Cardinality ║ Sub_part ║ Packed ║ Null ║ Index_type ║ Comment ║
╠════════════════════╬════════════╬═══════════════════════════╬══════════════╬═══════════════╬═══════════╬═════════════╬══════════╬════════╬═════════════════════════╬════════════╬══════════════════╣
║ user_match_ratings ║ 0 ║ PRIMARY ║ 1 ║ id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 1 ║ rater_user_id ║ A ║ 11039059 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index1 ║ 2 ║ created_at ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 1 ║ rated_user_id ║ A ║ 4014203 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index2 ║ 2 ║ rater_user_id ║ A ║ 220781193 ║ NULL ║ NULL ║ BTREE ║ ║ ║
║ user_match_ratings ║ 1 ║ user_match_ratings_index3 ║ 1 ║ rated_user_id ║ A ║ 2480687 ║ NULL ║ NULL ║ BTREE ║ ║ ║
╚════════════════════╩════════════╩═══════════════════════════╩══════════════╩═══════════════╩═══════════╩═════════════╩══════════╩════════╩═════════════════════════╩════════════╩══════════════════╝
Incluso con los índices, estas consultas son lentas.
Mi pregunta:
¿Separar esta tabla / datos en otra base de datos en un servidor que tenga suficiente memoria RAM para almacenar estos datos en la memoria aceleraría estas consultas? ¿Hay alguna forma de configurar las tablas / índices que podamos mejorar para agilizar estas consultas?
Actualmente tenemos 16GB de memoria; sin embargo, estamos buscando actualizar la máquina existente a 32 GB o agregar una nueva máquina con al menos esa cantidad, tal vez unidades de estado sólido también.
fuente
SELECT QUERY
. ¿Podría sugerirme? PD: Tu pregunta me obligó a unirme a esta comunidad (y);)Respuestas:
Reflexiones sobre el tema, en orden aleatorio:
El índice obvia para esta consulta es:
(rated_user_id, rating)
. Una consulta que obtiene datos solo para uno de los millones de usuarios y necesita 17 segundos está haciendo algo mal: leer del(rated_user_id, rater_user_id)
índice y luego leer de la tabla los valores (de cientos a miles) para larating
columna, yarating
que no está en ningún índice. Por lo tanto, la consulta tiene que leer muchas filas de la tabla que se encuentran en muchas ubicaciones de disco diferentes.Antes de comenzar a agregar numerosos índices en las tablas, intente analizar el rendimiento de toda la base de datos, todo el conjunto de consultas lentas, examine nuevamente las opciones de los tipos de datos, el motor que usa y los ajustes de configuración.
Considere la posibilidad de pasar a una versión más nueva de MySQL, 5.1, 5.5 o incluso 5.6 (también: versiones de Percona y MariaDB). Se han corregido varios beneficios a medida que se corrigieron los errores, se mejoró el optimizador y puede establecer el umbral bajo para consultas lentas en menos de 1 segundo (como 10 milisegundos). Esto le dará información mucho mejor sobre consultas lentas.
La elección del tipo de datos
rating
es extraña.VARCHAR(1)
? ¿Por qué noCHAR(1)
? ¿Por qué noTINYINT
? Esto le ahorrará algo de espacio, tanto en la tabla como en los índices que (incluirán) esa columna. Una columna varchar (1) necesita un byte más sobre char (1) y si son utf8, las columnas (var) char necesitarán 3 (o 4) bytes, en lugar de 1 (tinyint).fuente
Manejé tablas para el gobierno alemán con a veces 60 millones de registros.
Teníamos muchas de estas mesas.
Y necesitábamos saber muchas veces las filas totales de una tabla.
Después de hablar con los programadores de Oracle y Microsoft, no estábamos tan contentos ...
Entonces, nosotros, el grupo de programadores de bases de datos, decidimos que en cada tabla hay un registro, siempre el registro en el que se almacenan los números de registro totales. Actualizamos este número, dependiendo de las filas INSERT o DELETE.
Intentamos todas las otras formas. Esta es, con mucho, la forma más rápida.
Lo utilizamos ahora desde 1998 y nunca tuvimos un número incorrecto de filas, en todas nuestras tablas de registro multimillonarias.
fuente
count(*)
tiene algunas mejoras.Intentaré particionar los tipos de calificación, como:
mutual_match_ratings_N, mutual_match_ratings_S, etc.
Debería realizar una consulta para cada tipo, pero tal vez sea más rápido que a la inversa. Darle una oportunidad.
Esto supone que tiene un número fijo de tipos de calificación y que no necesita esta tabla para otras consultas que serían peores con esta nueva estructura.
Si ese es el caso, debe buscar otro enfoque o mantener dos copias de la tabla (su tabla inicial y las particionadas) si eso es asequible en términos de espacio y facilidad de mantenimiento (o lógica de aplicación).
fuente