数据库sorting与编程的Javasorting
我想通过JPA从数据库(MySQL)中获取数据,我希望它通过某个列值进行sorting。
那么,最佳做法是:
- 从数据库中检索数据作为对象列表(JPA),然后使用一些Java API以编程方式进行sorting。
要么
- 让数据库使用sortingselect查询对其进行sorting。
提前致谢
如果您正在检索所有数据库数据的子集,例如在1000个屏幕上显示20行,最好对数据库进行sorting。 这将会更快,更容易,并允许您一次检索一页行(20,50,100),而不是所有行。
如果你的数据集相当小,如果你想实现一个复杂的sorting,你的代码sorting可能会更方便。 通常这种复杂的sorting可以在SQL
完成,但不像在代码中那么容易。
简而言之,经验法则是通过SQL
sorting,以及一些边缘情况下的规则。
一般来说,你最好在你的SQL查询中使用ORDER BY
– 这样,如果有一个适用的索引,你可能会得到你的sorting“免费”(最坏的情况,这将是相同的工作量在你的代码中执行它,但是往往可能比这更less工作!)。
这不完全是关键,但是我最近发布了一些与数据库和应用程序端sorting相关的内容。 这篇文章是关于一个.net技术的,所以大部分对你来说可能不会有意思,但是基本原则依然存在:
推迟sorting到客户端(如jQuery,数据集/数据视图sorting)可能看起来很诱人。 如果(且仅当):它实际上是分页,sorting和过滤的可行选项:
这组数据很小,而且
1.对性能和可伸缩性没有多less关注
从我的经验来看,符合这种标准的系统是less之又less的。 请注意,在应用程序/数据库中混合和匹配sorting/分页是不可能的 – 如果向数据库询问未sorting的100行数据,然后在应用程序端对这些行进行sorting,则很可能无法获得集合您期望的数据。 这看起来很明显,但是我已经看到了足够多的错误,至less我想提一提。
出于多种原因,对数据库进行sorting和过滤要高效得多。 首先,数据库引擎对于完成sorting和过滤所需的工作进行了高度优化; 这是他们底层代码的devise目的。 但是即使假设你可以编写能够匹配成熟数据库引擎的sorting,过滤和分页性能的代码,仍然可以在数据库中完成这项工作,因为简单的原因是限制从数据库传输到应用程序服务器的数据量。
因此,例如,如果在过滤之前有10,000行,并且查询将该数字降低到75,则在客户端上进行过滤将导致所有10,000行的数据通过networking传递(并传递到应用服务器的内存中)在数据库端将导致只有过滤的75行在数据库和应用程序之间移动。 他可以对性能和可伸缩性产生巨大的影响。
完整的文章在这里: http : //psandler.wordpress.com/2009/11/20/dynamic-search-objects-part-5sorting/
我遇到了同样的问题,并决定运行一个基准来量化速度差异。 结果让我吃惊。 我想在这个问题上发表我的经验。
和其他一些海报一样,我的想法是数据库层能够更快地完成这个任务,因为他们被认为是对这种事情进行了调整。 @Alex提出了一个很好的观点,如果数据库已经有了索引,那么速度会更快。 我想回答在非索引sorting时原始sorting更快的问题。 请注意,我说的更快,而不是更简单。 我想在很多情况下,让db做这个工作更简单,更不容易出错。
我的主要假设是,这种sorting适合主要记忆。 不是所有的问题都适合在这里,但是很多人都可以。 对于内存不足的情况,数据库很可能在这里发光,尽pipe我没有testing。 在内存sorting的情况下,所有的java / c / c ++在我非正式的基准testing中performance都优于mysql,如果有人可以这么称呼它的话。
我希望我有更多的时间来更深入地比较数据库层和应用层,但是还有其他一些职责。 尽pipe如此,我还是忍不住把这张照片logging给正在这条路上行走的其他人。
当我开始走这条路时,我开始看到更多的障碍。 我应该比较数据传输吗? 怎么样? 我可以比较时间来读取数据库与时间来读取在Java平面文件? 如何隔离sorting时间vs数据传输时间与时间来读取logging? 有了这些问题,我想到了方法和时间的数字。
所有时间以毫秒为单位,除非另有说明
所有sorting例程都是语言提供的默认值(这对于随机sorting的数据来说足够好)
所有的编译都是通过netbeansselect的一个典型的“发布configuration文件”,没有定制,除非另有说明
所有的mysqltesting都使用下面的模式
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个元素大致2.1到2.4要么sortingbigint_value或float_value
Java的JDBC的MySQL调用(类似的性能做从MySQL cli进行sorting)
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
Javasorting生成随机数字(实际上比磁盘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 ++时序结果(见下面的来源)
使用qsortdebuggingconfiguration文件
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释放configuration文件
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
释放configuration文件使用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
发布configuration文件从文件中读取随机数据并使用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循环(因此testingGC,但我没有看到任何明显的差异移动循环外的分配)
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; }
我几乎肯定,允许数据库sorting它会更快。 有一些工程师花费大量的时间来完善和优化他们的searchalgorithm,而你将不得不实现自己的sortingalgorithm,这可能会增加一些计算量。
我会让数据库这样做,他们通常是非常好的。
让数据库进行sorting。 它的目的是为你做“脏”的工作
让数据库对其进行sorting。 然后,您可以轻松地使用JPA进行分页,而无需在整个结果集中进行读取。
那么,没有一个简单的方法可以回答这个问题。 必须在上下文中回答。
您的应用程序(中间层)是否与数据库在同一节点上运行?
如果是的话,你不必担心数据库和中间层之间的延迟。 那么问题就变成:查询的子集/结果集有多大? 请记住,要sorting这是中间层,您将采取大小为N的列表/集合,并且可以编写自定义比较器或使用默认的收集比较器。 pipe他呢。 所以,一开始,你的规模是N.
但是,如果答案是否定的,那么您将遇到将结果集从数据库传输到中间层的延迟。 然后,如果您正在执行分页,这是您应该做的最后一件事情,那么在切割页面后,您将会丢弃结果集的90-95%。
所以浪费的带宽是不合理的。 想象一下,在您的租户组织中为每一个请求做这件事。
不过你看,这是不好的devise。
我会在数据库中做到这一点,不pipe是什么。 仅仅因为今天几乎所有的应用程序都需要分页; 即使他们没有通过电报向客户发送大量的结果集,也是一种浪费; 把所有的房客拖下来。
一个有趣的想法是,我现在正在玩弄HTML5的强大function,在Angular等浏览器框架中使用双向数据绑定,并将一些处理推回给浏览器。 那样的话,在你完成之前,你不会排队等待别人。 真正的分布式处理 但是,在决定什么可以推,什么不可以的时候要小心。