2009-12-10 41 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路數據,像角瀏覽器框架結合的力量,推動一些處理返回給瀏覽器。這樣,在你完成之前,你不會在排隊等待別人。真正的分佈處理。但是在決定什麼可以推動什麼,什麼不可以時,必須小心。