2009-02-04 7 views
6

tengo los 3 siguientes tablas en una base de datos MySQL 4.x:consulta SQL: une interno optimización entre grandes mesas

  • hosts: (300.000 registros)
    • id (UNSIGNED INT) PRIMARY KEY nombre
    • (VARCHAR 100)
  • caminos: (6.000.000 registros)
    • id (UNSIGNED INT) PRIMARY KEY
    • nombre (VARCHAR 100)
  • URLs: (7.000.000 registros)
    • anfitrión (unsigned int) PRIMARY KEY < - - enlaces a hosts.id
    • ruta (INT_SIGNED) PRIMARY KEY < --- enlaces a paths.id

Como puede ver, el esquema es realmente simple pero el problema es la cantidad de datos en estas tablas.

Ésta es la consulta que estoy corriendo:

SELECT CONCAT(H.name, P.name) 
FROM hosts AS H 
INNER JOIN urls as U ON H.id = U.host 
INNER JOIN paths AS P ON U.path = P.id; 

Esta consulta funciona perfectamente bien, pero tarda 50 minutos para correr. ¿Alguien tiene alguna idea sobre cómo podría acelerar esa consulta?

Gracias de antemano. Nicolas

Respuesta

1

Por un lado yo no haría el CONCAT en la consulta. Hazlo afuera.

Pero en realidad su consulta se ejecuta lentamente porque está recuperando millones de filas.

5

¿Quizás debería incluir una cláusula WHERE? ¿O realmente necesitas TODOS los datos?

1

¿Ya ha declarado algunos índices en los atributos de combinación?

PS: Ver here [enlace roto] para índices de MySQL 4.x

+0

En realidad, si realmente desea obtener todas las filas devueltas, los índices pueden no ser útiles. Hacer una búsqueda de índice para cada valor en la tabla es probablemente más lento que escanear completamente las tablas y mezclarlas o combinarlas. –

+0

Veo varios 100 megabytes de datos. Si todo encaja en la memoria, tienes razón. Pero un DBMS adecuado (y supongo que incluso MySQL 4.x, ya que es lo suficientemente adecuado) ignorará los índices existentes por sí mismo. – Leonidas

1

intente optimizar sus tablas antes de ejecutar la consulta:

optimize table hosts, paths, urls; 

Te puede ahorrar algo de tiempo, especialmente si las filas se han eliminado de las mesas. (vea here para obtener más información sobre OPTIMIZE)

0

El concat definitivamente le está frenando. ¿Podemos ver los resultados de una explicación de mysql sobre esto?Documentation Link

Lo más importante es intentar y obtener solo los datos que necesita. Si puede obtener menos registros que lo aceleren tanto como cualquier otra cosa. Pero una explicación de MySQL debería ayudarnos a ver si algún índice ayudaría.

1

Intentaré crear una nueva tabla con los datos que desea obtener. Hacer esto significa que pierdes algunos datos reales, pero ganas con rapidez. ¿Podría esta idea ser similar a OLAP o algo así?

Por supuesto, tiene que hacer una actualización (diaria o lo que sea) de esta tabla.

+0

Sí, una "vista materializada" sería recomendable, si no necesita los datos más recientes todo el tiempo. – Leonidas

4

Esto me parece un caso en el que el uso demasiado celoso de claves sustitutas te está ralentizando. Si las tablas fueron:

  • anfitriones:

    • nombre (VARCHAR 100) CLAVE PRIMARIA
  • caminos:

    • nombre (VARCHAR 100) de clave principal
  • URLs:

    • anfitrión (VARCHAR 100) de clave primaria < --- enlaces a hosts.name
    • camino (VARCHAR 100) de clave primaria < --- enlaces a paths.name

Entonces su consulta requeriría ninguna une en absoluto:

SELECT CONCAT(U.host, U.path) FROM urls U; 

Es cierto que las URL de tabla ocuparía más espacio en disco, pero ¿eso importa?

EDITAR: Pensándolo bien, ¿cuál es el objetivo de la tabla PATHS de todos modos? ¿Con qué frecuencia diferentes hosts comparten los mismos caminos?

por qué no:

  • anfitriones:

    • nombre (VARCHAR 100) CLAVE PRIMARIA
  • direcciones:

    • anfitrionas (VARCHAR 100) PRIMARIO KEY < --- enlaces a hosts.nombrar
    • ruta (VARCHAR 100) CLAVE PRIMARIA < --- ningún vínculo con cualquier

Edit2: O si realmente necesidad la clave sustituta rural por:

  • hosts:

    • Identificación del número entero PRIMARY KEY nombre
    • (VARCHAR 100)
  • URLs:

    • anfitrión entero de clave primaria < --- enlaces a hosts.name
    • camino (VARCHAR 100) PRIMARIA LLAVE < --- sin enlace a ninguna parte

    SELECCIONE CONCAT (H.name, U.path) FROM urls U UNIR hosts H ON H.id = U.host;

+0

Estaba a punto de agregar una respuesta que decía lo mismo que la parte "En segundo pensamiento". –

+0

De acuerdo, solo por interés, si ese es su punto de vista, ¿por qué usar una base de datos relacional? Su intención es exactamente lo opuesto a lo que aconsejo a todos mis clientes. Todo lo que puedo decir es * ARGH !!! * – MatBailie

+0

Demios, compadezco a tus clientes si insistes en las llaves sustitutas en TODAS las tablas. Las bases de datos relacionales funcionan igual de bien con las claves naturales, a veces incluso mejor. "ARGH !!!" ¡en efecto! –

0

Entiendo que desea una lista completa de direcciones URL, que son 7 millones de registros. Quizás as sugested by Mitch considere usar la cláusula WHERE para filtrar sus resultados. Tal vez el tiempo se relaciona principalmente con el retraso en los registros que muestran

tiempo de comprobación para esta consulta

select count(*) 
FROM hosts AS H 
INNER JOIN urls as U ON H.id = U.host 
INNER JOIN paths AS P ON U.path = P.id 

Si esto sigue siendo lenta me gustaría ir para comprobar la temporización de select count (*) de urls

continuación

select count(*) 
from urls u 
inner join hosts h on u.host = h.id 

continuación

select count(*) 
from urls u 
inner join hosts h on u.host = h.id 
inner join paths p on u.path = p.id 

sólo para localizar la fuente de la desaceleración

También a veces la reordenación de la consulta puede ayudar a

SELECT CONCAT(u.host, u.path) 
from urls u 
inner join hosts h on u.host = h.id 
inner join paths p on u.path = p.id 
0

no puedo decir con certeza acerca de MySQL, pero sé que en SQL Server claves primarias crea un índice automáticamente pero las claves externas no lo hacen. Asegúrese de verificar que haya un índice en sus campos de clave externa.

1

No soy un experto en MySQL, pero parece que las claves primarias de MySQL están agrupadas; querrá asegurarse de que ese sea el caso con sus claves principales; los índices agrupados definitivamente ayudarán a acelerar las cosas.

Una cosa, sin embargo, no creo que pueda tener dos claves "primarias" en ninguna tabla; su tabla de urls me parece bastante sospechosa por ese motivo.Sobre todo, debe asegurarse de que esas dos columnas en la tabla de URL estén indexadas a la empuñadura (un único índice numérico en cada una de ellas debería estar bien) porque se está uniendo a ellas, por lo que el DBMS necesita saber cómo hacerlo. encontrarlos rápidamente; eso podría ser lo que está pasando en tu caso. Si está escaneando toda la tabla tantas filas, entonces sí, podría estar sentado allí durante bastante tiempo mientras el servidor intenta encontrar todo lo que pidió.

También sugiero eliminar esa función CONCAT de la declaración de selección, y ver cómo eso afecta los resultados. Me sorprendería si eso no fuera un factor contribuyente de alguna manera. Simplemente recupere ambas columnas y maneje la concatenación luego, y vea cómo va eso.

Por último, ¿has descubierto dónde está el cuello de botella? Solo unirse a tres mesas de varios millones de filas no debería tomar mucho tiempo en absoluto (esperaría tal vez un segundo más o menos, solo echar un vistazo a sus tablas y consultas), siempre que las tablas estén correctamente indexadas. Pero si está presionando esas filas sobre una NIC lenta o ya pegada, a un servidor de aplicaciones con memoria insuficiente, etc., la lentitud podría no tener nada que ver con su consulta, sino con lo que sucede después de la consulta. Siete millones de filas son bastantes datos para ensamblar y desplazar, sin importar cuánto tiempo lleve el hallazgo de esas filas. Intente seleccionar solo una fila, en lugar de los siete millones, y vea cómo se ve por contraste. Si eso es rápido, entonces el problema no es la consulta, es el conjunto de resultados.

+0

MySQL solo permite 1 clave principal por tabla, pero esa clave puede estar formada por varias columnas de la tabla. Entonces, en el ejemplo de Nicolás, la tabla 'urls' tiene una sola clave primaria compuesta por' host' + 'path'. – Manzabar

+0

Claro, eso tiene sentido. Me olvidé de preguntar si las claves son en realidad una compuesta (que no creo que necesariamente quede clara). Principalmente, sin embargo, solo quería señalar la importancia de que esas dos columnas se indexen explícitamente de alguna manera. –

2

En general, el mejor consejo es rastrear y perfilar para ver lo que realmente está tomando tiempo. Pero aquí están mis pensamientos sobre cosas específicas a mirar.

(1) Diría que desea asegurarse de que los índices NO se utilicen en la ejecución de esta consulta. Como no tiene condiciones de filtrado, debería ser más eficiente escanear todas las tablas y luego unirlas con una operación de ordenación o combinación.

(2) La concatenación de cadenas seguramente lleva algo de tiempo, pero no entiendo por qué las personas recomiendan eliminarla. Es de suponer que necesitaría hacer la concatenación en otro fragmento de código, donde aún tomaría la misma cantidad de tiempo (a menos que la concatenación de cadenas de MySQL sea particularmente lenta por algún motivo).

(3) La transferencia de datos del servidor al cliente probablemente tome un tiempo considerable, posiblemente más que el tiempo que el servidor necesita para recuperar los datos. Si tiene herramientas para rastrear este tipo de cosas, úselos. Si puede aumentar el tamaño de la matriz de búsqueda en su cliente, experimente con diferentes tamaños (por ejemplo, en JDBC use Statement.setFetchSize()). Esto puede ser significativo incluso si el cliente y el servidor están en el mismo host.

1

Como su conjunto de resultados devuelve todos los datos, hay muy poca optimización que se pueda realizar. Estás escaneando toda la tabla, luego uniéndote a otras tablas que tienen índices.

¿Están las PrimaryKeys agrupadas? Esto garantiza que los datos se almacenen en el disco en el orden de índice, por lo que se evita el rebote en diferentes partes del disco.

Además, puede hacer que los datos se distribuyan en varios discos. Si tiene URL en PRIMARY y PATHS/HOSTS en SECONDARY, obtendrá un mejor rendimiento de las unidades.

1

Debe consultar la configuración de su servidor. Los parámetros de memoria predeterminados para MySQL lisiarán el rendimiento en una tabla de ese tamaño. Si está utilizando los valores predeterminados, debe aumentar al menos key_buffer_size y join_buffer_size por al menos un factor de 4, tal vez mucho más. Mire en la documentación; hay otros parámetros de memoria que puede ajustar.

MySQL tiene una curiosa peculiaridad del rendimiento en la que si las tablas superan un cierto tamaño con consultas que devolverán la mayoría de los datos, el rendimiento irá al baño. Lamentablemente, no tiene manera de decirle cuándo se alcanza ese umbral. Sin embargo, me parece que lo tienes.

0

Como no soy un gran admirador de MySQL, le preguntaría si ha probado PostgreSQL. En esa base de datos, querrá asegurarse de que su configuración de work_mem sea bastante alta, pero puede establecerla por conexión de base de datos con SET work_mem = 64MB, por ejemplo.

Otra sugerencia es analizar el uso de entradas de ruta duplicadas. Hay son muchas URL que comparten rutas.

Otra cosa que podría o no ayudar es usar campos de texto de longitud fija en lugar de varchar. Solía ​​hacer una diferencia de velocidad, pero no estoy seguro acerca de los motores DB actuales.

Si utiliza PostgreSQL, le permitirá usar JOIN USING, pero incluso en MySQL me gusta más: nombre su campo de Id. El mismo en cada tabla. En lugar de id en hosts y host en urls, asígnele el nombre host_id en ambos lugares.

Ahora algunos más comentarios. :) Este diseño de datos que tiene aquí es muy útil cuando selecciona un pequeño conjunto de filas, quizás todas las URL del mismo dominio. También puede ayudar a lote si sus consultas a menudo necesitan realizar escaneos secuenciales de la tabla urls para otros datos almacenados allí, porque el escaneo puede omitir los campos de texto grandes (a menos que no importe porque su DB almacena texto a través de punteros a una tabla vinculada de todos modos).

Sin embargo, si casi siempre selecciona todos los datos de dominio y ruta, entonces tiene más sentido almacenarlos en una tabla.