一次性能测试

名词解释
热数据:状态还未到结束状态的数据。
归档数据:状态已是最终状态的数据。

需求

表数量级在千万级以上,该表会被程序每隔30秒查询符合特定条件的数据一次,并且如果符合特定条件会被执行修改,被执行修改的数据是多条数据,不是单条;该表也会承担查询所有的需求,如后台管理;该表数据也会出现不定期的批量增加操作,暂可定为1分钟执行一次插入。
现做一次测试,检测一下此场景下两种方案的执行效率。
方案一:一表。即所有数据都保存到一个表,所有操作都操作此表
优点:读写都只操作一个表
缺点:表数据量大,对于频繁的查询操作会降低效率

方案二:两个表:一个表表A保存所有数据,一个表表B只保存热数据。频繁的查询,修改,插入操作都访问表B,只有后台管理的查询查询表A,表B的数据,检测到数据已是归档数据时执行删除操作。
优点:频繁查询的表数据量小,提高了查询速度
缺点:1、每次插入、修改数据需要操作两个表,增加了写的压力 
      2、增加了删除操作步骤,也会影响数据库效率

测试方案

编写三个接口分别模拟新增,查询,修改。其中新增接口每隔1分钟调用一次,查询接口每隔30秒执行一次,修改接口每隔5分钟执行一次。表字段status代表状态,目前设置两个状态,01,1表示结束状态,0表示开始状态,一条数据初始状态为0。插入操作使用多线程(模拟多用户**),查询和新增均为单线程。修改接口查询所有状态为0的数据,选择修改状态的数,通过三种方式选择每次择其一,第一种方式id2的倍数的,第二种方式id3的倍数的,第三种方式id是质数的,先选择筛选数据的方式,然后再通过方式进行筛选符合的数据,然后进行状态的修改。
硬件准备:
数据库服务器:
Os:Centos 7.1 64
CPU2
系统盘:20G
数据盘:200G
带宽:5mbps
软件准备:
Mysql
Jmeter
应用程序:
语言:java
Jdk:1.7
表结构设计:
字段
说明
类型
长度
是否必填
primarykey
id
         
status
状态:0--开始 1--结束        
userId
用户ID long 20
issue
期次 varchar 8 是 
lotteryCode
彩种编码 varchar 8
price
价格 varchar 25
multiple
倍数 int 10

部分代码

1、方案一查询

@RequestMapping(value = "/info",method = RequestMethod.GET)
public String getBystatus(){
    List<Long> ids = ticketService.getTicketIdByStatus(0);
    logger.info(" ids = "+ids);
    return "manager";
}
2、方案二查询

@RequestMapping(value = "/info",method = RequestMethod.GET)
public String getByStatus(){
    List<Long> ids = tempService.getTicketIdByStatus(0);
    logger.info(" ids = "+ids);
    return "manager";
}

3、方案一新增

@RequestMapping(method = RequestMethod.POST)
public String add(){
    logger.info(" add start ");
    ticketService.add();
    return "manager";
}

4、方案二新增

@RequestMapping(method = RequestMethod.POST)
public String add(){
    logger.info(" temp & ticket add start ");
    tempService.add();
    ticketService.add();
    return "manager";
}

5、方案一修改

@RequestMapping(method = RequestMethod.PUT)
public String update(){
    List<Long> updateIds = new LinkedList<Long>();//只添加不查询,用链表效率较好
    List<Long> ids = ticketService.getTicketIdByStatus(0);
    Random random = new Random();
    int result = random.nextInt(3);
    logger.info(" random result = "+result);
    int i = 0;
    for(Long id : ids){
        if(result ==0 ){
            //更新id2的倍数的数据
            if(id%2==0){
                updateIds.add(id);
            }
        }else if(result == 1){
            //更新id3的倍数的数据
            if(id%3==0){
                updateIds.add(id);
            }

        }else if (result ==2){
            //更新查出来的前3条数据
            if(i<3){
                updateIds.add(id);
            }else break;
            i++;
        }
    }
    logger.info(" id = "+ids.size()+" updateIds size = "+updateIds.size());
    ticketService.updateList(updateIds);

    return "manager";
}

6、方案二修改

@RequestMapping(method = RequestMethod.PUT)
public String update(){
    logger.info(" temp delete ,ticket update start ");

    List<Long> updateIds = new LinkedList<Long>();//只添加不查询,用链表效率较好
    List<Long> ids = tempService.getTicketIdByStatus(0);
    Random random = new Random();
    int result = random.nextInt(3);
    logger.info(" random result = "+result);
    int i = 0;
    for(Long id : ids){
        if(result ==0 ){
            //更新id2的倍数的数据
            if(id%2==0){
                updateIds.add(id);
            }
        }else if(result == 1){
            //更新id3的倍数的数据
            if(id%3==0){
                updateIds.add(id);
            }

        }else if (result ==2){
            //更新查出来的前3条数据
            if(i<3){
                updateIds.add(id);
            }else break;
            i++;
        }
    }
    logger.info(" id = "+ids.size()+" updateIds size = "+updateIds.size());
    tempService.deleteList(updateIds);
    ticketService.updateList(updateIds);

    return "manager";
}

测试报告

1、8万条数据结果
方案一:
一次性能测试

方案二:
一次性能测试


方案二对比方案一没有太大优势。

2、50万条数据

方案一
一次性能测试

方案二:
一次性能测试


方案二依然没有太大优势,并且在新增和修改的接口,处理时间略高于方案一

3、230万条数据

方案一:

一次性能测试

方案二:

一次性能测试


综上,如果数据量较少的时候,可选方案一,如果数据量较大比如200万,则方案二。

这个表的特点是读多写多。