redis性能測試
一 測試目的
開發需要為了尋求系統最優的解決方案,但是網上關於性能方面的資料並不是很多,沒有很明顯的數據說明,只是一般的結論性判斷不好說什麽,所以這裏自己重新測試整理了一番.
二 測試環境
主機主機ip為...86,系統為64位win7,4g內存,雙核2.93GHz,2.94GHz,從機ip為...59,系統為64位win7,6g內存,雙核2.93GHz,2.94GHz.
雙機在同一個局域網,開啟了密碼驗證,主從配置通過配置文件直接配置後,從機配置為不可寫入.
開發工具為Eclipse Indigo Service Release 1,java環境為32位java1.6 .Redis為Redis-x64-3.0.504版本,redis架包為jedis-2.9.0.jar.
三 開始測試
3.1 一般測試
3.1.1 測試方法:
直接手動操作,針對redis進行各種狀況的模擬.主要涉及到的工具有redis,management tool for redis.cmd窗口.
3.1.2 測試過程:
主從均關閉,開啟主redis導入少量數據到主redis,開啟從redis,從redis有一樣的數據.
主從均關閉,開啟從redis,刪除少量數據到從redis(management tool for redis),開啟主redis,主redis數據不變化,刷新從,從redis恢復原來的數據.
主從均開啟,操作部分數據到主redis,從redis有同樣的數據,
主從均開啟,主導入部分數據之後從有同樣數據,關閉主之後再次開啟,主的數據恢復.
主從均開啟,主導入部分數據之後從有同樣數據,關閉從之後再次開啟,從的數據恢復.
主從均開啟,主導入部分數據之後從有同樣數據,關閉主之後,刪除本地文件dump.rdb,然後主再次開啟,主的數據沒有,從的數據直接同步為主的沒有數據狀態,cmd操作從機發現無法進行寫入.
主從均開啟,主導入部分數據之後從有同樣數據,關閉從之後,刪除本地文件dump.rdb,再次開啟從,從的數據恢復.
3.1.3 測試結論:
主機開啟之後,首先從dump文件恢復數據,之後會不定期同步數據到磁盤的dump.rbd文件,關閉之後再起開啟會再次直接從dump恢復數據,如果沒有這個文件會造成數據清空的狀況.
從開啟之後,會從dump文件回復數據,之後查找網絡的主機,找到之後進行一個備份編碼的對比,如果相同就進行局部同步,不同就進行全同步--即發送全同步請求之後,直接獲取到從機的dump文件,之後直接清空從機的數據之後從文件恢復.
3.2代碼測試
3.2.1 寫入數據測試
3.2.1.1 操作方法
通過寫程序對數據的作進行寫入操作並進行分析.
3.2.1.2 操作代碼
直接通過jedis代碼一個個寫入數據:
private static void EngineDataCreat() { Jedis jedis = new Jedis(rip, rport); jedis.auth(rpassword); System.out.println("Server is running: " + jedis.ping() + " Start to write!"); jedis.flushAll(); MyTimeUtil mt = new MyTimeUtil(); for (int j = 0; j < 1; j++) { //寫入的次數,多次以求取平均值 mt.start(); for (int i = 0; i < 1000; i++) { for (PlanProprity e : PlanProprity.values()) { jedis.set("China_beijing_plane:" + i + ":" + e, " " + mt.getTime()); } } for (int i = 0; i < 1000; i++) { for (CarProprity e : CarProprity.values()) { jedis.set("China_hubei_car:" + i + ":" + e, " " + mt.getTime()); } }
mt.endAndDisp(); } System.out.println("Server is running: " + jedis.ping() + "Write finished!"); }
直接通過jedis管道進行數據的批量寫入:
private static void EngineDataCreatByPipe() { Jedis jedis = new Jedis(rip, rport); jedis.auth(rpassword); System.out.println("Server is running: " + jedis.ping() + " Start to write!"); jedis.flushAll(); MyTimeUtil mt = new MyTimeUtil(); for (int j = 0; j < 1; j++) { mt.start(); Pipeline pipeline = jedis.pipelined(); for (int i = 0; i < 1000; i++) { for (PlanProprity e : PlanProprity.values()) { pipeline.set("China_beijing_plane:" + i + ":" + e, " " + mt.getTime()); } } for (int i = 0; i < 1000; i++) { for (CarProprity e : CarProprity.values()) { pipeline.set("China_hubei_car:" + i + ":" + e, " " + mt.getTime()); } } pipeline.syncAndReturnAll(); mt.endAndDisp(); } System.out.println("Server is running: " + jedis.ping() + "Write finished!"); }
上面的那個計時的類是自己寫的一個工具,可以參考下:
http://www.cnblogs.com/wangkun1993/p/7199146.html 自寫時間小工具類
rip就是主機和從機的ip號,rport是端口好,rpassword是驗證的密碼.
遍歷的兩個是兩個大小為十的枚舉類型:
enum PlanProprity { PSpeed, PPrice, PLength, PHeight, PWidth, PCapacity, PMore1, PMore2, PMore3, PMore4 }; enum CarProprity { CSpeed, CPrice, CMOre1, CMOre2, CMOre3, CMOre4, CMOre5, CMOre6, CMOre7, CMOre }
3.2.1.3 操作結果
主單個寫入10000條數據耗時: 24454ms 28491ms 45811ms 38017ms 22528ms 17845ms 18502ms 18079ms
主通過管道一次寫入10000數據耗時: 388ms 503ms 1288ms 922ms 419ms 341ms 342ms 325ms
寫入20w數據4356ms
寫入200w數據34121ms
從機無法寫入數據.
從更改為主後測試遠程寫入數據:
從單個寫入10000條數據耗時 6528ms 26953ms 32541ms 24982ms 22493ms 29789ms 31062ms
從通過管道一次寫入10000數據耗時 227ms 753ms 599ms 387ms 508ms 531ms 507ms
3.2.1.4 結果分析
通過對比發現數據的寫入速度通過管道實現效率得到了極大的提升,電腦的處理速度和網絡的連通率對於遠程操作的影響還是比較大的.
3.2.2 讀取數據測試
3.2.2.1 操作方法
通過代碼對redis進行數據的獲取操作.
3.2.2.2 操作代碼
直接通過get函數單個獲取數據操作:
private static void getDate() { Jedis jedis = new Jedis(rip, rport); jedis.auth(rpassword); System.out.println("Server is running: " + jedis.ping()); // jedis.flushAll(); MyTimeUtil mt = new MyTimeUtil(); String strSaveDir = "d:\\edata\\"; String fileName = "BAIHEYUAN_HW_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".DJ"; // String fileName="BAIHEYUAN_HW_20161122_0030.DJ"; MyFileUtil mf = new MyFileUtil(); String fileContent = null, tcont = null; String dateType = "China_beijing_plane"; System.out.println("Start to get data"); for (int j = 0; j < 1; j++) { fileContent = null; mt.start(); for (int i = 0; i < 1000; i++) { for (PlanProprity e : PlanProprity.values()) { tcont = jedis.get(dateType + ":" + i + ":" + e); // fileContent += dateType+":" + i + ":\t" + e + tcont // + "\r\n"; } } mt.end(); mt.disp(); // if(mf.existsDictionary(strSaveDir + fileName)){ // mf.createFile(strSaveDir + fileName, fileContent); // } } System.out.println("Get data end!"); System.out.println("Server is running: " + jedis.ping()); }
通過管道進行批量操作:
private static void getDateByPipe() { Jedis jedis = new Jedis(rip, rport); jedis.auth(rpassword); System.out.println("Server is running: " + jedis.ping()); // jedis.flushAll(); MyTimeUtil mt = new MyTimeUtil(); String strSaveDir = "d:\\edata\\"; String fileName = "BAIHEYUAN_HW_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".DJ"; // String fileName="BAIHEYUAN_HW_20161122_0030.DJ"; MyFileUtil mf = new MyFileUtil(); String fileContent = null; Response<String> tcont = null; String dateType = "China_beijing_plane"; System.out.println("Start to get data"); for (int j = 0; j < 1; j++) { fileContent = null; mt.start(); Pipeline pipeline = jedis.pipelined(); for (int i = 0; i < 100; i++) { for (PlanProprity e : PlanProprity.values()) { tcont = pipeline.get("China_beijing_plane:" + i + ":" + e); fileContent += dateType + ":" + i + ":\t" + e + tcont + "\r\n"; } } pipeline.syncAndReturnAll(); mt.end(); mt.disp(); if (mf.existsDictionary(strSaveDir + fileName)) { mf.createFile(strSaveDir + fileName, fileContent); } } System.out.println("Get data end!"); System.out.println("Server is running: " + jedis.ping()); }
上面的代碼中除了獲取數據之外,另外還有一個導出數據到字符串和一個導出到文本的操作,後面發現這兩個操作大大影響了時間效率,因此作為對比,進行了註釋之後的二次測試.
如果對於文件類比較感興趣可以參考下我寫的一個工具類: http://www.cnblogs.com/wangkun1993/p/7199326.html 自寫文件小工具類
3.2.2.3 操作結果
主獲取數據測試:
單個獲取10000條數據時間:64704ms
去掉寫數據操作 19356ms 20747ms 17678ms 20170ms
單個獲取1000條數據時間:3202ms 2432ms 1791ms 2857ms 2218ms 2107ms
去掉寫數據操作 1255ms 1267ms 1570ms 1408ms 1137ms
單個獲取100條數據時間:186ms
去掉寫數據操作 169ms 180ms 236ms 218ms
批量獲取10000條數據時間:31367ms 31749ms 23582ms
去掉寫數據操作 367ms 308ms 375ms
批量獲取1000條數據時間:171ms 215ms 208ms 226ms 248ms 199ms 185ms 177ms
去掉寫數據操作 86ms 62ms 45ms
從獲取數據測試:
單個獲取1000條數據時間:1675ms 826ms 691ms 667ms 1112ms 1930ms 614ms
去掉寫數據操作 955ms 818ms 959ms 1007ms 1187ms 338ms 494ms
單個獲取100條數據時間:66ms 44ms 46ms
去掉寫數據操作 121ms 137ms 101ms
批量個獲取10000條數據時間:29965ms 30986ms 22533ms
去掉寫數據操作 363ms 323ms 360ms
批量獲取1000條數據時間:174ms 145ms 135ms
去掉寫數據操作 55ms 27ms 12ms
3.2.2.4 結果分析
從上面的數據可以看到,通過管道獲取數據想效率比一般獲取數據要高的多,因此數據量比較多的時候顯然管道操作是比較合適的,如果只是幾個數據的操作,一般兩種方式都行了.
對於操作的時候如果進行一些文件的導出操作,可以看到完成的時間是大大增加了,因此如果需要在獲取數據之後進行一些其他的操作,最好認真斟酌一下,可以在數據獲取完畢之後直接從內存中讀取數據,進行一系列的操作.
3.2.3 恢復數據測試
3.2.3.1 操作方法
通過代碼,針對操作之後恢復數據的時間進行一個判斷,
3.2.3.2 操作代碼
private static void testRecovery() { Jedis jedis = new Jedis(rip, rport); long t = 0; boolean tag=true; for (int i = 0; i < 1;) { try { String ttt = null; if ("OK" .equals(jedis.auth(rpassword))&&tag==true) { t = System.currentTimeMillis(); System.out.println(System.currentTimeMillis() - t+" OK");//break; tag=false; } if ("PONG".equals(jedis.ping())) { System.out.println(System.currentTimeMillis() - t+" PONG");//break; } if (jedis.dbSize() >= 20000) { System.out.println((System.currentTimeMillis() - t)+" all data"); break; } } catch (Exception e) { // TODO: handle exception System.out.println("test"); } } System.out.println(System.currentTimeMillis() - t+" all out"); }
代碼沒有什麽特殊的地方,直接運行就行了,運行之後才打開redis進行恢復操作,從文件恢復的時間太短,不好統計,直接取的它自己顯示的數字,主要測試的是遠程服務器傳輸數據需要話費的時間.
3.2.3.3 操作結果
主恢復數據測試(2w數據880kb)
直接dump文件恢復(s) 0.044 0.049 0.047
測試20w(2.89m)恢復時間) 0.15s
刪除主機備份文件無法恢復.
從恢復數據測試(2w數據880kb)
直接dump文件恢復(s) 0.40 0.039 0.040 0.039
刪除從機備份文件之後網絡獲取文件回復
2w數據3.35s 3.40s
3.2.3.4 結果分析
從結果來看,影響恢復速度的因素主要是網絡的問題,從文件恢復幾乎可以在秒級完成,可以忽略不計,當然需要看情況啦,至少我邊項目不需要考慮從文件恢復的時間.恢復的速度還是比較滿意的,測試數據時候的因為是內部網絡,所以這個值不好做評判參考類.
目前測試就這些,後續可能繼續進行其他測試,會繼續修改這個博客.
個人原創,轉載請註明出處.
歡迎指出不足之處以作改進.
redis性能測試