2009-12-10 57 views
36

我想通过JPA从数据库(MySQL)中获取数据,我希望它按某个列值进行排序。数据库排序与编程java排序

那么,什么是最好的做法,以:

  • 从数据库(JPA),然后 排序以编程方式使用一些的Java API对象列表检索数据。

OR

  • 使用排序选择查询让数据库排序。

在此先感谢

+2

有时,它不是一个简单的技术选择,在我的情况下,该数据库是非常繁忙的,是最有可能成为瓶颈,所以我HAVA在内存中进行排序。 – shellbye 2015-08-19 06:35:08

回答

46

如果您正在检索所有数据库数据的子集,例如在1000个屏幕上显示20行,最好在数据库上进行排序。这会更快,更容易,并且允许您一次检索一页行(20,50,100),而不是所有行。

如果你的数据集相当小,如果你想实现一个复杂的排序,你的代码排序可能会更方便。通常这种复杂的排序可以在SQL中完成,但不像在代码中那么容易。

它的缺点是,经验法则是通过SQL进行排序,其中一些边缘情况符合规则。

+1

+1为细微差别。 asdf – 2009-12-10 19:31:13

+3

我不同意“经验法则”。在数据层中排序的代价很高,典型的用例是可能需要多个排序顺序来执行相同的结果集,在应用程序层进行排序,即更改表示层中数据的显示顺序,使其更有意义从便利性和可扩展性的角度来看。 – 2013-03-15 19:37:59

+1

如果我针对API /服务获取我的数据会怎么样?尤其是它涉及多个API调用。 – Matt 2016-05-21 00:03:34

1

让数据库进行排序。它的目的是为你做“肮脏”的工作...

4

我几乎肯定会让数据库对它进行排序会更快。有许多工程师花费大量时间来完善和优化他们的搜索算法,而你必须实现自己的排序算法,这可能会增加一些计算量。

3

我会让数据库做这样的事情,他们通常很擅长。

28

一般来说,你最好在你的SQL查询中使用ORDER BY - 这样,如果有适用的索引,你可能会得到你的排序“免费”(最坏的情况,它将是相同的数量在你的代码中这样做的工作,但通常可能比这更少工作!)。

+0

典型的使用情况是在一个单一的结果集将有多个排序顺序要求,使索引的好处是有限的,除非你;再愿意支付每个查询与索引...这是不是大多数系统的现实各样需求。在应用程序层排序,即能够改变一个给定ResultSet的显示顺序任何方式用户决定他们想要的分类未做另一个请求到数据层,使得从方便和可扩展性的角度来看更有意义。 – 2013-04-23 16:53:35

+0

@ opc.three首先,我不同意这是一个典型的用例,它取决于您的应用程序。其次,如果您要检索大数据的子集,该怎么办?你会从数据源中获取数百万条记录并在内存中处理它们吗?而在去年,我就不会太害怕将请求发送到数据层的,因为你必须经常做也无妨(例如,如果用户输入不同的搜索条件),也因为你总是可以使用缓存或特殊的系统(例如SOLR)是如果你想加快搜索/检索和单独数据库是不够的。 – 2014-12-03 07:06:39

+0

>>其次,如果您要检索大数据的子集,该怎么办?你会从数据源中获取数百万条记录并在内存中处理它们吗? “这不是有问题的用例。在你的例子中,你需要ORDER BY来获得正确的结果。在原始评论的例子中,ORDER BY仅仅是为了演示层......苹果和桔子的好处。你可以拒绝接受它,但这是一个典型的用例。在应用程序层中进行缓存和排序时,您将在扩展时留下更多选项。 – 2014-12-03 15:58:24

16

这并不完全正确,但我最近发布了一些与数据库和应用程序端排序相关的内容。这篇文章是关于一个.net技术,所以它大部分可能不会让你感兴趣,但基本原则仍然存在:

推迟排序到客户端(例如jQuery,数据集/ Dataview排序)可能看起来像诱人。它实际上是为寻呼一个可行的选择,排序和过滤,如果(且仅当):

1.组数据是小,

1.有关于很少关注性能和可扩展性

从我的经验来看,符合这种标准的系统很少。请注意,在应用程序/数据库中混合和匹配排序/分页是不可能的 - 如果您向数据库询问未排序的100行数据,然后在应用程序端对这些行进行排序,那么您可能无法获取该集合您期望的数据。这看起来很明显,但我已经看到了足够多的错误,至少我想提及它。

由于多种原因,对数据库进行排序和筛选会更有效率。首先,数据库引擎为进行排序和过滤所需的工作而进行了高度优化;这是他们底层代码的设计目的。但即使假设你可以编写能够匹配成熟数据库引擎的排序,过滤和分页性能的代码,仍然最好在数据库中完成这项工作,原因很简单,因为限制它的效率更高从数据库传输到应用程序服务器的数据量。例如,如果在过滤之前您有10,000行,并且您的查询将该数字降低到75,则客户端上的过滤将导致所有10,000行中的数据通过网络传递(并传入您的应用程序服务器的内存),在数据库端进行过滤只会导致在数据库和应用程序之间移动过滤的75行。他可以对性能和可伸缩性产生巨大影响。

完整的职位是在这里: http://psandler.wordpress.com/2009/11/20/dynamic-search-objects-part-5sorting/

1

让数据库排序。然后,您可以轻松地使用JPA进行分页,而无需在整个结果集中进行读取。

11

我遇到了同样的问题,并决定运行一个基准来量化速度差异。结果令我感到惊讶。我想用这个问题发表我的经验。

和其他许多海报一样,我的想法是数据库层会更快地完成排序,因为它们被认为是针对这种事情进行了调整的。 @Alex提出了一个很好的观点,如果数据库已经有了索引,那么速度会更快。我想回答在非索引排序时原始排序更快的问题。请注意,我说得更快,而不是更简单。我认为在很多情况下让db做这项工作更简单,也更不容易出错。

我的主要假设是这种排序适合主存。并不是所有的问题都适合这里,但是很多人都可以。对于内存不足的情况,数据库很可能在这里发光,尽管我没有测试它。在内存排序的情况下,所有java/c/C++在我的非正式基准测试中表现都优于mysql,如果有人可以这么称呼它的话。

我希望我有更多的时间来更全面地比较数据库层和应用层,但是还有其他的职责要求。尽管如此,我还是忍不住为这条路上的其他人记录了这张笔记。

当我开始走这条道路时,我开始看到更多的障碍。我应该比较数据传输吗?怎么样?我可以比较读取数据库与读取java平面文件的时间吗?如何隔离排序时间与数据传输时间以及时间来读取记录?有了这些问题,我想到了方法和时间。

以毫秒所有的时间,除非另有发布

所有排序例程是由语言提供的默认值(这些都是随机排序的数据不够好)

所有编译是一个典型的“释放曲线”通过NetBeans的选择没有定制除非另有发布

所有测试的MySQL使用下面的架构

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

首先这里是填充数据库的一小段代码。可能有更简单的方法,但是这是我做过什么:

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直接CLI结果:

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

这些时间是有些困难,因为我拥有的唯一信息是执行后报告的时间的命令。

从MySQL提示千万元件大致是2.1至2.4或者用于分拣bigint_value或float_value

爪哇JDBC MySQL的呼叫(类似的性能做从MySQL CLI排序)

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) { 

    } 

} 

五个运行:

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

Java排序正在生成随机数(实际上比磁盘IO读取速度慢)。分配时间是生成随机数,并填充阵列

调用等

JavaSort(10,10000000); 

计时结果的时间:

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 

这些结果是用于读取双打的文件以二进制模式

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 

执行缓冲区传输会导致运行时间更快

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和C++定时结果使用的qsort

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 

释放曲线使用std使用的qsort

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 

释放曲线(参见下面的源)

调试资料:: 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 

释放曲线从读取文件的随机数据,并使用std ::排序(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 

下面是使用的源代码。希望最小的错误:)

Java源 注意内部JavaSort的runCode和writeFlag需要根据你想什么时间进行调整。还要注意的是,内存分配发生在for循环(因此测试GC,但我没有看到任何明显的差别移动外循环分配)

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

#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

好,并没有真正直接的方法来回答这个问题。它必须在上下文中回答。

您的应用程序(中间层)是否与数据库在同一节点上运行?

如果是的话,你不必担心数据库和中间层之间的延迟。然后问题就变成:查询的子集/结果集有多大?请记住,要排序这是中间层,您将采用一个大小为N的列表/集合,并且可以编写自定义比较器或使用默认的集合比较器。管他呢。因此,从一开始,就会受到N的影响。

但是,如果答案是否定的,那么您会将结果集从数据库传输到中间层。然后,如果您正在执行分页,这是您应该做的最后一件事情,那么在切分页面后,您会将该结果集的90-95%扔掉。

所以浪费的带宽是没有道理的。想象一下,在您的租户组织中为每个请求都这样做。你看它

但是办法,这是不好的设计。

我会做这个数据库中,不管是什么。仅仅因为今天几乎所有的应用都需要分页;即使他们不通过电报向客户发送大量结果集也是一种浪费;拖垮了所有的租户。那我这几天玩弄

一个有趣的想法是利用HTML5,2路数据,像角浏览器框架结合的力量,推动一些处理返回给浏览器。这样,在你完成之前,你不会在排队等待别人。真正的分布处理。但是在决定什么可以推动什么,什么不可以时,必须小心。