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

为什么会选择这个(queryOBBacklogData)接口呢?
/**
* 切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);
}
wms.wms_ob_cross_dock
wms.wms_ob_assign_order
wms.wms_picking_task.
wms.wms_check_task_detail
wms.wms_order_sku
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行数据
为了尽可能发挥性能压测作用,性能压测环境应当尽可能同线上环境一致,所以我们使用了和线上一样的环境
Forcebot(http://forcebot.jd.com) 是专门为开发人员、测试人员提供的性能测试平台,通过编写脚本、配置监控、设置场景、启动任务、实时监控、日志定位、导出报告一系列操作流程来完成性能测试,灵活的脚本配置满足同步、异步、集合点等多种发压模式。
帮助文档(http://doc.jd.com/forcebot/helper/)
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效率低。
1)clickhouse对并发有一定的支持,并不是不支持高并发,可以通过调整max_thread提高并发
2)在并发方面Elasticsearch比clickhouse支持的更好,但是相应的响应速度慢很多
综合考量,我们认为clickhouse足以支撑我们的业务诉求
作者:潘雪艳
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
我认为我的问题最好用一个例子来描述。假设我有一个名为“Thing”的简单模型,它有一些简单数据类型的属性。像...Thing-foo:string-goo:string-bar:int这并不难。数据库表将包含具有这三个属性的三列,我可以使用@thing.foo或@thing.bar之类的东西访问它们。但我要解决的问题是当“foo”或“goo”不再包含在简单数据类型中时会发生什么?假设foo和goo代表相同类型的对象。也就是说,它们都是“Whazit”的实例,只是数据不同。所以现在事情可能看起来像这样......Thing-bar:int但是现在有一个新的模型叫做“Whazit”,看起来
我有一个要在我的Rails3项目中使用的数组扩展方法。它应该住在哪里?我有一个应用程序/类,我最初把它放在(array_extensions.rb)中,在我的config/application.rb中我加载路径:config.autoload_paths+=%W(#{Rails.root}/应用程序/类)。但是,当我转到railsconsole时,未加载扩展。是否有一个预定义的位置可以放置我的Rails3扩展方法?或者,一种预先定义的方式来添加它们?我知道Rails有自己的数组扩展方法。我应该将我的添加到active_support/core_ext/array/conversion
参见下面的示例,我想最好使用第二种方法,但第一种也可以。哪种方法最好,使用另一种的后果是什么?classTestdefstartp"started"endtest=Test.newtest.startendclassTest2defstartp"started"endendtest2=Test2.newtest2.start 最佳答案 我肯定会说第二种变体更有意义。第一个不会导致错误,但对象实例化完全过时且毫无意义。外部变量在类的范围内不可见:var="string"classAvar=A.newendputsvar#=>strin
不知何故,我似乎无法获得包含我的聚合的响应...使用curl它按预期工作:HBZUMB01$curl-XPOST"http://localhost:9200/contents/_search"-d'{"size":0,"aggs":{"sport_count":{"value_count":{"field":"dwid"}}}}'我收到回复:{"took":4,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":90,"max_score":0.0,"hits":[]},"a
如果我构建了一个应用程序来访问来自Gmail、Twitter和Facebook的一些数据,并且我希望用户只需输入一次他们的身份验证信息,并且在几天或几周后重置,那会怎样是在Ruby中动态执行此操作的最佳方法吗?我看到很多人只是拥有他们客户/用户凭证的配置文件,如下所示:gmail_account:username:myClientpassword:myClientsPassword这看起来a)非常不安全,b)如果我想为成千上万的用户存储此类信息,它就无法工作。推荐的方法是什么?我希望能够在这些服务之上构建一个界面,因此每次用户进行交易时都必须输入凭据是不可行的。
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
我正在使用Devise在Rails应用程序中,并希望通过API公开一些模型数据,但应该像应用程序一样限制对API的访问。$curlhttp://myapp.com/api/v1/sales/7.json{"error":"Youneedtosigninorsignupbeforecontinuing."}很明显。在这种情况下是否有访问API的最佳实践?我更喜欢一步验证+获取数据,但这只是为了让客户的工作更轻松。他们将使用JQuery在客户端提取数据。感谢您提供任何信息!凡妮莎 最佳答案 我建议您按照以下帖子中的选项2:使用APIke
我正在开发一个Rails2.3.1网站。在整个网站中,我需要一个用于在各种页面(主页、创建帖子页面、帖子列表页面、评论列表页面等)上创建帖子的表单——只要说这个表单需要在由各种Controller)。这些页面中的每一个都显示在相应的Controller/操作中检索到的各种其他信息。例如,主页列出了最新的10篇文章、从数据库中提取的内容等。因此,我已将帖子创建表单移动到它自己的部分中,并将该部分包含在所有必要的页面中。请注意,部分POST中的表单到/questions(路由到PostsController::create——这是默认的Rails行为)。我遇到的问题是当Posts表单没有正