0
点赞
收藏
分享

微信扫一扫

性能场景数据必须如此设计,才能让你的系统跑得更快!

性能项目中,性能数据是重要的输入资源。但有人用极少的数据,来做较大压力,显然不符合真实场景,虽然拿到的结果好看,但无价值。

性能场景中的数据到底应该做成啥样?RESAR性能工程中,场景里使用的数据要满足:

  • 符合真实环境中的数据分布
    才能模拟出相应的IO操作
  • 符合真实用户输入的数据
    以真正模拟真实环境中的用户操作

分别对应:铺底数据和参数化数据。

1 铺底数据

线上系统架构中,系统常用到的数据分为:

  • 静态数据(红点)
  • 动态数据(绿点)

这也是性能场景中要存入的铺底数据:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据

若无铺底数据,相当于一个空系统。但生产环境中,这系统肯定不会空,所以要有足够数据。若数据不真实,就无法模拟生产上有真实数据的场景,如应用的内存占用、数据库IO能力、网络吞吐能力。

静态数据,易出现的问题是:

  • 一想到占网络带宽大,就觉得要用CDN
  • 或觉得不模拟静态数据,就不符合真实场景,不支持我们的优化结果

其实数据放在哪,怎么做合理,怎么做成本低,都要综合考虑,不是随大流:

  • 有的官方门户网站没啥流量,技术规划时非把几个图片放CDN,以显示架构多先进
  • 一些企业认为网站上的图片很重要,不懂技术又寻找安全感,非把图片都放到自己的服务器。本来图片就很大,一张有3~4M,用户一访问,自然就慢

这两种极端都不可取。外行指使内行干活,基本没好结果,因为有些外行觉得只要压力发起就可,细节上根本不在乎结果。处理这样问题的最合理方式:

  • 先分析业务逻辑
  • 再判断技术架构实现

1.1 静态数据

通常有两个可存放地方:

  • 服务端的Web层
  • CDN

大系统的流量大,网络带宽就得多。这种情况下,数据必然要放CDN。如果不使用 CDN 技术,数据需要从一个中心服务器传输到用户的设备上,会导致:

  1. 延迟高:由于数据需要从远程服务器传输,因此会增加访问的延迟,降低用户体验。
  2. 网络拥塞:如果所有用户都从同一个服务器上请求数据,会导致网络拥塞,降低整个系统的响应能力。
  3. 安全性问题:如果所有用户都从同一个服务器上请求数据,这也意味着所有用户的数据都存储在同一个地方,一旦服务器遭受attack或数据泄漏,用户的数据就会受到威胁。

因此,使用 CDN 技术可以将数据存储在距离用户更近的服务器上,可以提高访问速度和响应能力,减少延迟和网络拥塞的问题,并且可以提高系统的安全性。

对一些小的业务系统,由于用户不多,整体网络流量要求也少,可将静态数据直接放到负载均衡服务器(如Nginx)或应用服务器。用户访问一次之后,后续访问直接走本地缓存,对系统压力也不会产生多大影响。

1.2 动态数据

需分析,因为有些动态数据可放CDN。

  • 不用任何预热加载时,这些动态数据都存DB
  • 使用预热加载时,这些数据就会转到缓存(也取决于架构设计和代码实现),变成:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_02

按这样逻辑,真实场景中业务操作的数据量实际有多少,我们就要模拟多少,不然会出现一些问题。当模拟数据量与实际数据量差别较大,会对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';

因为表数据量不同:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_03

表操作细节。第一个表(用户量1000):

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_04

第二个表(用户量100w):

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_05

只需对比“executing”行就能看到明显差距,它告诉我们当执行语句时,需要的CPU时间明显因为数据量增加而增加。所以,若无足够铺底数据放在性能场景,一开始便注定悲剧。

② 缓存的区别

数据量的多少在缓存中有明显区别:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_06

场景中用的数据量越多,缓存必然要求越大。

③ 压力工具使用的区别

压力工具中使用的数据多少,不仅影响着压力工具本身需要的内存,同时也影响着性能场景的执行结果。

④ 网络的区别

不同的数据量,不管是走缓存,还是数据库,对客户端和服务器之间的网络消耗都是差不多的。只要不是缓存在客户那边,都是要走到服务器里转一圈的。

所以我们认为,数据量是多还是少,对客户端和服务器之间的网络的压力没有什么区别。如果你用的是CDN,那可以做另外的考虑。

⑤ 应用的区别

如果不是在应用中直接缓存数据,我们也认为对应用没什么区别,反正不管是什么样的请求过来,都是要到缓存或数据库中去取数据的,应用的Self Time不会有什么差别,方法依旧要执行。但是,如果你的应用是直接在应用的缓存中存数据的,那就有区别了,同样也是数据量越大,对内存的要求就越大。

基于这几点,可以看到有两个较重要环节:数据库和缓存,这是直接影响。

间接影响,比如数据在数据库中执行得慢了,在同步调用的应用中必然需要更多的应用线程来处理。

假设有一个100 TPS的系统,先忽略其他时间,只看数据库时间。如果数据库执行需要10ms,那应用只需要一个线程就能处理完了。如果数据库需要100ms,而我们仍想达100TPS,应用就得有10个线程同时处理。

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_07

整个链路上的所有线程、队列、超时等都会因为受到数据量的影响而产生大的变化。

要想模拟出生产时候的样子,铺底数据不能含糊。

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组件

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_08

这俩都能用Redis做参数化的数据源。

2.3 连接MySQL做参数化

① 创建一个JDBC Connection Configuration。

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_09

配置好连接信息,如用户名密码:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_10

② 创建一个JDBC Request

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据库_11

用JDBC Request把数据取回来:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_12

③ 用${user_name}引用参数

完成上述三步,就实现了用DB的方式做参数化。

3 怎么造数据

  • 用户数据
  • 地址数据
  • 商品数据
  • 订单数据

数据量如何设置?

根据第4讲的性能方案,登录TPS如果是每秒150,并且如果按容量场景的需求,在场景连续递增时,大概在20分钟内(这是一个经验值,在具体的场景执行中也会有变化)会递增到最大值,然后再执行10分钟,也就是说总时间大概为30分钟。

但是因为场景是递增的,一开始我们并没有要求达到150TPS,同时登录场景TPS最大值能达到多少,我们现在也没法预知。根据经验来看的话,登录的TPS在当前的硬件环境中,就算是不走缓存,达到三、四百应该是没有多大问题的。

如果按最大400TPS来算,跑半个小时,需要的数据量就是54万,而我们造出来的用户量要远远大于这个量级。这里我们就先造200万的用户量,因为地址的数据量肯定大于用户的数据量,所以会多于200万。

先查当前DB中的数据量,再确定要造多少。

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据库_13

这数据量级明显不够。

怎么造出那么多数据量?造的数据主要分为:用户数据和订单数据。

  • 登录用户

先了解表结构,造出数据只有符合业务逻辑才能使用。看用户表结构和数据:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_14

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_15

地址表:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_16

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_17

造数据时,不要往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}

造用户数据,就是实现注册流程。

先分析用户注册的代码,直接把其中的注册代码部分拿过来用。具体调用代码:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_18

造数据需要关心注册流程?若我们是调接口造数据,就不需要;但若写代码开启多线程造数据,就得了解接口之间的调用关系。

截出中间一部分,分析它们的调用关系:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据_19

注册用户表中的密码都是加密的,可通过注册用户实现类代码:

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层,查看用户地址的代码调用关系,如下:

性能场景数据必须如此设计,才能让你的系统跑得更快!_数据库_20

然后找到用户地址的关键代码:

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线程池,就可把基础数据快速造完。造用户地址数据的时间记录:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_21

最后造出:

性能场景数据必须如此设计,才能让你的系统跑得更快!_参数化_22

表中的订单数据会在做基准场景时补充上去。等这些数据量都有了,我们在容量场景中就有了足够的铺底数据。

4 总结

性能场景中的数据到底应该做成什么样子。

造数据方法很多,不用拘泥于某种造数据手段,只要能快速造足够数据量就好。

RESAR性能工程中,性能场景需要两类数据:铺底数据和参数化数据。铺底数据需满足:

  • 一定要造出符合生产量级的数据量;
  • 数据量要真实模拟出生产的数据分布;
  • 数据要真实可用。

参数化数据需要满足这两个条件:

  • 参数化数据量要足够;
  • 要符合真实用户的输入数据。

有这些知识,就不会造数据时混乱。

5 FAQ

  1. 为何要造符合生产量级的数据量?
  2. 为什么参数化时要用符合真实用户的输入数据?

通常我们执行性能脚本时不会执行一次,若按上面30min 100TPS的场景,对不可重复使用的数据,若我们每次执行前做一次数据,太不易用啦,但不造数据还不行,如何解决这样的数据问题?做数据库备份回滚。

静态铺地数据指图片、视频这些吗?是的。

参数化数据是否影响压测机的性能,看压测机压力就可以了吧?是的。不过要分析了参数化数据量的具体影响才能判断。

若压测环境跟生产环境硬件配置不对等,那数据量是否也等比缩放?也要缩放,不过不一定等比。要做基准测试才知道。

参数化直接从DB取,连接DB,查询数据需消耗时间,且占用数据库资源,这种参数化方式很明显对压测的性能有影响,为何推荐这种方式?

在参数化数据多的时候,建议用缓存或数据库中直接取数据的方式。但是你可以不用被测系统的缓存或数据库,独立搭建一个专门存参数化数据的缓存或数据库即可。

jemeter参数化文件一般超过多少条数据量会使jmeter成为瓶颈?没有定论。还是得看一下参数化的是什么。

造出生产量级数,确保应用,数据库,缓存在生产压力下运行;参数化用户输入数据,保障了数据多样性,造数很容易造成数据同化严重,热点集中,不能再现真实,400TPS跑半个小时,是否应该是72万数据量?

若数据在每个用户使用时是唯一的,那就应该是72万的数据量。

“对于唯一性数据(比如用户数据)来说,我们需要使用多少参数化数据是非常容易计算的。比如一个运行半小时的场景,TPS 如果是 100 的话,那就需要 18 万的数据量”要模拟每一个压测请求都是不同的用户发起的,这里需要构造18万个不同用户。

铺底数据就是已存在的数据,参数化数据就是压力测试发起请求时产生的新数据?

参数化数据有两种。一种是用户输入的但数据在铺底数据中没有;还有一种是铺底数据中必须有的数据,比如说用户名之类的。

真实环境下单都是要真正的支付钱的,这个怎么解决

如果对方提供测试系统就可以连。如果不提供,那只能mock了

尽量模拟真实用户效果,但像我们测试web一般都是忽略了css,js等直接测试主接口,这是否合理?

对后端的压力来说,是合理的。若从用户角度,显然不合理。 所以要做相应换算。

举报

相关推荐

0 条评论