需求
表数量级在千万级以上,该表会被程序每隔 30 秒查询符合特定条件的数据一次,并且如果符合特定条件会被执行修改,被执行修改的数据是多条数据,不是单条;该表也会承担查询所有的需求,如后台管理;该表数据也会出现不定期的批量增加操作,暂可定为 1 分钟执行一次插入。 现做一次测试,检测一下此场景下两种方案的执行效率。 方案一:一表。即所有数据都保存到一个表,所有操作都操作此表 优点:读写都只操作一个表 缺点:表数据量大,对于频繁的查询操作会降低效率 方案二:两个表:一个表表 A 保存所有数据,一个表表 B 只保存热数据。频繁的查询,修改,插入操作都访问表 B ,只有后台管理的查询查询表 A ,表 B 的数据,检测到数据已是归档数据时执行删除操作。 优点:频繁查询的表数据量小,提高了查询速度 缺点:1、每次插入、修改数据需要操作两个表,增加了写的压力 2、增加了删除操作步骤,也会影响数据库效率测试方案
编写三个接口分别模拟新增,查询,修改。其中新增接口每隔 1 分钟调用一次,查询接口每隔 30 秒执行一次,修改接口每隔 5 分钟执行一次。表字段 status 代表状态,目前设置两个状态, 0 和 1,1 表示结束状态, 0 表示开始状态,一条数据初始状态为 0 。插入操作使用多线程 ( 模拟多用户投注 ) ,查询和新增均为单线程。修改接口查询所有状态为 0 的数据,选择修改状态的数,通过三种方式选择每次择其一,第一种方式 id 是 2 的倍数的,第二种方式 id 是 3 的倍数的,第三种方式 id 是质数的,先选择筛选数据的方式,然后再通过方式进行筛选符合的数据,然后进行状态的修改。 硬件准备: 数据库服务器: Os: Centos 7.1 64 位 CPU : 2 核 系统盘: 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 ){ //更新id是2的倍数的数据 if(id%2==0){ updateIds.add(id); } }else if(result == 1){ //更新id是3的倍数的数据 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 ){ //更新id是2的倍数的数据 if(id%2==0){ updateIds.add(id); } }else if(result == 1){ //更新id是3的倍数的数据 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万,则方案二。
这个表的特点是读多写多。