首页 > 代码库 > redis性能测试
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有同样的数据,
主从均开启,删除部分数据(management tool for redis)到从redis,主redis原有数据不会变动,再次刷新从redis发现更改未生效,cmd操作从机发现无法进行写入,
主从均开启,主导入部分数据之后从有同样数据,关闭主之后再次开启,主的数据恢复.
主从均开启,主导入部分数据之后从有同样数据,关闭从之后再次开启,从的数据恢复.
主从均开启,主导入部分数据之后从有同样数据,关闭主之后,删除本地文件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性能测试