2009-12-10 12 views
36

Quiero obtener datos de la base de datos (MySQL) por JPA, lo quiero ordenado por algún valor de columna.tipo de base de datos frente a programmatic java sort

Así que, ¿cuál es la mejor práctica, a:

  • Recuperar los datos de la base de datos como lista de objetos (APP), entonces especie mediante programación utilizando algunas API de Java.

O

  • Vamos a la base de datos especie mediante el uso de una consulta de selección de clasificación.

Gracias de antemano

+2

A veces no es una opción de tecnología simple, en mi caso, la base de datos está muy ocupada y es muy probable que se convierta en el cuello de botella, por lo que DEBO ordenar en memoria. – shellbye

Respuesta

46

Si está recuperando un subconjunto de todos los datos de la base de datos, por ejemplo, mostrando 20 filas en la pantalla de 1000, es mejor ordenar en la base de datos. Esto será más rápido y sencillo, y le permitirá recuperar una página de filas (20, 50, 100) a la vez en lugar de todas.

Si su conjunto de datos es bastante pequeño, ordenar su código puede ser más conveniente si desea implementar un tipo complejo. Por lo general, este tipo complejo se puede hacer en SQL, pero no tan fácilmente como en el código.

El cortocircuito es, la regla de oro es ordenar a través de SQL, con algunos casos de borde a la regla.

+1

+1 por matiz. asdf –

+3

Tengo que estar en desacuerdo con la "regla de oro". La clasificación en el nivel de datos es costosa; el típico caso de uso es que se requieren varios órdenes de clasificación para el mismo conjunto de resultados, ordenando en el nivel de aplicación, es decir, para cambiar el orden de visualización de la capa de presentación, tiene mucho más sentido desde un punto de vista de conveniencia y escalabilidad. –

+1

¿Qué sucede si voy contra API/servicios para obtener mis datos? y especialmente involucra más de una llamada API. – Matt

1

Deje que la base de datos hacer la clasificación. Está diseñado para hacer el trabajo "sucio" para usted ...

4

Estoy casi seguro de que será más rápido permitir que la base de datos lo ordene. Hay ingenieros que pasan mucho tiempo perfeccionando y optimizando sus algoritmos de búsqueda, mientras que usted tendrá que implementar su propio algoritmo de clasificación que podría agregar algunos cálculos más.

3

Dejo que la base de datos haga el tipo, en general son muy buenos.

28

En general, es mejor usar ORDER BY en su consulta SQL - de esta manera, si hay un índice aplicable, es posible que obtenga su clasificación "gratis" (en el peor de los casos, será la misma cantidad) de trabajo como hacerlo en tu código, pero a menudo puede ser menos trabajo que eso!).

+0

El caso de uso típico es cuando un solo conjunto de resultados tendrá múltiples requisitos de orden, por lo que el beneficio de un índice es limitado a menos que esté dispuesto a cubrir cada requisito de clasificación de cada consulta con un índice ... que no es realista para la mayoría de los sistemas . Clasificar en el nivel de la aplicación, es decir, poder cambiar el orden de visualización de un determinado conjunto de resultados de cualquier forma que el usuario decida que quiere ordenarlo sin hacer otra solicitud al nivel de datos, tiene mucho más sentido desde el punto de vista de conveniencia y escalabilidad. –

+0

@ opc.three En primer lugar, no estoy de acuerdo con que sea un caso de uso típico, depende de su aplicación. En segundo lugar, ¿qué sucede si va a recuperar un subconjunto de Big Data? ¿Buscaría todos esos millones de registros de un origen de datos y los trataría en la memoria? Y por último, no tendría demasiado miedo de enviar solicitudes a un nivel de datos porque a menudo tiene que hacerlo de todos modos (por ejemplo, si un usuario ingresa diferentes criterios de búsqueda) y porque siempre puede usar el almacenamiento en caché o sistemas especiales (por ejemplo, solr) si desea acelerar la búsqueda/recuperación y solo DB no es suficiente. –

+0

>> En segundo lugar, ¿qué sucede si va a recuperar un subconjunto de Big Data? ¿Buscaría todos esos millones de registros de un origen de datos y los trataría en la memoria? << Ese no es el caso de uso en cuestión. En su ejemplo, necesita el ORDER BY para obtener el resultado correcto. En el ejemplo del comentario original, ORDER BY es simplemente para el beneficio de la capa de presentación ... manzanas y naranjas. Puede negarse a aceptarlo, pero es un caso de uso típico. Al almacenar en caché y ordenar en un nivel de aplicación, se dejará más opciones al escalar. –

16

Esto no es completamente correcto, pero recientemente publiqué algo relacionado con la clasificación de la base de datos y la del lado de la aplicación. El artículo trata acerca de una técnica de .NET, por lo que la mayoría probablemente no le interese, pero los principios básicos permanecen:

La clasificación diferida en el lado del cliente (p. Ej. JQuery, ordenación de conjuntos de datos/dataview) puede parecer tentador. Y que en realidad es una opción viable para paginación, clasificación y filtrado, si (y sólo si):

1. el conjunto de datos es pequeña, y

1. hay poca preocupación acerca rendimiento y escalabilidad

Según mi experiencia, los sistemas que cumplen con este tipo de criterios son pocos y distantes.Tenga en cuenta que no es posible mezclar y combinar la ordenación/paginación en la aplicación/base de datos: si le pide a la base de datos para 100 filas de datos sin clasificar, luego clasifique esas filas en el lado de la aplicación, es probable que no obtenga el conjunto de los datos que estabas esperando Esto puede parecer obvio, pero he visto el error cometido tantas veces que quería al menos mencionarlo.

Es mucho más eficiente ordenar y filtrar en la base de datos por varias razones. Por un lado, los motores de bases de datos están altamente optimizados para hacer exactamente el tipo de trabajo que implica la clasificación y el filtrado; esto es lo que su código subyacente fue diseñado para hacer. Pero incluso excluyendo eso, incluso suponiendo que se pueda escribir código que coincida con el tipo de clasificación, filtrado y paginación de un motor de base de datos maduro, es preferible hacer este trabajo en la base de datos, por la sencilla razón de que es más eficiente limitarlo. la cantidad de datos que se transfieren de la base de datos al servidor de aplicaciones.

Así, por ejemplo, si usted tiene 10.000 filas antes de la filtración y la consulta Parés ese número a 75, el filtrado de los resultados de los clientes en los datos de todas las 10.000 filas que se pasa a través del cable (y en su aplicación memoria del servidor), donde el filtrado en el lado de la base de datos daría lugar a que solo las 75 filas filtradas se muevan entre la base de datos y la aplicación. Esto puede tener un gran impacto en el rendimiento y la escalabilidad.

El post completo está aquí: http://psandler.wordpress.com/2009/11/20/dynamic-search-objects-part-5sorting/

1

Vamos a la base de datos ordenarla. Entonces puede tener paginación con JPA fácilmente sin readin en todo el conjunto de resultados.

11

Me encontré con esta misma pregunta, y decidí que debería ejecutar un pequeño punto de referencia para cuantificar las diferencias de velocidad. Los resultados me sorprendieron. Me gustaría publicar mi experiencia con este tipo de pregunta.

Al igual que con otros carteles aquí, pensé que la capa de la base de datos haría el tipo más rápido porque supuestamente están sintonizados para este tipo de cosas. @Alex destacó que si la base de datos ya tiene un índice en el género, será más rápido. Quería responder a la pregunta de cuál clasificación cruda es más rápida en ordenaciones no indexadas. Nota, dije más rápido, no más simple. Creo que en muchos casos dejar que el DB haga el trabajo es más simple y menos propenso a errores.

Mi suposición principal fue que el género cabría en la memoria principal. No todos los problemas encajarán aquí, pero un buen número sí. Para los tipos de memoria insuficiente, puede ser que las bases de datos brillen aquí, aunque no lo compruebo. En el caso de en los tipos de memoria, todo java/c/C++ superó a mysql en mi punto de referencia informal, si se pudiera llamar así.

Ojalá hubiera tenido más tiempo para comparar más a fondo la capa de la base de datos frente a la capa de la aplicación, pero lamentablemente otros deberes llamados. Aún así, no pude evitar grabar esta nota para otros que están viajando por este camino.

Al comenzar este camino empecé a ver más obstáculos. ¿Debo comparar la transferencia de datos? ¿Cómo? ¿Puedo comparar el tiempo para leer db vs time para leer un archivo plano en Java? ¿Cómo aislar el tiempo de ordenamiento frente al tiempo de transferencia de datos frente al tiempo para leer los registros? Con estas preguntas aquí estaba la metodología y los números de tiempo que se me ocurrieron.

franja horaria en ms menos que se indique lo contrario

Todas las rutinas de ordenación son los valores por defecto proporcionados por el lenguaje (estos son lo suficientemente bueno para datos ordenados al azar)

Todo compilación fue con un típico "liberación perfil" seleccionada a través de NetBeans sin personalización menos que se indique lo contrario

Todas las pruebas para MySQL utiliza el siguiente esquema

mysql> CREATE TABLE test_1000000 
(
pk bigint(11) NOT NULL, 
float_value DOUBLE NULL, 
bigint_value  bigint(11) NULL, 
PRIMARY KEY (pk) 
) Engine MyISAM; 

mysql> describe test_1000000; 
+--------------+------------+------+-----+---------+-------+ 
| Field  | Type  | Null | Key | Default | Extra | 
+--------------+------------+------+-----+---------+-------+ 
| pk   | bigint(11) | NO | PRI | NULL |  | 
| float_value | double  | YES |  | NULL |  | 
| bigint_value | bigint(11) | YES |  | NULL |  | 
+--------------+------------+------+-----+---------+-------+ 

Primero aquí hay un pequeño fragmento para llenar el DB. Puede haber formas más fáciles, pero esto es lo que hice:

resultados
public static void BuildTable(Connection conn, String tableName, long iterations) { 
    Random ran = new Random(); 
    Math.random(); 
    try { 


     long epoch = System.currentTimeMillis(); 
     for (long i = 0; i < iterations; i++) { 
      if (i % 100000 == 0) { 
       System.out.println(i + " next 100k"); 
      } 
      PerformQuery(conn, tableName, i, ran.nextDouble(), ran.nextLong()); 
     } 

    } catch (Exception e) { 
     logger.error("Caught General Exception Error from main " + e); 

    } 
} 

MYSQL directa de la CLI:

select * from test_10000000 order by bigint_value limit 10; 
10 rows in set (2.32 sec) 

Estos tiempos eran un poco difícil, ya que la única información que tenía era el tiempo reportado después de la ejecución del comando.

desde el indicador de MySQL para 10000000 elementos que es aproximadamente 2.1 a 2.4, ya sea para la clasificación bigint_value o float_value

Java JDBC llamada mysql (rendimiento similar a hacer una especie de cli MySQL)

public static void SortDatabaseViaMysql(Connection conn, String tableName) { 

    try { 
     Statement stmt = conn.createStatement(); 
     String cmd = "SELECT * FROM " + tableName + " order by float_value limit 100"; 


     ResultSet rs = stmt.executeQuery(cmd); 
    } catch (Exception e) { 

    } 

} 

cinco carreras:

da=2379 ms 
da=2361 ms 
da=2443 ms 
da=2453 ms 
da=2362 ms 

Java Sort La generación de números aleatorios en marcha (en realidad fue más lenta que la lectura de IO de disco).tiempo de asignación es el momento para generar números aleatorios y rellenar la matriz

Calling como

JavaSort(10,10000000); 

resultados de temporización:

assignment time 331 sort time 1139 
assignment time 324 sort time 1037 
assignment time 317 sort time 1028 
assignment time 319 sort time 1026 
assignment time 317 sort time 1018 
assignment time 325 sort time 1025 
assignment time 317 sort time 1024 
assignment time 318 sort time 1054 
assignment time 317 sort time 1024 
assignment time 317 sort time 1017 

Estos resultados fueron para la lectura de un archivo de dobles en modo binario

assignment time 4661 sort time 1056 
assignment time 4631 sort time 1024 
assignment time 4733 sort time 1004 
assignment time 4725 sort time 980 
assignment time 4635 sort time 980 
assignment time 4725 sort time 980 
assignment time 4667 sort time 978 
assignment time 4668 sort time 980 
assignment time 4757 sort time 982 
assignment time 4765 sort time 987 

Realizar una transferencia de búfer da como resultado tiempos de ejecución mucho más rápidos

assignment time 77 sort time 1192 
assignment time 59 sort time 1125 
assignment time 55 sort time 999 
assignment time 55 sort time 1000 
assignment time 56 sort time 999 
assignment time 54 sort time 1010 
assignment time 55 sort time 999 
assignment time 56 sort time 1000 
assignment time 55 sort time 1002 
assignment time 56 sort time 1002 

C y C++ resultados de temporización (véase más adelante para la fuente)

perfil de depuración utilizando qsort perfil

assignment 0 seconds 110 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds 

liberación utilizando qsort perfil

assignment 0 seconds 100 milliseconds Time taken 1 seconds 600 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 580 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds 
assignment 0 seconds 80 milliseconds Time taken 1 seconds 590 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 1 seconds 580 milliseconds 

de liberación utilizando std :: sort (a, a + ARRAY_SIZE);

assignment 0 seconds 100 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 870 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds 
assignment 0 seconds 120 milliseconds Time taken 0 seconds 890 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 900 milliseconds 
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds 
assignment 0 seconds 100 milliseconds Time taken 0 seconds 890 milliseconds 
assignment 0 seconds 150 milliseconds Time taken 0 seconds 870 milliseconds 

Perfil de liberación lectura datos aleatorios de archivo y utilizar std :: sort (a, a + ARRAY_SIZE)

assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 40 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds 
assignment 0 seconds 40 milliseconds Time taken 0 seconds 880 milliseconds 

A continuación se muestra el código fuente utilizado. Es de esperar errores mínimos :)

fuente de Java Tenga en cuenta que interna a JavaSort el runCode y writeFlag deben ajustarse en función de lo que desea hora. También tenga en cuenta que la asignación de memoria que ocurre en el bucle (GC así probar, pero no ve ninguna diferencia apreciable en movimiento la asignación fuera del bucle)

public static void JavaSort(int iterations, int numberElements) { 

    Random ran = new Random(); 
    Math.random(); 
    int runCode = 2; 
    boolean writeFlag = false; 
    for (int j = 0; j < iterations; j++) { 
     double[] a1 = new double[numberElements]; 
     long timea = System.currentTimeMillis(); 
     if (runCode == 0) { 
      for (int i = 0; i < numberElements; i++) { 
       a1[i] = ran.nextDouble(); 

      } 
     }    
     else if (runCode == 1) { 

      //do disk io!! 
      try { 
      DataInputStream in = new DataInputStream(new FileInputStream("MyBinaryFile.txt")); 
      int i = 0; 
      //while (in.available() > 0) { 
      while (i < numberElements) { //this should be changed so that I always read in the size of array elements 
       a1[i++] = in.readDouble(); 
      } 
      } 
      catch (Exception e) { 

      } 

     } 
     else if (runCode == 2) { 
      try { 
       FileInputStream stream = new FileInputStream("MyBinaryFile.txt"); 
       FileChannel inChannel = stream.getChannel(); 

       ByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); 
       //int[] result = new int[500000]; 

       buffer.order(ByteOrder.BIG_ENDIAN); 
       DoubleBuffer doubleBuffer = buffer.asDoubleBuffer(); 
       doubleBuffer.get(a1); 
      } 
      catch (Exception e) { 

      } 
     } 

     if (writeFlag) { 
      try { 
       DataOutputStream out = new DataOutputStream(new FileOutputStream("MyBinaryFile.txt")); 
       for (int i = 0; i < numberElements; i++) { 
        out.writeDouble(a1[i]); 
       } 
      } catch (Exception e) { 

      } 
     } 
     long timeb = System.currentTimeMillis(); 
     Arrays.sort(a1); 

     long timec = System.currentTimeMillis(); 
     System.out.println("assignment time " + (timeb - timea) + " " + " sort time " + (timec - timeb)); 
     //delete a1; 
    } 
} 

C/C++ fuente

#include <iostream> 
#include <vector> 
#include <algorithm> 
#include <fstream> 

#include <cstdlib> 
#include <ctime> 
#include <cstdio> 
#include <math.h> 
#include <stdio.h> 
#include <time.h> 
#include <stdlib.h> 

#define ARRAY_SIZE 10000000 

using namespace std; 

int compa(const void * elem1, const void * elem2) { 
    double f = *((double*) elem1); 
    double s = *((double*) elem2); 
    if (f > s) return 1; 
    if (f < s) return -1; 
    return 0; 
} 

int compb (const void *a, const void *b) { 
    if (*(double **)a < *(double **)b) return -1; 
    if (*(double **)a > *(double **)b) return 1; 
    return 0; 
} 

void timing_testa(int iterations) { 

    clock_t start = clock(), diffa, diffb; 

    int msec; 
    bool writeFlag = false; 
    int runCode = 1; 

    for (int loopCounter = 0; loopCounter < iterations; loopCounter++) { 
     double *a = (double *) malloc(sizeof (double)*ARRAY_SIZE); 
     start = clock(); 
     size_t bytes = sizeof (double)*ARRAY_SIZE; 
     if (runCode == 0) { 
      for (int i = 0; i < ARRAY_SIZE; i++) { 
       a[i] = rand()/(RAND_MAX + 1.0); 
      } 
     } 
     else if (runCode == 1) { 
      ifstream inlezen; 

      inlezen.open("test", ios::in | ios::binary); 


      inlezen.read(reinterpret_cast<char*> (&a[0]), bytes); 

     } 
     if (writeFlag) { 
      ofstream outf; 
      const char* pointer = reinterpret_cast<const char*>(&a[0]); 
      outf.open("test", ios::out | ios::binary); 
      outf.write(pointer, bytes); 
      outf.close(); 

     } 

     diffa = clock() - start; 
     msec = diffa * 1000/CLOCKS_PER_SEC; 
     printf("assignment %d seconds %d milliseconds\t", msec/1000, msec % 1000); 
     start = clock(); 
     //qsort(a, ARRAY_SIZE, sizeof (double), compa); 
     std::sort(a, a + ARRAY_SIZE); 
     //printf("%f %f %f\n",a[0],a[1000],a[ARRAY_SIZE-1]); 
     diffb = clock() - start; 

     msec = diffb * 1000/CLOCKS_PER_SEC; 
     printf("Time taken %d seconds %d milliseconds\n", msec/1000, msec % 1000); 
     free(a); 
    } 



} 

/* 
* 
*/ 
int main(int argc, char** argv) { 

    printf("hello world\n"); 
    double *a = (double *) malloc(sizeof (double)*ARRAY_SIZE); 


    //srand(1);//change seed to fix it 
    srand(time(NULL)); 

    timing_testa(5); 



    free(a); 
    return 0; 
} 
1

Bueno , no hay realmente una manera directa de responder esto; debe ser respondido en el contexto.

¿Su aplicación (nivel medio) se está ejecutando en el mismo nodo que la base de datos?

En caso afirmativo, no tiene que preocuparse por la latencia entre la base de datos y el nivel medio. Entonces surge la pregunta: ¿Qué tan grande es el subconjunto/conjunto de resultados de su consulta? Recuerde que para ordenar esto es de nivel medio, tomará una lista/conjunto de tamaño N y escribirá un comparador personalizado o usará el comparador de colecciones predeterminado. O lo que sea. Por lo tanto, al principio, tiene retroceso en el tamaño N.

Pero si la respuesta es no, entonces se ve afectado por la latencia involucrada en la transferencia de su conjunto de resultados de DB a nivel intermedio. Y luego, si está realizando la paginación, que es lo último que debe hacer, está tirando el 90-95% de ese conjunto de resultados después de cortar las páginas.

Por lo que el ancho de banda desperdiciado no se puede justificar. Imagine hacer esto para cada solicitud, en todas las organizaciones de inquilinos.

Como sea que lo mires, este es un mal diseño.

Lo haría en la base de datos, no importa qué. Simplemente porque casi todas las aplicaciones actuales exigen paginación; incluso si no envían resultados masivos por cable a su cliente es un desperdicio total; arrastra a todos hacia abajo a través de todos sus inquilinos.

Una idea interesante con la que estoy jugando estos días es aprovechar el poder de HTML5, el enlace de datos bidireccional en marcos de navegador como Angular, y devolver algún procesamiento al navegador. De esa manera, no terminas esperando en la fila para que alguien más te cierre antes de terminar. Procesamiento distribuido verdadero. Pero se debe tener cuidado al decidir qué se puede impulsar y qué no.

Cuestiones relacionadas