背景介绍:
最近我们在竞标一个项目,对方IT有几个压测要求:
1.系统用户量4亿+;
2.用户注册场景下TPS达到2000;
3.用户查询场景下TPS达到8000;
4.用户抢券秒杀场景下TPS达到8000;
5.数据库单表数据量均不能超过5000w;
6.上述所有接口响应时间不超过500ms;
拿到这个需求之后boss就找我,让我一个星期内把这个压测报告提供给对方,废话不多说,开始干活。
解决方法:
在介绍一下我的思路
1.拿到这个需求之后,我首先按照对方要求的单表不超过5000w,用户总量4亿+,决定使用集群+读写分离+分库分表的方式来达到要求。
a.为了提高系统的稳定性和响应速度,我们数据库采用集群模式,集群里面一主多从,数据库直接使用阿里云的云数据库PolarDB,结构如下图;
b. 为了系统的扩展性和尽量减少数据的迁移,我决定把表一次性拆分到位,最终决定拆分成8个数据库,每个库8张表,结构图如下,这样有个好处就是每张表实际只存储700w用户就可以;
2.我们的开发语言是java,使用的框架是springcloud,分库分表插件使用 sharding-jdbc(下方是sharding配置)
sharding:
jdbc:
datasource:
names: db0,db1,db2,db3,db4,db5,db6,db7
db0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db0?characterEncoding=utf-8
username: ***
password: ***
db1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db1?characterEncoding=utf-8
username: ***
password: ***
db2:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db2?characterEncoding=utf-8
username: ***
password: ***
db3:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db3?characterEncoding=utf-8
username: ***
password: ***
db4:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db4?characterEncoding=utf-8
username: remote
password: ***
db5:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db5?characterEncoding=utf-8
username: ***
password: ***
db6:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql:/***.rwlb.rds.aliyuncs.com:3306/db6?characterEncoding=utf-8
username: ***
password: ***
db7:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://***.rwlb.rds.aliyuncs.com:3306/db7?characterEncoding=utf-8
username: ***
password: ***
config:
sharding:
default-database-strategy:
inline:
sharding-column: id
algorithm-expression: db$->{id % 8}
tables:
member:
actual-data-nodes: db$->{0..7}.member_$->{0..7}
table-strategy:
inline:
sharding-column: age
algorithm-expression: member_$->{age % 8}
key-generator:
column: id
props:
sql:
show: true
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
测试准备:
1.向数据库中插入4亿条用户信息 (下方是多线程写入代码)
private Long save(int size,int batch){
Long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(50);
CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i=0;i<size;i++){
executor.execute(new Runnable() {
@Override
public void run() {
Member member = new Member();
member.setAge(RandomUtil.randomInt(0,100));
member.setName(RandomUtil.randomStringUpper(4));
member.setCreateTime(new Date());
try {
memberService.save(member);
}catch (Exception e){
e.printStackTrace();
}finally {
countDownLatch.countDown();
}
}
});
}
try {
countDownLatch.await();
executor.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
Long end = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
sb.append(batch+"开始时间:"+start);
sb.append(batch+"结束时间:"+end);
sb.append(batch+"总计条数:"+size);
sb.append(batch+"总计耗时:"+(end-start)+"ms");
System.out.println(sb.toString());
return end-start;
}
2.部署到服务器开始写入数据,等了几个小时终于写入完成(写入期间数据库QPS,和最终写入数据量约4.9亿)
3.写入期间我只用了2台服务器,所以这个应该不是数据库的极限。
开始写入测试:
1.使用阿里云的PTS配置测试场景,作为开发人员个人感觉这个很好用,就是有点费钱;
2.先测试写入场景,因为之前有数据写入,我知道数据写入应该是没有问题的,所以我这里就用了一台应用服务器
3.下面4张图分别是PTS的场景配置、施压配置、场景调试、和最终的测试报告
说明一下,这里的并发只有100,因为这个这个资源包里并发只有这么点,接下来测试查询的时候肯定需要重新购买资源包,这个场景会重新测试
场景配置
施压配置
场景调试 (用来测试你的场景,看返回结果是否是你想要的结果)
测试报告
4.通过测试发现单台应用服务器的TPS就可以达到2000,所以说写入场景现在没有问题了
开始查询测试:
查询用户的时候,我预计性能会不如之前的写入,因为按照分库分表的策略,写入计算好路由,写入是很快速的,最终一次写入只需要执行一条sql语句;
我们的会员查询接口是按照会员id查询的,所以确定了会员id就知道去那个数据库查找,但是按照目前的分表策略,是没有办法确定去那张表查找,只能全部查找,也就是按照id查询会员信息,要执行8条sql语句;
查询测试准备工作:
因为预估一台应用服务器没有办法达到要求,这里要做负载均衡,直接用户阿里云的ALB,下图是直接用10台服务器,在实际测试过程中是主键递增的,不是一次就用了10台;
接下来开始测试写入场景:
1.首先查找到500w个用户的id,导入到cvs里面。
2.配置测试场景,这里把之前cvs压缩传入数据源管理,就可以根据这些id来模拟实际的用户查询了。
3.配置压测具体参数,这次的并发直接设置为2000
4.调试场景,发现场景配置没有问题
5.开始测试, 下图是应用服务器有10台的测试报告
6.下图是逐渐增加应用服务器的时候数据库服务器的QPS,从图中应该可以看出,应用服务器增加了4次,从2台->4台->6台->10台。
总结
1.在分库分表之前要规划好分库分表策略,要根据业务和实际需求设置合理的策略,例如在本次测试中用户查询的性能之所以不如用户写入就是因为分表策略的设置,写入只需要执行一条SQL,但是查询就需要执行8条SQL;
2.使用负载均衡后性能并不是线性提升,按照预估应用服务器需要8台就可以达到要求,但是实际中使用了10台;
3.现在各种各样的云产品越来越多,可以在合适的场景灵活的使用云产品,例如在本次测试中我使用了 阿里云的 ECS服务器、PolarDB云数据库、负载均衡 SLB、性能测试PTS,使用这些云产品可以大幅度提高工作效率。如果在以往我们需要自己安装数据库,配置nginx,配置各种集群,搭建测试环境,测试完成之后还要写测试报告,现在直接使用即可,测试报告也自动生成,本来一个星期才能完成的事情,现在只需要2天就能完成了,剩余的3天可以再学点新的技术了;