实战 Redis 序列化性能测试 (Kryo 和字符串)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
在 Java 应用的开发中,有时候需要将 Java 对象实例保存在 Redis 中,常用方法有两种:
将对象序列化成字符串后存入 Redis;
将对象序列化成 byte 数组后存入 Redis;
以上两种方式孰优孰劣?字符串方式来存取的好处是编码和调试更简单容易,而 byte 数组的优势又在哪里呢,今天我们针对这两种存储方式做一次对比试验,用数据来得出结论;
测试方法简述
本次做的是对比测试,写 Redis 和读 Redis 都会测试到,测试一共有以下四种:
并发场景下对象通过 fastjson 转字符串,然后存入 Redis;
并发场景下对象通过 Kyro 序列化成 byte 数组,然后存入 Redis;
并发场景下从 Redis 取出字符串,通过 fastjson 转成对象;
并发场景下从 Redis 取出 byte 数组,然后通过 Kyro 反序列化成对象;
测试环境简介
本次测试需要以下三台电脑,全部是 Linux:
Redis 服务器;
Web 应用服务器;
安装有 Apache bench,用于发起性能测试,并统计出测试结果;
整体部署情况如下:
测试步骤梳理
在正式开始前,先将所有步骤整理好以免遗漏,接下来一步一步进行就可以了:
部署 Redis;
开发基于字符串存取的 web 应用 redis-performance-demo-string;
开发基于 Kyro 序列化存取的 web 应用 redis-performance-demo-kryo;
web 应用编译构建;
在测试端机器上安装 Apache bench;
部署应用 redis-performance-demo-string;
用 Apache bench 先 web server 发起请求,然后丢弃测试结果,这次请求中部分处理是在 JIT 之前完成的,不算数;
清理 Redis 数据,用 Apache bench 先 web server 再次发起请求,保存测试结果;
清理 Redis 数据,部署应用 redis-performance-demo-kryo;
用 Apache bench 先 web server 发起请求,然后丢弃测试结果,这次请求中部分处理是在 JIT 之前完成的,不算数;
清理 Redis 数据,用 Apache bench 先 web server 再次发起请求,保存测试结果;
对比结果,得出测试结论;
本章源码下载
本章实战的源码可以在 github 下载,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在以下两个文件夹中:
redis-performance-demo-string:对应字符串存取对象的应用;
redis-performance-demo-kryo:对应 kryo 序列化对象的应用;
如下图所示:
应用版本
JDK:1.8.0_161;
Maven:3.5.0;
SpringBoot:1.4.1.RELEASE;
Redis:3.2.12.;
Fastjson:1.2.47;
Kryo:4.0.0;
Apache bench:2.3;
Ubuntu:16.04.3 LTS;
接下来我们开始实战吧;
部署 Redis
Redis 的安装和部署就不在本章展开了,以下两点请注意:
关闭 redis 远程保护:config set protected-mode "no";
修改 conf 文件,关闭持久化;
开发基于字符串存取的 web 应用 redis-performance-demo-string
这是个基于 SpringBoot 的简单 web 应用,将几处重点列举出来:
首先是 application.properties 文件中有 Redis 配置信息,请将 IP 和端口替换为您的 Redis 服务器的 IP 和端口:
其次,是 web 接口对应的 controller 类 RedisController.java:
关于该类有以下几处需要注意:
字符串转对象、对象转字符串的操作都是通过 Fastjson 实现的;
add 方法是用于写性能测试的主要方法,每次请求该接口,都会连续执行 100 次对象到字符串的转换,然后写入 Redis;
check 方法是用于读性能测试的主要方法,每次请求该接口,都会连续执行 100 次读取 Redis,然后将字符串转换成对象;
add 和 check 方法中获取 Redis 连接时都有可能获取失败,所以如果发生异常就 sleep 后再重试;
成员变量 addPersionIdGenerator、checkPersionIdGenerator 都是用于 id 增长的 AtomicInteger 实例,这样性能测试时就不用输入 id 了,用这两个对象生成连续的 id;
Helper.success 和 Helper.error 方法会设置 Response 的返回码,Apache bench 是根据 Response 的返回码是否位 200 来判定请求是成功还是失败;
开发基于 Kyro 序列化存取的 web 应用 redis-performance-demo-kryo
在 SpringBoot 框架使用 Kyro 作为 Redis 序列化工具的详细过程请参考《SpringBoot下用Kyro作为Redis序列化工具》, 这里就不多说了,同样是类需要关注:
以上代码,同样需要关注的是 add 和 check 方法,它们是性能测试时被调用的接口;
web 应用编译构建
在应用 redis-performance-demo-string 的 pom.xml 所在目录执行命令 mvn clean package -U -DskipTests,编译构架成功后,在 target 目录下得到文件 redis-performance-demo-string-0.0.1-SNAPSHOT.jar;
在应用 redis-performance-demo-kryo 的 pom.xml 所在目录执行命令 mvn clean package -U -DskipTests,编译构架成功后,在 target 目录下得到文件 redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar;
redis-performance-demo-string-0.0.1-SNAPSHOT.jar 和 redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar 这两个文件留在稍后部署 web 应用的时候使用;
在测试端机器上安装 Apache bench
准备一台 Linux 机器作为执行性能测试的机器,在上面安装 Apache bench,对于 ubuntu 执行以下命令即可完成安装:
本次性能测试,我在一台树莓派 3B 上安装了 Apache bench,作为性能测试的执行机器,如果您手里有这类设备也可以尝试,先安装 64 位 Linux 操作系统,详情参照《树莓派 3B 安装 64 位操作系统(树莓派无需连接显示器键盘鼠标)》;
部署应用 redis-performance-demo-string
将前面生成的 redis-performance-demo-string-0.0.1-SNAPSHOT.jar 文件复制到 web 应用服务器上,执行命令 java -jar >redis-performance-demo-string-0.0.1-SNAPSHOT.jar,即可启动应用;
redis-performance-demo-string 应用预热
用 Apache bench 先 web server 发起请求,然后丢弃测试结果,因为这次请求中部分处理是在 JIT 之前完成的,不算数;
在 Apache bench 所在机器上执行如下命令即可发起序列化和写入 Redis 的性能测试:
以上是序列化和写入 Redis 的测试,执行完毕后再执行下面的读 Redis 和反序列化的性能测试:
192.168.31.104 是部署 redis-performance-demo-string 应用的应用服务器 IP 地址,8080 是应用启动后监听的端口;
正式压测 redis-performance-demo-string 并保存结果
先清理预热时残留的数据,在 Redis 服务器上执行 redis-cli 进入命令行,然后执行 flushall 清除该 Redis 所有数据,注意:该命令会删除 Redis 上全部数据,请慎用!!!;
通过浏览器访问地址:http://192.168.31.104:8080/reset, 将生成 id 的全局变量重新设置为 0;
测试序列化和写入,在 Apache bench 所在机器再次执行 ab -n 150000 -c 200 http://192.168.31.104:8080/add , 等测试结束后,记录测试结果中的三个关键信息如下:
去 Redis 服务器执行命令 info,得到 Redis 内存使用大小为 3.30G(used_memory_human);
去 Redis 服务器执行命令 dbsize,得到记录数为 15000000,符合预期;
测试反序列化和读取,在 Apache bench 所在机器执行 ab -n 150000 -c 200 http://192.168.31.104:8080/check ,等测试结束后,记录测试结果中的三个关键信息如下:
部署应用 redis-performance-demo-kryo
将前面生成的 redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar 文件复制到 web 应用服务器上,执行命令 java -jar >redis-performance-demo-kryo-0.0.1-SNAPSHOT.jar,即可启动应用;
redis-performance-demo-kryo 应用预热
用 Apache bench 先 web server 发起请求,然后丢弃测试结果,因为这次请求中部分处理是在 JIT 之前完成的,不算数;
在 Apache bench 所在机器上执行如下命令即可发起序列化和写入 Redis 的性能测试:
以上是序列化和写入 Redis 的测试,执行完毕后再执行下面的读 Redis 和反序列化的性能测试:
192.168.31.104 是部署 redis-performance-demo-kryo 应用的应用服务器 IP 地址,18080 是应用启动后监听的端口;
正式压测 redis-performance-demo-kryo 并保存结果
先清理预热时残留的数据,在 Redis 服务器上执行 redis-cli 进入命令行,然后执行 flushall 清除该 Redis 所有数据,注意:该命令会删除 Redis 上全部数据,请慎用!!!;
通过浏览器访问地址:http://192.168.31.104:18080/reset , 将生成 id 的全局变量重新设置为 0;
测试序列化和写入,在 Apache bench 所在机器再次执行 ab -n 150000 -c 200 http://192.168.31.104:18080/add , 等测试结束后,记录测试结果中的三个关键信息如下:
去 Redis 服务器执行命令 info,得到 Redis 内存使用大小为 3.20G:
去 Redis 服务器执行命令 dbsize,得到记录数为 15000000,符合预期;
测试反序列化和读取,在 Apache bench 所在机器执行 ab -n 50000 -c 500 http://192.168.31.104:18080/check ,等测试结束后,记录测试结果中的三个关键信息如下:
至此,性能测试已经完毕,我们把关键的 QPS 和内存大小拿来对比一下,如下表所示:
从以上对比可以发现:
两种序列化方案的数据存入 Redis 后,kryo 占用内存小于 string,但是优势并不明显;
不论是读还是写,kryo 方案的吞吐率低于 sting 方案,这和之前预期的不同,但是网上已经有很多实践证明 kryo 方案的速度优于字符串方案,所以除了 kryo 本身的优势,对于 kryo 方案的集成以及 redis 连接管理等因素对吞吐率都有影响,SpringBoot 的 StringRedisTemplate 看来是个优秀的处理工具;
测试的硬件环境与生产环境有着不小差别,所以数据仅供参考,也可能是我的测试代码质量堪忧所致(囧),如果您发现其中的问题,期待您的及时指正;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/bbc413a9dc9ee188b52482d28】。文章转载请联系作者。
评论