2017-01-30 20 views
16

它將如何有益的是使用Python/PHP非永久性陣列存儲6GB +數據與超過800個億行中RAM,而不是使用MySQL/MongoDB/Cassandra/BigTable/BigData(Persistence Database)數據庫處理簡單查詢時的速度/延遲?持久性數據庫(MySQL的/的MongoDB /卡桑德拉/ BigTable的/ BigData)與非持久性陣列(PHP/Python)的

例如,在800個多萬行找到一個名字在1秒鐘內:這可能嗎?有沒有人有處理超過1-2億行數據集的經驗,並在1秒鐘內獲得一個簡單的搜索查詢結果?

是否還有更好的,行之有效的方法來處理數十億行的?

+0

你真的只是想在單個很長的表中查找一行嗎?這可以通過具有適當索引的內存數組(例如,熊貓數據幀)或者在具有適當字段上的索引的數據庫表中快速進行。如果沒有索引,對內存數組的全面掃描可能比對磁盤上的表進行全面掃描要快,但主要是因爲您已將整個表讀入內存。如果使用內存數組,則需要在啓動時從磁盤讀取所有內容,最後將其寫回,並擔心在線程之間進行同步。有了數據庫你不會。 –

回答

4

它應該是非常大的不同,圍繞4-5個數量級快。數據庫以4KB塊(通常)存儲記錄,並且必須將每個這樣的塊存入內存中,這需要幾毫秒。將表格的大小除以4KB並獲取圖片。相比之下,內存數據的相應時間通常是納秒。毫無疑問,內存更快,真正的問題是如果你有足夠的內存,並且你可以在那裏保存數據多久。

然而,上述保持一段select * from table查詢。如果你想要一個select * from table where name=something,您可以在名稱創建一個索引,使數據庫不必掃描整個文件,結果應該是非常非常更好,可能是實際使用非常滿意。

+0

如果你曾經與10億行左右的交易達成協議並能在1秒內得到結果,你能分享你的個人實驗/經驗嗎?你的發現是什麼? –

+0

我曾嘗試過這種方式,但是當涉及到索引時,它也不會像預期的那樣提供性能,就像我在1秒內所說的那樣。 BigData和BigTable可以做到這一點,但它又是分配文件系統並擁有多個相同數據副本的技術,以便多個並行工作者/線程可以通過有效共享執行相同的工作。 在這裏,我期待有人用非持久性方法真正擊敗了BigData或&BigTable的性能。 –

4

4字節(int)的* 1_000_000_000〜4千兆 4字節(int)的* 1_000_000_000/64字節= 6250萬次(對於L1高速緩存) 4字節(int)的* 1_000_000_000/64字節= 6250萬次(對於L2高速緩存)

採取延遲,這都應該知道的主內存爲100 ns from here我們得到了100秒。 如果所有內部L1高速緩存(64字節爲intel線),它接近大約31.25毫秒。 但在此之前,還有L2/L3高速緩存(相同的行大小)將是218,75毫秒。 您可以看到,要連續讀取1 Mb(換句話說,這是最好的情況),因此對於4 Gb,它是4024 * 250μs= 1006000μs〜= 1 s。 SSD磁盤的延遲較少,但並非如此簡單。有研究表明,大多數可供所有人購買的SSD磁盤不能承載真正非常高的負載率(原因 - 它們失敗並且更有趣 - 它們擁有自己的垃圾收集器,它可以增加大的潛伏)。但是也有一些解決方案適用於像Aerospike這樣的SSD磁盤環境,當然,固態硬盤比硬盤更快。

只爲了解。在典型的筆記本電腦上(我的:intel core i5,x64,16Gb RAM),我需要接近580毫秒到875毫秒來計算10億個int元素的長整數。 我也可以看到從300 Mb/s到354.66 Mb/s的Clickhouse速度來計算我SSD上Int32列的總和。 (注意在這兩種情況下的總和沒有意義,因爲類型溢出)

當然,我們也有CUDA作爲變體或甚至簡單的線程(假設多線程將計算總和,我們可以輕鬆地acheave)。

那麼...我們能做什麼?

縮放有兩種類型:垂直和水平。大多數數據庫都喜歡水平縮放,我想原因很簡單。 水平縮放比垂直簡單。對於垂直擴展,您需要人員(或您應該擁有)在不同領域的非常好的專業知識。例如,從我的生活中,我應該瞭解很多關於Java/HotSpot/OS體系結構/許多許多技術/框架/數據庫,以便在創建高性能應用程序/算法時編寫或理解不同決策的優點。 而這只是開始,然後有更多的專家,然後我。

其他數據庫使用垂直縮放,更準確地說,它們對特定場景/查詢使用特殊優化。

所有的決定都是不同操作之間的妥協。 例如,對於Top N問題,Vertica和Druid具有特定的實現,它們完全解決了此任務。 在Cassandra中,爲了使所有選擇都快速,您應該爲一個表創建多個表或多個視圖,對於特定的查詢有不同的表示效率,當然,由於數據發佈,花費更多的存儲空間。

即使您可以在一秒鐘內讀取10億行的最大問題之一 - 您不可能同時在同一個表中編寫同一個表。 換句話說,主要問題 - 很難同時滿足所有用戶對所有用戶任務的請求。

是否有更好的,經過驗證的方法來處理數十億行?

一些例子:

  1. RAM的內存映射文件(持有開銷)組合:當您使用內存映射文件(或隨機訪問文件),可以存儲更多的數據,並與好的磁盤,可以獲得高讀/寫速率。
  2. 索引內存段:這個想法是通過索引拆分數據,索引將與段關聯,並在段內進行查詢,而不處理所有數據。
  3. 任務的特定存儲區:當你知道你的數據和需求時,你可以拿出存儲空間,這對於其他人來說是非常有效的,但對於其他人來說卻不是這樣(在你的情況下「找到一個名字」,你可以通過索引和分區數據字母,前綴等);
  4. 在C/C++中進行復雜的計算,有時這可能會更快。 :)這有點有趣,但真實的故事。通過口碑,C++在編程和支持方面更加複雜,但如果您擁有足夠的專業知識,在C++上編寫快速應用程序會更容易;
  5. 數據發佈(以多種方式爲不同的查詢存儲數據);代碼生成(即時生成代碼,將針對每個特定任務進行優化);
  6. 多線程,當然:如果你可以有效地共享cpu資源,在多個線程中執行一個任務;當然,高速緩存:基於磁盤/ RAM /網絡(我的意思是外部高速緩存服務器)的高速緩存結果;

在某些情況下使用自己的解決方案可能會更昂貴(和有效),然後自定義。在某些情況下,它不是...

比較字符串是相對複雜的,所以我想你需要從計算開始計算你需要多少時間來比較兩個字符串。 這個簡單的例子顯示了我們需要多少時間來比較Java中的兩個字符串。

import org.openjdk.jmh.annotations.*; 
import org.openjdk.jmh.runner.Runner; 
import org.openjdk.jmh.runner.options.OptionsBuilder; 

import java.util.concurrent.ThreadLocalRandom; 
import java.util.concurrent.TimeUnit; 

@State(Scope.Benchmark) 
@BenchmarkMode(Mode.SampleTime) 
@OutputTimeUnit(TimeUnit.MICROSECONDS) 
@Threads(1) 
public class StringEquals { 

    @Param({"0", "5", "10"}) 
    int prefix; 

    String theSamePart, theSamePartQuery; 

    @Setup(Level.Invocation) 
    public void setData() { 
     String value = String.valueOf(ThreadLocalRandom.current().nextInt()); 
     theSamePart = prefix > 0 ? value.substring(Math.min(prefix, value.length())) : value; 
     value = String.valueOf(ThreadLocalRandom.current().nextInt()); 
     theSamePartQuery = prefix > 0 ? theSamePart + value.substring(Math.min(prefix, value.length())) : value; 
    } 

    @Benchmark 
    public boolean equals(StringEquals stringEquals) { 
     return stringEquals.theSamePart.equals(stringEquals.theSamePartQuery); 
    } 

    public static void main(String[] args) throws Exception { 
     new Runner(new OptionsBuilder() 
       .include(StringEquals.class.getSimpleName()) 
       .measurementIterations(10) 
       .warmupIterations(10) 
       .build()).run(); 
    } 

} 

結果:

Benchmark       (prefix) Mode  Cnt  Score Error Units 
StringEquals.equals      0 sample 3482270  0,047 ± 0,011 us/op 
StringEquals.equals:equals·p0.00   0 sample    0,022   us/op 
StringEquals.equals:equals·p0.50   0 sample    0,035   us/op 
StringEquals.equals:equals·p0.90   0 sample    0,049   us/op 
StringEquals.equals:equals·p0.95   0 sample    0,058   us/op 
StringEquals.equals:equals·p0.99   0 sample    0,076   us/op 
StringEquals.equals:equals·p0.999   0 sample    0,198   us/op 
StringEquals.equals:equals·p0.9999   0 sample    8,636   us/op 
StringEquals.equals:equals·p1.00   0 sample   9519,104   us/op 
StringEquals.equals      5 sample 2686616  0,037 ± 0,003 us/op 
StringEquals.equals:equals·p0.00   5 sample    0,021   us/op 
StringEquals.equals:equals·p0.50   5 sample    0,028   us/op 
StringEquals.equals:equals·p0.90   5 sample    0,044   us/op 
StringEquals.equals:equals·p0.95   5 sample    0,048   us/op 
StringEquals.equals:equals·p0.99   5 sample    0,060   us/op 
StringEquals.equals:equals·p0.999   5 sample    0,238   us/op 
StringEquals.equals:equals·p0.9999   5 sample    8,677   us/op 
StringEquals.equals:equals·p1.00   5 sample   1935,360   us/op 
StringEquals.equals      10 sample 2989681  0,039 ± 0,001 us/op 
StringEquals.equals:equals·p0.00   10 sample    0,021   us/op 
StringEquals.equals:equals·p0.50   10 sample    0,030   us/op 
StringEquals.equals:equals·p0.90   10 sample    0,049   us/op 
StringEquals.equals:equals·p0.95   10 sample    0,056   us/op 
StringEquals.equals:equals·p0.99   10 sample    0,074   us/op 
StringEquals.equals:equals·p0.999   10 sample    0,222   us/op 
StringEquals.equals:equals·p0.9999  10 sample    8,576   us/op 
StringEquals.equals:equals·p1.00   10 sample   325,632   us/op 

所以假設你需要1_000_000_000字符串,則需要大約8_000_000_000我們= 8000 S表示處理1個十億串在99.99%的情況下。

與此相反,我們可以嘗試做並行方式:

import org.openjdk.jmh.annotations.*; 
import org.openjdk.jmh.runner.Runner; 
import org.openjdk.jmh.runner.options.OptionsBuilder; 

import java.util.concurrent.*; 

@State(Scope.Benchmark) 
@BenchmarkMode(Mode.SampleTime) 
@OutputTimeUnit(TimeUnit.MILLISECONDS) 
@Threads(1) 
public class SearchBillionForkJoin { 

    static final int availableProcessors = 4; // Runtime.getRuntime().availableProcessors() 
    static final int size = 10_000_000, bucketSize = size/availableProcessors; 
    static final int handlersCount = availableProcessors; 

    @Param({"50", "100"}) 
    int spinner; 

    String[] a; 
    Callable<Integer>[] callables; 
    ForkJoinTask<Integer>[] tasks; 
    QueryHolder queryHolder; 

    @Setup(Level.Trial) 
    public void setup() { 
     callables = new Callable[handlersCount]; 
     queryHolder = new QueryHolder(); 
     a = new String[size]; 
     for (int i = 0; i < callables.length; ++i) { 
      switch (i) { 
       case 0: 
        callables[i] = createForBucket(queryHolder, a, 0, bucketSize); 
        break; 
       case 1: 
        callables[i] = createForBucket(queryHolder, a, bucketSize, bucketSize * 2); 
        break; 
       case 2: 
        callables[i] = createForBucket(queryHolder, a, bucketSize * 2, bucketSize * 3); 
        break; 
       case 3: 
        callables[i] = createForBucket(queryHolder, a, bucketSize * 3, size);; 
        break; 
      } 
     } 
     tasks = new ForkJoinTask[handlersCount]; 
    } 

    @Setup(Level.Invocation) 
    public void setData() { 
     for (int i = 0; i < a.length; ++i) { 
      a[i] = String.valueOf(ThreadLocalRandom.current().nextInt()); 
     } 
     queryHolder.query = String.valueOf(ThreadLocalRandom.current().nextInt()); 
    } 

    @Benchmark 
    public Integer forkJoinPoolWithoutCopy() { 
     try { 
      for (int i = 0; i < tasks.length; ++i) { 
       tasks[i] = ForkJoinPool.commonPool().submit(callables[i]); 
      } 
      Integer position = -1; 
      boolean findMore = true; 
      head: 
      while(position == -1 && findMore) { 
       findMore = false; 
       for (int i = 0; i < tasks.length; ++i) { 
        if (tasks[i].isDone() && !tasks[i].isCancelled()) { 
         final Integer value = tasks[i].get(); 
         if (value > -1) { 
          position = value; 
          for (int j = 0; j < tasks.length; ++j) { 
           if (j != i && !tasks[j].isDone()) { 
            tasks[j].cancel(true); 
           } 
          } 
          break head; 
         } 
        } else { 
         findMore = true; 
        } 
       } 
       int counter = spinner; 
       while (counter > 0) --counter; 
      } 
      return position; 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     new Runner(new OptionsBuilder() 
       .include(SearchBillionForkJoin.class.getSimpleName()) 
       .jvmArgs("-Xmx10G") 
       .measurementIterations(10) 
       .warmupIterations(10) 
       .build()).run(); 
    } 

    static boolean isDone(ForkJoinTask[] tasks) { 
     for (int i = 0; i < tasks.length; ++i) { 
      if (!tasks[i].isDone()) { 
       return false; 
      } 
     } 
     return true; 
    } 

    static Callable<Integer> createForBucket(QueryHolder queryHolder, String[] a, int start, int end) { 
     return new Callable<Integer>() { 
      @Override 
      public Integer call() throws Exception { 
       for (int j = start; j < end; ++j) { 
        if (queryHolder.query.equals(a[j])) { 
         return j; 
        } 
       } 
       return -1; 
      } 
     }; 
    } 

    static class QueryHolder { 
     String query = null; 
    } 

} 

我用10_000_000和4個線程(4個CPU核心),因爲我沒有足夠的內存吧。 結果看起來仍然不合適。

Benchmark                  (spinner) Mode Cnt Score Error Units 
SearchBillionForkJoin.forkJoinPoolWithoutCopy           50 sample 166 47,136 ± 1,989 ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.00   50 sample   5,521   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.50   50 sample  47,055   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.90   50 sample  54,788   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.95   50 sample  56,653   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.99   50 sample  61,352   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.999   50 sample  63,635   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.9999   50 sample  63,635   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p1.00   50 sample  63,635   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy          100 sample 162 51,288 ± 4,031 ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.00   100 sample   5,448   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.50   100 sample  49,840   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.90   100 sample  67,030   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.95   100 sample  90,505   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.99   100 sample  110,920   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.999   100 sample  121,242   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p0.9999  100 sample  121,242   ms/op 
SearchBillionForkJoin.forkJoinPoolWithoutCopy:forkJoinPoolWithoutCopy·p1.00   100 sample  121,242   ms/op 

換句話說63,635 ms * 100 = 6363,5 ms = 6 s。 例如,如果您可以使用關聯鎖(每個線程一個完整的cpu),則可以改進此結果。但可能太複雜。

讓我們嘗試用段只是爲了節目的想法:

import org.openjdk.jmh.annotations.*; 
import org.openjdk.jmh.runner.Runner; 
import org.openjdk.jmh.runner.options.OptionsBuilder; 

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.concurrent.*; 

@State(Scope.Benchmark) 
@BenchmarkMode(Mode.SampleTime) 
@OutputTimeUnit(TimeUnit.MILLISECONDS) 
@Threads(1) 
public class SearchInMapBillionForkJoin { 

    static final int availableProcessors = 8; // Runtime.getRuntime().availableProcessors() 
    static final int size = 10_000_000, bucketSize = size/availableProcessors; 
    static final int handlersCount = availableProcessors; 

    Map<Integer, List<StringWithIndex>> strings; 
    QueryHolder queryHolder; 
    ForkJoinTask<Integer>[] tasks; 
    Callable<Integer>[] callables; 
    @Param({"50", "100"}) 
    int spinner; 

    @Setup(Level.Trial) 
    public void setup() throws Exception { 
     queryHolder = new QueryHolder(); 
     strings = new ConcurrentHashMap<>(); 
     tasks = new ForkJoinTask[handlersCount]; 
     callables = new Callable[handlersCount]; 
     setData(); 
    } 

    public void setData() throws Exception { 
     final int callableBucket = size/handlersCount; 
     for (int i = 0; i < handlersCount; ++i) { 
      callables[i] = createGenerateForBucket(strings, callableBucket); 
      tasks[i] = ForkJoinPool.commonPool().submit(callables[i]); 
     } 
     while(!isDone(tasks)) { 
      int counter = spinner; 
      while (counter > 0) --counter; 
     } 
     Map<Integer, Integer> distribution = new HashMap<>(); 
     for (List<StringWithIndex> stringWithIndices : strings.values()) { 
      distribution.compute(stringWithIndices.size(), (key, value) -> value == null ? 1 : value + 1); 
     } 
     int maxListSize = 0; 
     for (int i = 0; i < handlersCount; ++i) { 
      Integer max = tasks[i].get(); 
      if (max > maxListSize) { 
       maxListSize = max; 
      } 
     } 
     System.out.println("maxListSize = " + maxListSize); 
     System.out.println("list size distribution = " + distribution); 
     System.out.println("map size = " + strings.size()); 
     distribution = null; 
     queryHolder.query = String.valueOf(ThreadLocalRandom.current().nextInt()); 
    } 

    @Benchmark 
    public Integer findInSegment() { 
     final String query = this.queryHolder.query; 
     final Integer hashCode = query.hashCode(); 
     final Map<Integer, List<StringWithIndex>> strings = this.strings; 
     if (strings.containsKey(hashCode)) { 
      List<StringWithIndex> values = strings.get(hashCode); 
      if (!values.isEmpty()) { 
       final int valuesSize = values.size(); 
       if (valuesSize > 100_000) { 
        final int bucketSize = valuesSize/handlersCount; 
        callables[0] = createSearchForBucket(query, values, 0, bucketSize); 
        callables[1] = createSearchForBucket(query, values, bucketSize, bucketSize * 2); 
        callables[2] = createSearchForBucket(query, values, bucketSize * 2, bucketSize * 3); 
        callables[3] = createSearchForBucket(query, values, bucketSize * 3, values.size()); 
        try { 
         for (int i = 0; i < callables.length; ++i) { 
          tasks[i] = ForkJoinPool.commonPool().submit(callables[i]); 
         } 
         Integer position = -1; 
         boolean findMore = true; 
         head: 
         while (position == -1 && findMore) { 
          findMore = false; 
          for (int i = 0; i < tasks.length; ++i) { 
           if (tasks[i].isDone() && !tasks[i].isCancelled()) { 
            final Integer value = tasks[i].get(); 
            if (value > -1) { 
             position = value; 
             for (int j = 0; j < tasks.length; ++j) { 
              if (j != i && !tasks[j].isDone()) { 
               tasks[j].cancel(true); 
              } 
             } 
             break head; 
            } 
           } else { 
            findMore = true; 
           } 
          } 
          int counter = spinner; 
          while (counter > 0) --counter; 
         } 
         return position; 
        } catch (Exception e) { 
         throw new RuntimeException(e); 
        } 
       } else { 
        for (StringWithIndex stringWithIndex : values) { 
         if (query.equals(stringWithIndex.value)) { 
          return stringWithIndex.index; 
         } 
        } 
       } 
      } 
     } 
     return -1; 
    } 

    public static void main(String[] args) throws Exception { 
     new Runner(new OptionsBuilder() 
       .include(SearchInMapBillionForkJoin.class.getSimpleName()) 
       .jvmArgs("-Xmx6G") 
       .measurementIterations(10) 
       .warmupIterations(10) 
       .build()).run(); 
    } 

    static class StringWithIndex implements Comparable<StringWithIndex> { 
     final int index; 
     final String value; 

     public StringWithIndex(int index, String value) { 
      this.index = index; 
      this.value = value; 
     } 

     @Override 
     public int compareTo(StringWithIndex o) { 
      int a = this.value.compareTo(o.value); 
      if (a == 0) { 
       return Integer.compare(this.index, o.index); 
      } 
      return a; 
     } 

     @Override 
     public int hashCode() { 
      return this.value.hashCode(); 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (obj instanceof StringWithIndex) { 
       return this.value.equals(((StringWithIndex) obj).value); 
      } 
      return false; 
     } 

    } 

    static class QueryHolder { 
     String query = null; 
    } 

    static Callable<Integer> createSearchForBucket(String query, List<StringWithIndex> values, int start, int end) { 
     return new Callable<Integer>() { 
      @Override 
      public Integer call() throws Exception { 
       for (int j = start; j < end; ++j) { 
        StringWithIndex stringWithIndex = values.get(j); 
        if (query.equals(stringWithIndex.value)) { 
         return stringWithIndex.index; 
        } 
       } 
       return -1; 
      } 
     }; 
    } 

    static Callable<Integer> createGenerateForBucket(Map<Integer, List<StringWithIndex>> strings, 
                int count) { 
     return new Callable<Integer>() { 
      @Override 
      public Integer call() throws Exception { 
       int maxListSize = 0; 
       for (int i = 0; i < count; ++i) { 
        String value = String.valueOf(ThreadLocalRandom.current().nextInt()); 
        List<StringWithIndex> values = strings.computeIfAbsent(value.hashCode(), k -> new ArrayList<>()); 
        values.add(new StringWithIndex(i, value)); 
        if (values.size() > maxListSize) { 
         maxListSize = values.size(); 
        } 
       } 
       return maxListSize; 
      } 
     }; 
    } 

    static boolean isDone(ForkJoinTask[] tasks) { 
     for (int i = 0; i < tasks.length; ++i) { 
      if (!tasks[i].isDone()) { 
       return false; 
      } 
     } 
     return true; 
    } 

} 

結果:

Benchmark              (spinner) Mode  Cnt Score Error Units 
SearchInMapBillionForkJoin.findInSegment        50 sample 5164328 ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.00   50 sample   ≈ 10⁻⁵   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.50   50 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.90   50 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.95   50 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.99   50 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.999   50 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.9999   50 sample   0.009   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p1.00   50 sample   18.973   ms/op 
SearchInMapBillionForkJoin.findInSegment        100 sample 4642775 ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.00   100 sample   ≈ 10⁻⁵   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.50   100 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.90   100 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.95   100 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.99   100 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.999   100 sample   ≈ 10⁻⁴   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p0.9999  100 sample   0.005   ms/op 
SearchInMapBillionForkJoin.findInSegment:findInSegment·p1.00   100 sample   0.038   ms/op 

之前做任何全球性的結論,是很好的瞭解一些批評這個例子:

  • 由於基準數據的人工在列表大小之間具有非常好的分佈:例如:maxListSize = 3,列表大小di stribution = {1 = 9954167,2 = 22843,3 = 49},地圖大小= 9977059.所有迭代的maxListSize僅爲4.
  • 因此我們永遠不會進入「if(valuesSize> 100_000)」分支;我們可能不會進入「} else {for(StringWithIndex stringWithIndex:values){」,因爲「if(strings.containsKey(hashCode))」condition;
  • 這個測試與以前的測試相反,在不同的PC上運行(8 cpu,32 Gb RAM,amd64);

在這裏,你可以得到的想法,檢查是否有關鍵的地圖(或內存段),顯然,是更好的,然後遍歷所有的數據。這個主題非常廣泛。有許多人表現出色,可以說「性能優化是無限的過程」。 :) 我也應該提醒說,「預優化是不好的」,並從我這裏補充說,這並不意味着你應該設計你的系統時不加思索,不合理。

免責聲明: 所有此信息可能是錯誤的。它僅用於信息目的,可能不會被納入任何合同。在用於生產場景之前,您應該自行檢查。而且你不應該在生產代碼中使用這個信息就是指我。我對可能出現的資金損失不承擔任何責任。所有這些信息都不是指我曾經工作過的任何公司。我不隸屬於任何MySQL/MongoDB/Cassandra/BigTable/BigData以及Apache Ignite/Hazelcast/Vertica/Clickhouse/Aerospike或任何其他數據庫。

+0

感謝您的回覆,將等待更多的洞察力從你身邊。 –

+0

謝謝,等待,我添加了信息。 – egorlitvinenko

0
  1. 你仍然可以利用基於RAM的查找,並且還具有專門的數據庫提供了相比於RAM一個普通的HashMap /數組作爲額外的功能。

  2. 你與基於RAM的查找目標是快速查找,避免網絡開銷。但是,兩者都可以通過本地託管數據庫來實現,或者網絡甚至不會成爲像名稱這樣的小數據負載的開銷。

  3. 通過RAM陣列的方法,該應用的適應能力降低,你有失敗的單點,不是一件容易的快照即你將不得不做一些數據變暖每次您的應用程序的更改或重新啓動,和你將始終是限制爲單個查詢模式,並且可能在未來不能發展。

  4. 與合理可比可以通過同樣良好的替代品可以是在上SSD機的羣集或主 - 從配置,或redis的。通過分片/集羣(即8個實例中的1/8數據),您可以獲得持續快照,高吞吐量,分佈和彈性的優勢,從而不會出現單點故障。