[英]why it is so slow with 100,000 records when using pipeline in redis?
據說在redis中需要很多set/get
時pipeline
是更好的方法,所以這是我的測試代碼:
public class TestPipeline {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
list.add(si);
ShardedJedis jedis = new ShardedJedis(list);
long startTime = System.currentTimeMillis();
ShardedJedisPipeline pipeline = jedis.pipelined();
for (int i = 0; i < 100000; i++) {
Map<String, String> map = new HashMap<String, String>();
map.put("id", "" + i);
map.put("name", "lyj" + i);
pipeline.hmset("m" + i, map);
}
pipeline.sync();
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
}
}
當我運行它時,這個程序暫時沒有響應,但是當我不使用pipe
,它只需要20073毫秒,所以我很困惑,為什么沒有pipeline
甚至更好的差距!
謝謝你回答我,幾個問題,你如何計算6MB數據? 當我發送10K數據時,管道總是比正常模式更快,但是100k,管道沒有響應。我認為100-1000操作是一個明智的選擇,如下所述。是不是有任何JIT,因為我不明白?
在編寫此類基准測試之前需要考慮幾點(尤其是使用JVM的基准測試):
在大多數(物理)機器上,當使用流水線時,Redis能夠處理超過100K的操作數/秒。 您的基准僅處理100K項目,因此它的持續時間不足以產生有意義的結果。 此外,JIT的連續階段沒有時間開始。
絕對時間不是一個非常相關的指標。 在保持基准運行至少10秒的同時顯示吞吐量(即每秒的操作次數)將是更好且更穩定的度量。
你的內循環會產生大量垃圾。 如果您計划對Jedis + Redis進行基准測試,那么您需要將自己程序的開銷保持在較低水平。
因為你已經在main函數中定義了所有內容,所以你的循環不會被JIT編譯(取決於你使用的JVM)。 只有內部方法調用可能是。 如果希望JIT高效,請確保將代碼封裝到可由JIT編譯的方法中。
可選地,您可能希望在執行實際測量之前添加預熱階段,以避免計算使用裸骨解釋器運行第一次迭代的開銷以及JIT本身的成本。
現在,關於Redis流水線,您的管道太長了。 管道中的100K命令意味着Jedis必須在向Redis發送任何內容之前構建一個6MB的緩沖區。 這意味着套接字緩沖區(在客戶端,可能是服務器端)將飽和,並且Redis也必須處理6 MB通信緩沖區。
此外,您的基准測試仍然是同步的(使用管道不會神奇地使其異步)。 換句話說,在將管道的最后一個查詢發送到Redis之前,Jedis不會開始閱讀回復。 當管道太長時,它有可能阻塞事物。
考慮將管道的大小限制為100-1000個操作。 當然,它會產生更多的往返,但通信棧的壓力將降低到可接受的水平。 例如,考慮以下程序:
import redis.clients.jedis.*;
import java.util.*;
public class TestPipeline {
/**
* @param args
*/
int i = 0;
Map<String, String> map = new HashMap<String, String>();
ShardedJedis jedis;
// Number of iterations
// Use 1000 to test with the pipeline, 100 otherwise
static final int N = 1000;
public TestPipeline() {
JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
list.add(si);
jedis = new ShardedJedis(list);
}
public void push( int n ) {
ShardedJedisPipeline pipeline = jedis.pipelined();
for ( int k = 0; k < n; k++) {
map.put("id", "" + i);
map.put("name", "lyj" + i);
pipeline.hmset("m" + i, map);
++i;
}
pipeline.sync();
}
public void push2( int n ) {
for ( int k = 0; k < n; k++) {
map.put("id", "" + i);
map.put("name", "lyj" + i);
jedis.hmset("m" + i, map);
++i;
}
}
public static void main(String[] args) {
TestPipeline obj = new TestPipeline();
long startTime = System.currentTimeMillis();
for ( int j=0; j<N; j++ ) {
// Use push2 instead to test without pipeline
obj.push(1000);
// Uncomment to see the acceleration
//System.out.println(obj.i);
}
long endTime = System.currentTimeMillis();
double d = 1000.0 * obj.i;
d /= (double)(endTime - startTime);
System.out.println("Throughput: "+d);
}
}
使用此程序,您可以使用或不使用流水線進行測試。 使用流水線時,請務必增加迭代次數(N參數),以使其運行至少10秒。 如果你在循環中取消注釋println,你會發現程序在開始時很慢並且隨着JIT開始優化而變得更快(這就是為什么程序應該運行至少幾秒鍾以產生有意義的結果)。
在我的硬件(舊的Athlon盒子)上,當使用管道時,我可以獲得8-9倍的吞吐量。 通過優化內循環中的鍵/值格式化並添加預熱階段,可以進一步改進程序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.