数据库排序与程序化java排序

时间:2009-12-10 19:11:28

标签: java sorting jpa

我想通过JPA从数据库(MySQL)获取数据,我希望它按一些列值排序。

那么,最佳做法是什么:

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

OR

  • 让数据库使用排序选择查询对其进行排序。

提前致谢

9 个答案:

答案 0 :(得分:52)

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

如果您的数据集非常小,如果您希望实现复杂的排序,则可以更方便地对代码进行排序。通常,这种复杂的排序可以在SQL中完成,但不像在代码中那么容易。

缺点是,经验法则是通过SQL排序,并且有一些边缘情况属于规则。

答案 1 :(得分:31)

一般情况下,最好在SQL查询中使用ORDER BY - 这样,如果有适用的索引,您可能会“免费”进行排序(最坏的情况是,它会是与在代码中执行相同的工作量,但通常可能没有那么多工作!)。

答案 2 :(得分:18)

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

将排序推迟到客户端(例如jQuery,Dataset / Dataview排序)可能看起来很诱人。它实际上是一个可行的选项,用于分页,排序和过滤,如果(并且仅限于):

1。数据集很小,

1。对性能和可伸缩性几乎没有关注

根据我的经验,符合这种标准的系统很少见。请注意,在应用程序/数据库中混合和匹配排序/分页是不可能的 - 如果您向数据库询问未排序的100行数据,然后在应用程序端对这些行进行排序,您可能不会得到该集合你期待的数据。这看起来似乎很明显,但我已经看到错误已经足够多次,我想至少提一下。

由于多种原因,在数据库中进行排序和过滤效率要高得多。首先,数据库引擎经过高度优化,可以完成排序和过滤所需的工作;这就是他们的底层代码的设计目的。但即便如此,即使假设您可以编写可以匹配成熟数据库引擎的排序,过滤和分页性能的代码,仍然可以在数据库中执行此工作,原因很简单,限制更有效从数据库传输到应用程序服务器的数据量。

例如,如果在过滤之前有10,000行,并且您的查询将该数字减少到75,那么在客户端上进行过滤会导致所有10,000行的数据通过网络传输(并进入您的应用程序)服务器的内存),在数据库端进行过滤将导致仅在数据库和应用程序之间移动过滤的75行。他可以对性能和可扩展性产生巨大影响。

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

答案 3 :(得分:14)

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

与其他一些海报一样,我的想法是数据库层可以更快地进行排序,因为它们应该针对这类事情进行调整。 @Alex提出了一个很好的观点,即如果数据库已经在排序上有索引,那么它会更快。我想回答一下在非索引排序上哪个原始排序更快的问题。注意,我说的更快,不简单。我认为在许多情况下让数据库完成工作更简单,更不容易出错。

我的主要假设是排序适合主存。并非所有问题都适合这里,但很多问题都适用。对于内存不足,很可能数据库在这里闪耀,尽管我没有测试过。在内存中的情况下,在我的非正式基准测试中,所有java / c / c ++的性能都超过了mysql,如果有人可以称之为。

我希望我有更多时间来更彻底地比较数据库层和应用层,但还有其他职责。尽管如此,我还是忍不住为其他在这条路上行走的人记录这张纸条。

当我开始走这条路时,我开始看到更多的障碍。我应该比较数据传输吗?怎么样?我可以比较读取db与时间的时间来读取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 Direct CLI结果:

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

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

从mysql提示符为10000000个元素,对于排序bigint_value或float_value

大致为2.1到2.4

Java 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 Sort在飞行中生成随机数(实际上比磁盘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 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

使用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 :: 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 :: 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

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

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;
}

答案 4 :(得分:4)

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

答案 5 :(得分:3)

我会让数据库进行排序,他们通常非常擅长。

答案 6 :(得分:1)

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

答案 7 :(得分:1)

嗯,实际上没有一种简单的方法来回答这个问题;必须在上下文中回答。

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

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

但如果答案是否定的,那么您会遇到将结果集从DB转移到中间层所涉及的延迟。然后,如果你正在进行分页,这是你应该做的最后一件事,那么你在裁剪页面后会丢掉90-95%的结果集。

所以浪费的带宽是不合理的。想象一下,在您的租户组织中为每个请求执行此操作。

不管你怎么看,这都是不好的设计。

无论怎样,我都会在数据库中这样做。仅仅因为今天几乎所有的应用程序都需要分页;即使他们没有通过电汇向您的客户发送大量的结果集也是一种浪费;拖拽所有租户的所有人。

我最近玩弄的一个有趣的想法是利用HTML5的强大功能,在Angular等浏览器框架中进行双向数据绑定,并将一些处理推送回浏览器。那样的话,在你完成之前,你最终不会在别人的排队等候。真正的分布式处理但必须注意决定什么可以推,什么不可。

答案 8 :(得分:0)

取决于上下文。

TL; DR

如果应用程序服务器中有完整的数据,请在应用程序服务器中进行。

如果已经在应用程序服务器端拥有了所需的完整数据集,那么最好在应用程序服务器端进行处理,因为这些服务器可以水平扩展。最可能的情况是:

  • 您从数据库中检索的数据集很小
  • 您在启动时将数据缓存在应用程序服务器端
  • 您正在执行事件源,并且无论如何都在应用程序服务器端建立数据。

除非可以保证不会影响客户端设备,否则请不要在客户端执行此操作。

数据库本身可能已经过优化,但是如果您可以减轻数据库负担,则可以降低总体成本,因为扩展数据库比扩展应用程序服务器要昂贵。