ClickHouse与Elasticsearch压测实践
1 需求分析
1.1 分析压测对象
1)什么是ClickHouse 和Elasticsearch
ClickHouse 是一个真正的列式数据库管理系统(DBMS)。在 ClickHouse 中,数据始终是按列存储的,包括矢量(向量或列块)执行的过程。只要有可能,操作都是基于矢量进行分派的,而不是单个的值,这被称为«矢量化查询执行»,它有利于降低实际的数据处理开销。
Elasticsearch是一个开源的分布式、RESTful 风格的搜索和数据分析引擎,它的底层是开源库Apache Lucene。 它可以被这样准确地形容:
- 一个分布式的实时文档存储,每个字段可以被索引与搜索
 - 一个分布式实时分析搜索引擎
 - 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
 
2)为什么要对他们进行压测
众所周知,ClickHouse在基本场景表现非常优秀,性能优于ES,但是我们实际的业务查询中有很多是复杂的业务查询场景,甚至是大数量的查询,所以为了在双十一业务峰值来到前,确保大促活动峰值业务稳定性,针对ClickHouse 和Elasticsearch在我们实际业务场景中是否拥有优秀的抗压能力,通过这次性能压测,探测系统中的性能瓶颈点,进行针对性优化,从而提升系统性能。
1.2 制定压测目标

为什么会选择这个(queryOBBacklogData)接口呢?
1)从复杂度来看,接口(queryOBBacklogData)查询了5次,代码如下:
/**
* 切ck-queryOBBacklogData
* @param queryBO
* @return
*/
public OutboundBacklogRespBO queryOBBacklogDataCKNew(OutboundBacklogQueryBO queryBO) {
log.info(" queryOBBacklogDataCK入参:{}", JSON.toJSONString(queryBO));
// 公共条件-卡最近十天时间
String commonStartTime = DateUtils.getTime(DateUtil.format(new Date(), DateUtil.FORMAT_DATE), DateUtils.ELEVEN_AM, 1, -10);
String commonEndTime = DateUtils.getTime(DateUtil.format(new Date(), DateUtil.FORMAT_DATE), DateUtils.ELEVEN_AM, 1, 1);
// 越库信息-待越库件数&待越库任务数
WmsObCrossDockQueryBo wmsObCrossDockQueryBo = wmsObCrossDockQueryBoBuilder(queryBO,commonStartTime, commonEndTime);
log.info("queryOBBacklogDataCK-wmsObCrossDockQueryBo: {}", JSON.toJSONString(wmsObCrossDockQueryBo));
CompletableFuture<OutboundBacklogRespBO> preCrossDockInfoCF = CompletableFuture.supplyAsync(
() -> wmsObCrossDockMapper.preCrossDockInfo(wmsObCrossDockQueryBo), executor);
// 集合任务信息-待分配订单
WmsObAssignOrderQueryBo wmsObAssignOrderQueryBo = wmsObAssignOrderQueryBoBuilder(queryBO, commonStartTime, commonEndTime);
log.info("queryOBBacklogDataCK-wmsObAssignOrderQueryBo: {}", JSON.toJSONString(wmsObAssignOrderQueryBo));
CompletableFuture<Integer> preAssignOrderQtyCF = CompletableFuture.supplyAsync(
() -> wmsObAssignOrderMapper.preAssignOrderInfo(wmsObAssignOrderQueryBo), executor);
// 拣货信息-待拣货件数&待拣货任务数
WmsPickTaskQueryBo wmsPickTaskQueryBo = wmsPickTaskQueryBoBuilder(queryBO, commonStartTime, commonEndTime);
log.info("queryOBBacklogDataCK-wmsPickTaskQueryBo: {}", JSON.toJSONString(wmsPickTaskQueryBo));
CompletableFuture<OutboundBacklogRespBO> prePickingInfoCF = CompletableFuture.supplyAsync(
() -> wmsPickTaskMapper.pickTaskInfo(wmsPickTaskQueryBo), executor);
// 分播信息-待分播件数&待分播任务
WmsCheckTaskDetailQueryBo wmsCheckTaskDetailQueryBo = wmsCheckTaskDetailQueryBoBuilder(queryBO, commonStartTime, commonEndTime);
log.info("queryOBBacklogDataCK-wmsCheckTaskDetailQueryBo: {}", JSON.toJSONString(wmsCheckTaskDetailQueryBo));
CompletableFuture<OutboundBacklogRespBO> preSowInfoCF = CompletableFuture.supplyAsync(
() -> wmsCheckTaskDetailMapper.checkTaskDetailInfo(wmsCheckTaskDetailQueryBo), executor);
// 发货信息-待发货件数
WmsOrderSkuQueryBo wmsOrderSkuQueryBo = wmsOrderSkuQueryBoBuilder(queryBO, commonStartTime, commonEndTime);
log.info("queryOBBacklogDataCK-wmsOrderSkuQueryBo: {}", JSON.toJSONString(wmsOrderSkuQueryBo));
CompletableFuture<Integer> preDispatchCF = CompletableFuture.supplyAsync(
() -> wmsOrderSkuMapper.preDispatchInfo(wmsOrderSkuQueryBo), executor);
return processResult(preCrossDockInfoCF, preAssignOrderQtyCF, prePickingInfoCF, preSowInfoCF, preDispatchCF);
}
2)接口(queryOBBacklogData),总共查询了5个表,如下:
wms.wms_ob_cross_dock
wms.wms_ob_assign_order
wms.wms_picking_task.
wms.wms_check_task_detail
wms.wms_order_sku
3)查询的数据量,如下:
select
(ifnull(sum(m.shouldBeCrossedDockQty),
0) -
ifnull(sum(m.satisfiedCrossedDockQty),
0)) as preCrossStockSkuQty,
count(m.docId) as preCrossStockTaskQty
from
wms.wms_ob_cross_dock m final
prewhere
m.createTime >= '2021-12-03 11:00:00'
and m.createTime <= '2021-12-14 11:00:00'
and m.warehouseNo = '279_1'
and m.orderType = '10'
and tenantCode = 'TC90230202'
where
m.deleted = 0
and m.deliveryDestination = '2'
and m.shipmentOrderDeleted = 0
and m.status = 0

从上面SQL截图可以看出,查询待越库件数&待越库任务数,共读了720817行数据
select count(distinct m.orderNo) as preAssignedOrderQty
from wms.wms_ob_assign_order m final
prewhere
m.createTime >= '2021-12-03 11:00:00'
and m.createTime <= '2021-12-14 11:00:00'
and m.warehouseNo = '361_0'
and tenantCode = 'TC90230202'
where m.taskassignStatus = 0
and m.deliveryDestination = 2
and m.stopProductionFlag = 0
and m.deleted = 0
and m.orderType = 10

从上面SQL截图可以看出,查询集合任务信息-待分配订单,共读了153118行数据
select minus(toInt32(ifnull(sum(m.locateQty), toDecimal64(0, 4))),
toInt32(ifnull(sum(m.pickedQty), toDecimal64(0, 4)))) as prePickingSkuQty,
count(distinct m.taskNo) as prePickingTaskQty
from wms.wms_picking_task m final
prewhere
m.shipmentOrderCreateTime >= '2021-12-03 11:00:00'
and m.shipmentOrderCreateTime <= '2021-12-14 11:00:00'
and m.warehouseNo = '286_1'
and tenantCode = 'TC90230202'
where m.pickingTaskDeleted = 0
and m.deliveryDestination = 2
and m.pickLocalDetailDeleted = 0
and m.shipmentOrderDeleted = 0
and m.orderType = 10
and (m.operateStatus = 0 or m.operateStatus = 1)

从上面SQL截图可以看出,查询拣货信息-待拣货件数&待拣货任务数,共读了2673536行数据
select minus(toInt32(ifnull(sum(m.locateQty), toDecimal64(0, 4))),
toInt32(ifnull(sum(m.pickedQty), toDecimal64(0, 4)))) as prePickingSkuQty,
count(distinct m.taskNo) as prePickingTaskQty
from wms.wms_picking_task m final
prewhere
m.shipmentOrderCreateTime >= '2021-12-03 11:00:00'
and m.shipmentOrderCreateTime <= '2021-12-14 11:00:00'
and m.warehouseNo = '279_1'
and tenantCode = 'TC90230202'
where m.pickingTaskDeleted = 0
and m.deliveryDestination = 2
and m.pickLocalDetailDeleted = 0
and m.shipmentOrderDeleted = 0
and m.orderType = 10
and (m.operateStatus = 0 or m.operateStatus = 1)

从上面SQL截图可以看出,查询分播信息-待分播件数&待分播任务,共读了1448149行数据
select ifnull(sum(m.unTrackQty), 0) as unTrackQty
from wms.wms_order_sku m final
prewhere
m.shipmentOrderCreateTime >= '2021-12-03 11:00:00'
and m.shipmentOrderCreateTime <= '2021-12-14 11:00:00'
and m.warehouseNo = '280_1'
and m.orderType = '10'
and m.deliveryDestination = '2'
and tenantCode = 'TC90230202'
where m.shipmentOrderDeleted <> '1'
and m.ckDeliveryTaskDeleted <> '1'
and m.ckDeliveryTaskDetailDeleted <> '1'
and m.ckDeliveryTaskStatus in ('1','0','2')

从上面SQL截图可以看出,查询发货信息-待发货件数,共读了99591行数据
2 测试环境准备
为了尽可能发挥性能压测作用,性能压测环境应当尽可能同线上环境一致,所以我们使用了和线上一样的环境
3 采集工具准备
监控工具
- http://origin.jd.com/ :监控JVM,方法级别监控(提供秒级支持)
 - http://console.jex.jd.com/ :提供异常堆栈监控,火焰图监控、线程堆栈分析
 - http://x.devops.jdcloud.com/ :支持查看clickhouse/Elasticsearch数据库服务每个节点的cpu使用率
 - http://dashboard.fireeye.jdl.cn/ :监测应用服务器cpu使用率、内存使用率
 
4 压测执行及结果分析
4.1 编写压测脚本工具
Forcebot(http://forcebot.jd.com) 是专门为开发人员、测试人员提供的性能测试平台,通过编写脚本、配置监控、设置场景、启动任务、实时监控、日志定位、导出报告一系列操作流程来完成性能测试,灵活的脚本配置满足同步、异步、集合点等多种发压模式。
帮助文档(http://doc.jd.com/forcebot/helper/)
4.2 设计压测数据
4.2.1 前期压测中名词解释
- DBCP:数据库连接池,是 apache 上的一个Java连接池项目。DBCP通过连接池预先同数据库建立一些连接放在内存中(即连接池中),应用程序需要建立数据库连接时直接到从接池中申请一个连接使用,用完后由连接池回收该连接,从而达到连接复用,减少资源消耗的目的。
 - maxTotal:是连接池中总连接的最大数量,默认值8
 - max_thread:clickhouse中底层配置,处理SQL请求时使用的最大线程数。默认值是clickhouse服务器的核心数量。
 - coordinating:协调节点数,主要作用于请求转发,请求响应处理等轻量级操作
 - 数据节点:主要是存储索引数据的节点,主要对文档进行增删改查操作,聚合操作等。数据节点对cpu,内存,io要求较高, 在优化的时候需要监控数据节点的状态,当资源不够的时候,需要在集群中添加新的节点
 
4.2.2 压测数据
clickhouse数据服务:32C128G6节点2副本
应用服务器:4核8G2
maxTotal=16
注:每次压测前,一定要观察每个数据节点的cpu使用率

注:从上面压测过程中,序号6-12可以看出,并发用户数在增加,但tps没有幅度变化,检查发现bigdata dbcp数据库链接池最大线程数未配置,默认最大线程数是8,并发用户数增加至8以后,clickhouse cpu稳定在40%~50%之间不再增加,应用服务器CPU稳定在25%左右。
之后我们调整maxTotal=50,通过调整max_thread不同的值,数据库节点CPU使用率保持在50%左右,来查看相应的监测数据指标:应用服务CPU使用率、TPS、TP99、并发用户数。

clickhouse数据节点,CPU使用率:

Elasticsearch数据服务:32C128G6节点2副本
应用服务器:4核8G2
Elasticsearch同样保持数据库服务CPU使用率达到(50%左右),再监控数据指标tps、tp99
调整指标如下:coordinating协调节点数、 数据节点、poolSize
指标1:coordinating=2,数据节点=4,poolSize=400

注:在压测的过程中发现,coordinating节点的cpu使用率达到51.69%,负载均衡的作用受限,所以协调节点需扩容2个节点
指标2:coordinating=4,数据节点=5,poolSize=800

注:在压测的过程中,发现CPU使用率(数据库)ES数据节点在40%左右的时候,一直上不去,查看日志发现activeCount已经达到797,需要增加poolSize值
指标3:coordinating=4,数据节点=5,poolSize=1200

注:压测过程中,发现coordinating协调节点还是需要扩容,不能支持现在数据节点cpu使用率达到50%
Elasticsearch数据节点及协调节点,CPU使用率:

我们在压测的过程中发现一些之前在开发过程中没发现的问题,首先bdcp数bigdata应用服务器,使用的线程池最大线程数为8时,成为瓶颈,用户数增加至8以后, clickhouse的cpu稳定在40%~50%之间不在增加,应用服务器CPU稳定在25%左右,其次warehouse集群协调节点配置低,协调节点CPU使用率高,最后是clickhouse-jdbc JavaCC解析sql效率低。
4.3 结果分析
4.3.1 测试结论
1)clickhouse对并发有一定的支持,并不是不支持高并发,可以通过调整max_thread提高并发
- max_thread=32时,支持最大TPS 是37,相应TP99是122
 - max_thread=2时,支持最大TPS 是66,相应TP99是155
 - max_thread=1时,支持最大TPS 是86,相应TP99是206
 
2)在并发方面Elasticsearch比clickhouse支持的更好,但是相应的响应速度慢很多
- Elasticsearch:对应的TPS是192,TP99是3050
 - clickhouse:对应的TPS 是86,TP99是206
 
综合考量,我们认为clickhouse足以支撑我们的业务诉求
4.3.2 优化建议
- 对ES协调节点进行扩容
 - bigdata应用线程池最大线程数调高至200
 - bigdata应用 dbcp线程池maxTotal设置成50
 - 读取配置文件工具类增加内存缓存
 
作者:潘雪艳
ClickHouse与Elasticsearch压测实践的更多相关文章
- jmeter压测实践
		
技巧一:命令行执行 命令执行:指定参数,报告的存储位置 jmeter -n -t baidu_requests_results.jmx -r -l baidu_requests_results.jtl ...
 - 在Mac上利用压测工具Jmeter-Suite进行一次压测实践的保姆级详细步骤(参考腾讯云文章)
		
参考的文章 压测工具Jmeter-Suite详细操作步骤 写此文的目的 由于我是刚开始接触kubernetes和jmeter,所以在学习过程中遇到了很多很多问题,同时我很烦恼为什么网上没有文章是从真正 ...
 - JMeter接口压测和性能监测
		
JMeter接口压力测试总结 一.安装JMeter 1. 在客户端机器上安装JMeter压测工具,我这里安装的版本是apache-jmeter-5.2.1,由于JMeter是JAVA语言开发的 ...
 - 高德全链路压测平台TestPG的架构与实践
		
导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...
 - ESRally压测ElasticSearch性能 CentOS 7.5 安装 Python3.7
		
1,CentOS 7.5 安装 Python3.7 1.安装开发者工具 yum -y groupinstall "Development Tools"2.安装Python编译依赖包 ...
 - 软件性能测试分析与调优实践之路-JMeter对RPC服务的性能压测分析与调优-手稿节选
		
一.JMeter 如何通过自定义Sample来压测RPC服务 RPC(Remote Procedure Call)俗称远程过程调用,是常用的一种高效的服务调用方式,也是性能压测时经常遇到的一种服务调用 ...
 - elasticsearch系列(二) esrally压测
		
环境准备 linux centOS(工作环境) python3.4及以上 pip3 JDK8 git1.9及以上 gradle2.13级以上 准备过程中的坑 这些环境准备没什么太大问题,都是wget下 ...
 - SwingBench 字符模式压测最佳实践
		
之前写过<使用SwingBench 对Oracle RAC DB性能 压力测试>,使用的是最基础直观的图形模式,已经可以满足大多数需求. 但是在有些场景下,图形模式可能本身消耗资源过大,尤 ...
 - 全链路压测平台(Quake)在美团中的实践
		
背景 在美团的价值观中,以“客户为中心”被放在一个非常重要的位置,所以我们对服务出现故障越来越不能容忍.特别是目前公司业务正在高速增长阶段,每一次故障对公司来说都是一笔非常不小的损失.而整个IT基础设 ...
 
随机推荐
- go-zero微服务实战系列(三、API定义和表结构设计)
			
前两篇文章分别介绍了本系列文章的背景以及根据业务职能对商城系统做了服务的拆分,其中每个服务又可分为如下三类: api服务 - BFF层,对外提供HTTP接口 rpc服务 - 内部依赖的微服务,实现单一 ...
 - 2.2.1 用户态、内核态的形成 -《zobolの操作系统学习札记》
			
内核态的出现,让计算机系统的权力向操作系统高度集中了. 操作系统分出内核态和用户态,就是为了进行不同等级的权限管理, 从而更好的适应多用户多任务并发的工作环境. 用户态和内核态的来源 在早期的单进程单 ...
 - 使用html2canvas,由html转换canvas时,出现图片丢失问题解决方案
			
在img标签上加上crossorigin="anonymous":如果是图片地址是跨域网址,请将图片转换为base64格式: 源码如下: <!DOCTYPE html> ...
 - rhel安装vmtools
			
第一步,vmware登录虚拟机,菜单栏找到"虚拟机"--"安装TOOLS" //如果打开虚拟机的光驱后没有文件.那么重复以上操作. 第二步,拷贝压缩文件到桌面: ...
 - js烧脑面试题大赏
			
本文精选了20多道具有一定迷惑性的js题,主要考察的是类型判断.作用域.this指向.原型.事件循环等知识点,每道题都配有笔者详细傻瓜式的解析,偏向于初学者,大佬请随意. 第1题 let a = 1 ...
 - Mysql事物锁等待超时(Lock wait timeout exceeded; try restarting transaction)
			
一.问题描述 在做查询语句时,MySQL 抛出了这样的异常:锁等待超时 Lock wait timeout exceeded; try restarting transaction,是当前事务在等待其 ...
 - python采集A站m3u8视频格式视频
			
基本开发环境 (https://jq.qq.com/?_wv=1027&k=NofUEYzs) Python 3.6 Pycharm 相关模块的使用 (https://jq.qq.com/?_ ...
 - # vue3 ref 和 reactive 函数
			
vue3 ref 和 reactive 函数 前言 上一篇博文介绍 setup 函数的时候,最后出现一个问题,就是在 setup 函数中,编写一个事件,直接去修改定义的变量,发现页面上没有更新成功,并 ...
 - 面试突击65:为什么要用HTTPS?它有什么优点?
			
说到 HTTPS 相信大部分人都是不陌生,因为目前我们使用的绝大数网站都是基于 HTTPS 的,比如以下这些: 那么问题来了,他们为什么要使用 HTTPS 呢?HTTPS 有哪些过人之处呢? 1.HT ...
 - java geteway 手机返回数据
			
import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.c ...