性能项目中,性能数据是重要的输入资源。但有人用极少的数据,来做较大压力,显然不符合真实场景,虽然拿到的结果好看,但无价值。
性能场景中的数据到底应该做成啥样?RESAR性能工程中,场景里使用的数据要满足:
- 符合真实环境中的数据分布
才能模拟出相应的IO操作 - 符合真实用户输入的数据
以真正模拟真实环境中的用户操作
分别对应:铺底数据和参数化数据。
1 铺底数据
线上系统架构中,系统常用到的数据分为:
- 静态数据(红点)
- 动态数据(绿点)
这也是性能场景中要存入的铺底数据:
若无铺底数据,相当于一个空系统。但生产环境中,这系统肯定不会空,所以要有足够数据。若数据不真实,就无法模拟生产上有真实数据的场景,如应用的内存占用、数据库IO能力、网络吞吐能力。
静态数据,易出现的问题是:
- 一想到占网络带宽大,就觉得要用CDN
- 或觉得不模拟静态数据,就不符合真实场景,不支持我们的优化结果
其实数据放在哪,怎么做合理,怎么做成本低,都要综合考虑,不是随大流:
- 有的官方门户网站没啥流量,技术规划时非把几个图片放CDN,以显示架构多先进
- 一些企业认为网站上的图片很重要,不懂技术又寻找安全感,非把图片都放到自己的服务器。本来图片就很大,一张有3~4M,用户一访问,自然就慢
这两种极端都不可取。外行指使内行干活,基本没好结果,因为有些外行觉得只要压力发起就可,细节上根本不在乎结果。处理这样问题的最合理方式:
- 先分析业务逻辑
- 再判断技术架构实现
1.1 静态数据
通常有两个可存放地方:
- 服务端的Web层
- CDN
大系统的流量大,网络带宽就得多。这种情况下,数据必然要放CDN。如果不使用 CDN 技术,数据需要从一个中心服务器传输到用户的设备上,会导致:
- 延迟高:由于数据需要从远程服务器传输,因此会增加访问的延迟,降低用户体验。
- 网络拥塞:如果所有用户都从同一个服务器上请求数据,会导致网络拥塞,降低整个系统的响应能力。
- 安全性问题:如果所有用户都从同一个服务器上请求数据,这也意味着所有用户的数据都存储在同一个地方,一旦服务器遭受attack或数据泄漏,用户的数据就会受到威胁。
因此,使用 CDN 技术可以将数据存储在距离用户更近的服务器上,可以提高访问速度和响应能力,减少延迟和网络拥塞的问题,并且可以提高系统的安全性。
对一些小的业务系统,由于用户不多,整体网络流量要求也少,可将静态数据直接放到负载均衡服务器(如Nginx)或应用服务器。用户访问一次之后,后续访问直接走本地缓存,对系统压力也不会产生多大影响。
1.2 动态数据
需分析,因为有些动态数据可放CDN。
- 不用任何预热加载时,这些动态数据都存DB
- 使用预热加载时,这些数据就会转到缓存(也取决于架构设计和代码实现),变成:
按这样逻辑,真实场景中业务操作的数据量实际有多少,我们就要模拟多少,不然会出现一些问题。当模拟数据量与实际数据量差别较大,会对DB、Cache等造成不同
1.3 影响
① 对DB压力的区别
设线上系统中100万用户,压测时,由于无生产数据,造数据又麻烦,就直接使用1000条甚至更低用户量做性能场景。
一个表里有100万条数据和1000条数据的差别是啥?前提条件:同样硬件环境、数据库、表结构、索引,只是两张表的数据不同。两条SQL:
1select * from ob_tuning.temp1_1000 where id = '3959805';2select * from ob_tuning.temp2_100w where id = '3959805';
因为表数据量不同:
表操作细节。第一个表(用户量1000):
第二个表(用户量100w):
只需对比“executing”行就能看到明显差距,它告诉我们当执行语句时,需要的CPU时间明显因为数据量增加而增加。所以,若无足够铺底数据放在性能场景,一开始便注定悲剧。
② 缓存的区别
数据量的多少在缓存中有明显区别:
场景中用的数据量越多,缓存必然要求越大。
③ 压力工具使用的区别
压力工具中使用的数据多少,不仅影响着压力工具本身需要的内存,同时也影响着性能场景的执行结果。
④ 网络的区别
不同的数据量,不管是走缓存,还是数据库,对客户端和服务器之间的网络消耗都是差不多的。只要不是缓存在客户那边,都是要走到服务器里转一圈的。
所以我们认为,数据量是多还是少,对客户端和服务器之间的网络的压力没有什么区别。如果你用的是CDN,那可以做另外的考虑。
⑤ 应用的区别
如果不是在应用中直接缓存数据,我们也认为对应用没什么区别,反正不管是什么样的请求过来,都是要到缓存或数据库中去取数据的,应用的Self Time不会有什么差别,方法依旧要执行。但是,如果你的应用是直接在应用的缓存中存数据的,那就有区别了,同样也是数据量越大,对内存的要求就越大。
基于这几点,可以看到有两个较重要环节:数据库和缓存,这是直接影响。
间接影响,比如数据在数据库中执行得慢了,在同步调用的应用中必然需要更多的应用线程来处理。
假设有一个100 TPS的系统,先忽略其他时间,只看数据库时间。如果数据库执行需要10ms,那应用只需要一个线程就能处理完了。如果数据库需要100ms,而我们仍想达100TPS,应用就得有10个线程同时处理。
整个链路上的所有线程、队列、超时等都会因为受到数据量的影响而产生大的变化。
要想模拟出生产时候的样子,铺底数据不能含糊。
2 参数化数据
场景中该用多少量数据,是性能场景中最容易出问题的环节。
参数化数据量应该多少,取决于场景运行多长时间。而在场景运行中,通常用到两类数据:
- 唯一性数据
- 可重复使用的数据
2.1 计算方式
① 唯一性数据(如用户数据)
要使用多少参数化数据很容易计算。比如一个运行半小时的场景,TPS如果是100的话,就要18万数据量:
$数据量 = 30min \times 60s \times 100TPS = 18w$
② 可重复使用的数据量
要分析真实业务场景中如何重复,如电商系统中商品的数据量,参数化时就能重复,毕竟多个人是可同时购买同一商品。我们假设平均有1000个用户在10个商品中,那当我们有18万个用户时,就需要1800个商品:
$商品数量 = 18w用户 \div 1000用户 \times 10 商品 = 1800 商品$
若参数化数据量太大,在压力工具中处理不了咋办?如JMeter处理文件参数化数据时,参数化文件太长,会导致JMeter消耗更多时间。这种参数化数据量要求多的情况,可采用连接远程缓存(如Redis)或数据库(如MySQL)的方式做参数化。
2.2 连接Redis做参数化
方法一:直接在JMeter写Beanshell连接Redis取数据
1import redis.clients.jedis.Jedis;2 //连接本地的 Redis 服务3Jedis jedis = new Jedis("172.16.106.130",30379);4log.info("服务正在运行: "+jedis.ping());5String key = vars.get("username");6String value = vars.get("token");7vars.put("tokenredis",jedis.get(key));
方法二:使用Redis Data Set组件
这俩都能用Redis做参数化的数据源。
2.3 连接MySQL做参数化
① 创建一个JDBC Connection Configuration。
配置好连接信息,如用户名密码:
② 创建一个JDBC Request
用JDBC Request把数据取回来:
③ 用${user_name}引用参数
完成上述三步,就实现了用DB的方式做参数化。
3 怎么造数据
- 用户数据
- 地址数据
- 商品数据
- 订单数据
数据量如何设置?
根据第4讲的性能方案,登录TPS如果是每秒150,并且如果按容量场景的需求,在场景连续递增时,大概在20分钟内(这是一个经验值,在具体的场景执行中也会有变化)会递增到最大值,然后再执行10分钟,也就是说总时间大概为30分钟。
但是因为场景是递增的,一开始我们并没有要求达到150TPS,同时登录场景TPS最大值能达到多少,我们现在也没法预知。根据经验来看的话,登录的TPS在当前的硬件环境中,就算是不走缓存,达到三、四百应该是没有多大问题的。
如果按最大400TPS来算,跑半个小时,需要的数据量就是54万,而我们造出来的用户量要远远大于这个量级。这里我们就先造200万的用户量,因为地址的数据量肯定大于用户的数据量,所以会多于200万。
先查当前DB中的数据量,再确定要造多少。
这数据量级明显不够。
怎么造出那么多数据量?造的数据主要分为:用户数据和订单数据。
- 登录用户
先了解表结构,造出数据只有符合业务逻辑才能使用。看用户表结构和数据:
地址表:
造数据时,不要往DB直接写存储过程插数据,除非你清楚表之间关系,且存储过程写得6。否则你会把DB弄得一团乱,最后不得不在数据库的表里改数据。推荐使用接口直接调用来造数据,操作简单,较安全。
代码造数据,就要做下面的分析。
用户表和地址表有对应关系,可通过下面代码查,地址表中的MemberID就是用户ID
1@Override2public int add(UmsMemberReceiveAddress address) {3 UmsMember currentMember = memberService.getCurrentMember();4 address.setMemberId(currentMember.getId());5 return addressMapper.insert(address);6}
造用户数据,就是实现注册流程。
先分析用户注册的代码,直接把其中的注册代码部分拿过来用。具体调用代码:
造数据需要关心注册流程?若我们是调接口造数据,就不需要;但若写代码开启多线程造数据,就得了解接口之间的调用关系。
截出中间一部分,分析它们的调用关系:
注册用户表中的密码都是加密的,可通过注册用户实现类代码:
1@Override2public void register(String username, String password, String telephone, String authCode) {3...............................4 //获取默认会员等级并设置5 UmsMemberLevelExample levelExample = new UmsMemberLevelExample();6 levelExample.createCriteria().andDefaultStatusEqualTo(1) ;7 List<UmsMemberLevel> memberLevelList = memberLevelMapper.selectByExample(levelExample);8 if (!CollectionUtils.isEmpty(memberLevelList)) {9 umsMember.setMemberLevelId(memberLevelList.get(0).getId());10 }11 //插入用户12 memberMapper.insert(umsMember);13 umsMember.setPassword(null);14}
就可直接写一段代码来造用户数据:《造用户代码.java》
有用户数据,还要下单用户的地址等详细信息,才能完成下单。
- 用户地址
根据用户地址资源路径找到Controller层,查看用户地址的代码调用关系,如下:
然后找到用户地址的关键代码:
1@Override2public int add(UmsMemberReceiveAddress address) {3 UmsMember currentMember = memberService.getCurrentMember();4 address.setMemberId(currentMember.getId());5 //插入地址6 return addressMapper.insert(address);7}
从这段代码中,我们可以观察到这几个信息:
- 调用地址接口需要用户登陆态,通过登陆态来解析用户ID号;
- 用户ID号是地址代码中的MemberID号;
- 用户ID号是自增加。
具体参考请见《造用户地址代码.java》。
通过上面代码,再开启Java线程池,就可把基础数据快速造完。造用户地址数据的时间记录:
最后造出:
表中的订单数据会在做基准场景时补充上去。等这些数据量都有了,我们在容量场景中就有了足够的铺底数据。
4 总结
性能场景中的数据到底应该做成什么样子。
造数据方法很多,不用拘泥于某种造数据手段,只要能快速造足够数据量就好。
RESAR性能工程中,性能场景需要两类数据:铺底数据和参数化数据。铺底数据需满足:
- 一定要造出符合生产量级的数据量;
- 数据量要真实模拟出生产的数据分布;
- 数据要真实可用。
参数化数据需要满足这两个条件:
- 参数化数据量要足够;
- 要符合真实用户的输入数据。
有这些知识,就不会造数据时混乱。
5 FAQ
- 为何要造符合生产量级的数据量?
- 为什么参数化时要用符合真实用户的输入数据?
通常我们执行性能脚本时不会执行一次,若按上面30min 100TPS的场景,对不可重复使用的数据,若我们每次执行前做一次数据,太不易用啦,但不造数据还不行,如何解决这样的数据问题?做数据库备份回滚。
静态铺地数据指图片、视频这些吗?是的。
参数化数据是否影响压测机的性能,看压测机压力就可以了吧?是的。不过要分析了参数化数据量的具体影响才能判断。
若压测环境跟生产环境硬件配置不对等,那数据量是否也等比缩放?也要缩放,不过不一定等比。要做基准测试才知道。
参数化直接从DB取,连接DB,查询数据需消耗时间,且占用数据库资源,这种参数化方式很明显对压测的性能有影响,为何推荐这种方式?
在参数化数据多的时候,建议用缓存或数据库中直接取数据的方式。但是你可以不用被测系统的缓存或数据库,独立搭建一个专门存参数化数据的缓存或数据库即可。
jemeter参数化文件一般超过多少条数据量会使jmeter成为瓶颈?没有定论。还是得看一下参数化的是什么。
造出生产量级数,确保应用,数据库,缓存在生产压力下运行;参数化用户输入数据,保障了数据多样性,造数很容易造成数据同化严重,热点集中,不能再现真实,400TPS跑半个小时,是否应该是72万数据量?
若数据在每个用户使用时是唯一的,那就应该是72万的数据量。
“对于唯一性数据(比如用户数据)来说,我们需要使用多少参数化数据是非常容易计算的。比如一个运行半小时的场景,TPS 如果是 100 的话,那就需要 18 万的数据量”要模拟每一个压测请求都是不同的用户发起的,这里需要构造18万个不同用户。
铺底数据就是已存在的数据,参数化数据就是压力测试发起请求时产生的新数据?
参数化数据有两种。一种是用户输入的但数据在铺底数据中没有;还有一种是铺底数据中必须有的数据,比如说用户名之类的。
真实环境下单都是要真正的支付钱的,这个怎么解决
如果对方提供测试系统就可以连。如果不提供,那只能mock了
尽量模拟真实用户效果,但像我们测试web一般都是忽略了css,js等直接测试主接口,这是否合理?
对后端的压力来说,是合理的。若从用户角度,显然不合理。 所以要做相应换算。