滴滴基于 Clickhouse 构建新一代日志存储系统

背景

此前,滴滴日志主要存储于 ES 中。然而,ES 的分词、倒排和正排等功能导致其写入吞吐量存在明显瓶颈。此外,ES 需要存储原始文本、倒排索引和正排索引,这增加了存储成本,并对内存有较高要求。随着滴滴数据量的不断增长,ES 的性能已无法满足当前需求。

在追求降低成本和提高效率的背景下,我们开始寻求新的存储解决方案。经过研究,我们决定采用 CK 作为滴滴内部日志的存储支持。据了解,京东、携程、B站等多家公司在业界的实践中也在尝试用 CK 构建日志存储系统。

挑战

面临的挑战主要来自下面三个方面:

  • 数据量大:每天会产生 PB 级别的日志数据,存储系统需要稳定地支撑 PB 级数据的实时写入和存储。
  • 查询场景多:在一个时间段内的等值查询、模糊查询及排序场景等,查询需要扫描的数据量较大且查询都需要在秒级返回。
  • QPS 高:在 PB 级的数据量下,对 Trace 查询同时要满足高 QPS 的要求。

为什么选 Clickhouse

  • 大数据量:CK 的分布式架构支持动态扩缩容,可支撑海量数据存储。
  • 写入性能:CK 的 MergeTree 表的写入速度在200MB/s,具有很高吞吐,写入基本没有瓶颈。
  • 查询性能:CK 支持分区索引和排序索引,具有很高的检索效率,单机每秒可扫描数百万行的数据。
  • 存储成本:CK 基于列式存储,数据压缩比很高,同时基于HDFS做冷热分离,能够进一步地降低存储成本。

架构升级

Read More

关于做好技术团队管理的几点心得

技术Leader核心工作是让团队拿更好的成绩,日常工作主要是制定方案、推进业务、掌控下属、协助领导等四项内容,本次分享主要分享最基础也是最重要的管理职责:

  • 调动团队同学的工作积极性
  • 让团队同学更高更快的成长

1、如何调动团队同学的工作积极性?

  • 精确的指示。需要更强调为什么做和这么做的意义,没有计划和目的的命令无法为下属提供工作的动力。
    • 举例:有个业务需要节省成本,需要要“查询性能提升2倍”。身为主管,自然不能直接告诉下属“查询性能提升2倍”。主管要做的是,考虑怎样才能使性能提升2倍,再用具体的语言指挥下属行动起来。
    • 工作边界要清晰
  • 了解下属的能力和意愿
    • 根据下属能力进行分工,整体分工需要张弛有度,有能提升硬实力的工作也有易产出有亮眼的工作
    • 针对个体而言,重点在于提升能力和个人意愿
    • 针对个体间而言,在于加强分工和协作,分工要有边界
    • 主管的行为一般有:授权型、参与型、说服型、命令型;根据下属不同性格要有不同的行为
  • 激情会传染。做主管的必须先激情四射,然后才能点燃下属们的激情
  • 要掌控业务全貌
  • 技术上能搞事情。能搞定别人解不了的事情,团队影响力越高,团队同学认同感和参与感越强;多思考技术和未来团队发展的事情
  • 提升下属对你的信任度
    • 有一技之长。技术leader某一块技术能力要做到团队最好,影响信服度
    • 能让对方感受到你是真实的帮助其成长,需要更多换位思考
    • 利用好团建和聚餐场合

Read More

Elasticsearch集群JVM coredump问题排查

前言

好几年前的文章了,之前排查问题,随手写的,但是发现其他团队人遇到类似问题没有思路,所以还是发出来,给大家一起解决问题的思路。

问题描述

ES集群磁盘报警,发现/home/coresave/ core文件导致根目录磁盘被打满,删除core文件恢复,已知这个集群新上线了jdk 17 zgc,排查下jvm为啥core。而jvm core一般有以下几个原因:资源超了(内存、线程数,vma数等),jvm bug(比如指令集)

排查过程

先去elasticsearch根目录查看core日志,即hs_err_pid_xxx.log,内容如下:
core日志文件
看core原因是因为资源不足(不一定是内存)导致的问题,jdk 17 zgc core后,fatal error 原因与g1 有明显不同,突然不知道怎么去排查了,研究下,思路如下。资源不足原因我们可以在hs_err.log里查看具体的原因,步骤如下:

1、先排查meminfo,看下机器内存情况

Read More

如何让ES低成本、高性能?滴滴落地ZSTD压缩算法的实践分享

前文分别介绍了滴滴自研的ES强一致性多活是如何实现的、以及如何提升ES的性能潜力。由于滴滴ES日志场景每天写入量在5PB-10PB量级,写入压力和业务成本压力大,为了提升ES的写入性能,我们让ES支持ZSTD压缩算法,本篇文章详细展开滴滴在落地ZSTD压缩算法上的思考和实践。

背景

ES通过索引(Index)对外提供数据检索能力,索引是用于组织和存储数据的逻辑单元。每个索引由若干个分片(shard)组成,每个分片就是一个Lucene索引,可以在不同的节点上进行分布式存储和并行处理,提高性能和可伸缩性。每个分片由一组段文件(segment)组成,段是分片中更小的存储和搜索单元,是一组物理文件,包含了检索需要的倒排索引(词项和文档ID的映射关系)和文档存储(字段值和其他元数据),如下图:
ES数据模型

Lucene作为ES的底层索引引擎,提供了灵活的数据检索能力,同时也导致CPU、存储占用较为严重。为实现降本增效,23年上半年,ES团队开启了Lucene压缩编码优化专项,通过改进存储层压缩算法,从而降低单位Document所占用的资源。本文概述了ES的底层索引文件,并介绍了Lucene存储压缩编码的优化。

Lucene索引文件介绍

ES的压缩编码优化专项涉及到Lucene底层的文件存储,Lucene索引由一组Segment构成,每个Segment包含了一系列文件,重点文件类型如下图:
Segment构成

Read More

解锁滴滴ES的性能潜力:JDK 17和ZGC的升级之路

前文介绍了滴滴自研的ES强一致性多活是如何实现的,其中也提到为了提升查询性能和解决查询毛刺问题,滴滴ES原地升级JDK17和ZGC,在这个过程中我们遇到了哪些问题,怎样解决的,以及最终上线效果如何,这篇文章就带大家深入了解。

背景

滴滴ES在2020年的时候由2.X升级到7.6.0,该版本是在官方7.6.0的基础上改造而来,支持的是JDK11,采用的垃圾回收器是G1。ES的业务主要分为两类,一类是日志场景,该场景写多读少,高峰期CPU使用率在85%左右,写入性能是它的主要瓶颈;另一类是非日志场景,例如POI检索、订单、支付,这些场景对查询耗时及查询稳定性都有着较高的要求。

随着ES业务数据量的增长,GC导致的查询稳定性压力剧增,已经逐渐无法满足业务需求。以下是ES面临的主要问题:

非日志场景的GC问题

1、Yong GC平均暂停时间长,一些集群的平均暂停时间达到百毫秒级别。

GC暂停时间长在订单集群这种112G大内存的进程中表现尤为明显。下图为订单集群的GC暂停时间,可以发现一次Yang GC的平均暂停时间就达到了200ms,有些甚至超过了1s。ES的核心业务POI、订单等对查询耗时的P99以及Max都有一定的需求,GC暂停导致的查询延时以及毛刺问题无法满足业务需求。

订单集群的GC暂停时间

Read More

探索ES高可用:滴滴自研跨数据中心复制技术详解

Elasticsearch 是一个基于Lucene构建的开源、分布式、RESTful接口的全文搜索引擎,其每个字段均可被索引,且能够横向扩展至数以百计的服务器存储以及处理TB级的数据,其可以在极短的时间内存储、搜索和分析大量的数据。

滴滴ES发展至今,承接了公司绝大部分端上检索和日志场景,包括地图POI检索、订单检索、客服、内搜及把脉ELK场景等。

近几年围绕稳定性、成本、效率和数据安全这几个方向持续探索:

  • 滴滴ES有很多在线P0级检索场景,为了提升集群稳定性,我们自研了跨数据中心复制能力,实现多机房数据写入强一致性,并配合管控平台让ES支持多活能力;
  • 为了提升查询性能和解决查询毛刺问题,我们在7.6版本上原地升级支持JDK 17;
  • ES日志场景每天写入量在5PB-10PB量级,写入压力和业务成本压力大,为了提升ES的写入性能,我们让ES支持ZSTD压缩算法;
  • 由于ES索引里包含很多敏感数据,我们又完善了ES的安全认证能力。

基于以上探索,我们总结了一定的经验,现分成4篇文章详细介绍。本篇文章介绍滴滴ES如何实现索引的跨数据中心复制从而保证索引的高可用。

滴滴跨数据中心复制能力 - Didi Cross Datacenter Replication,由滴滴自研,简称DCDR,它能够将数据从一个 Elasticsearch 集群原生复制到另一个 Elasticsearch 集群。如图所示,DCDR工作在索引模板或索引层面,采用主从索引设计模型,由Leader索引主动将数据push到Follower索引,从而保证了主从索引数据的强一致性。

DCDR跨数据中心复制能力图

Read More

Elasticsearch常用的命令

集群健康状态相关

查看集群健康状态

1
GET _cluster/health
number_of_pending_tasks:是指主节点创建索引并分配shards等任务,如果该指标数值一直未减小代表集群存在不稳定因素 。

查看未分配原因

1
GET _cluster/allocation/explain

有以下几种可能

  • INDEX_CREATED : Unassigned as a result of an API creation of an index. 索引创建 : 由于API创建索引而未分配的
  • CLUSTER_RECOVERED : Unassigned as a result of a full cluster recovery. 集群恢复 : 由于整个集群恢复而未分配
  • INDEX_REOPENED : Unassigned as a result of opening a closed index. 索引重新打开
  • DANGLING_INDEX_IMPORTED : Unassigned as a result of importing a dangling index. 导入危险的索引
  • NEW_INDEX_RESTORED : Unassigned as a result of restoring into a new index. 重新恢复一个新索引
  • EXISTING_INDEX_RESTORED : Unassigned as a result of restoring into a closed index. 重新恢复一个已关闭的索引
  • REPLICA_ADDED : Unassigned as a result of explicit addition of a replica. 添加副本
  • ALLOCATION_FAILED : Unassigned as a result of a failed allocation of the shard. 分配分片失败
  • NODE_LEFT : Unassigned as a result of the node hosting it leaving the cluster. 集群中节点丢失
  • REROUTE_CANCELLED : Unassigned as a result of explicit cancel reroute command. reroute命令取消
  • REINITIALIZED : When a shard moves from started back to initializing, for example, with shadow replicas. 重新初始化
  • REALLOCATED_REPLICA : A better replica location is identified and causes the existing replica allocation to be cancelled. 重新分配副本

查看具体索引未分配或不搬迁的原因

1
2
3
4
5
6
GET _cluster/allocation/explain
{
"index":"index_name",
"shard":0,
"primary":true
}

返回结果:

  1. The current state of the shard.
  2. The reason for the shard originally becoming unassigned.
  3. Whether to allocate the shard.
  4. Whether to allocate the shard to the particular node.
  5. The decider which led to the no decision for the node.导致该节点没有决策的决策器。
  6. An explanation as to why the decider returned a no decision, with a helpful hint pointing to the setting that led to the decision. 解释为什么决策器返回“否”决策,并提供一个有用的提示,指出导致该决策的设置。

解释一个索引为什么分配到该节点

1
2
3
4
5
6
7
GET _cluster/allocation/explain
{
"index": "my-index-000001",
"shard": 0,
"primary": false,
"current_node": "my-node"
}

重新尝试分配失败的shard

1
POST _cluster/reroute?retry_failed=true

查看等待中的任务

1
GET _cat/pending_tasks

可以看到任务都被指派了优先级( 比如说 URGENT 要比 HIGH 更早的处理 )

查看nodes

1
GET _cat/nodes

Read More