<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>云原生实时数据仓库 Blog</title>
        <link>https://selectdb.com/blog</link>
        <description>云原生实时数据仓库 Blog</description>
        <lastBuildDate>Mon, 28 Nov 2022 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-Hans</language>
        <item>
            <title><![CDATA[小米A:B实验场景基于 Apache Doris 的查询提速优化实践]]></title>
            <link>https://selectdb.com/blog/小米A:B实验场景基于 Apache Doris 的查询提速优化实践</link>
            <guid>/小米A:B实验场景基于 Apache Doris 的查询提速优化实践</guid>
            <pubDate>Mon, 28 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读： Apache Doris 是小米集团内部应用最为广泛的 OLAP 引擎之一，本文主要从数据的角度分析 A/B 实验场景查询的性能现状，探讨基于 Apache Doris 的性能优化的解决方案。经过一系列基于 Doris 的性能优化和测试，A/B 实验场景查询性能的提升超过了我们的预期。希望本次分享可以给有需要的朋友提供一些参考。]]></description>
            <content:encoded><![CDATA[<p><strong>导读：</strong> Apache Doris 是小米集团内部应用最为广泛的 OLAP 引擎之一，本文主要从数据的角度分析 A/B 实验场景查询的性能现状，探讨基于 Apache Doris 的性能优化的解决方案。经过一系列基于 Doris 的性能优化和测试，A/B 实验场景查询性能的提升超过了我们的预期。希望本次分享可以给有需要的朋友提供一些参考。</p><p>作者｜小米集团大数据工程师 乐涛</p><h1>业务背景</h1><p>A/B 实验是互联网场景中对比策略优劣的重要手段。为了验证一个新策略的效果，需要准备原策略 A 和新策略 B 两种方案。随后在总体用户中取出一小部分，将这部分用户完全随机地分在两个组中，使两组用户在统计角度无差别。将原策略 A 和新策略 B 分别展示给不同的用户组，一段时间后，结合统计方法分析数据，得到两种策略生效后指标的变化结果，并以此判断新策略 B 是否符合预期。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5dcb0c3801d547dc8a93d9e7788ef30c~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>小米 A/B 实验平台是一款通过 A/B 实验的方式，借助实验分组、流量拆分与科学评估来辅助完成科学的业务决策，最终实现业务增长的一款运营工具产品。其广泛的应用于产品研发生命周期中各个环节：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbc8ef09f18144f0837ee026d014d5bf~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><h1>数据平台架构</h1><p>本文主要从数据的角度分析 A/B 实验场景查询的性能现状，探讨一下基于 Apache Doris 的性能优化的解决方案。A/B实验平台的架构如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df1c3151f18a4dadb2105c194fa7569b~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><ul><li>平台使用的数据主要包含平台自用的实验配置数据、元数据，以及业务方上报的日志数据。</li><li>由于业务方引入 SDK，并与分流服务进行交互，日志数据中包含其参与的实验组 ID 信息。</li><li>用户在实验平台上配置、分析、查询，以获得报告结论满足业务诉求。</li></ul><p>鉴于 AB 实验报告各个业务方上报数据的链路都大体类似，我们就拿<strong>头部业务方广告业务</strong>举例，数据流程如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3dd1afba02114622820bd2ee05a047e4~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>从上图可知，整个数据链路并不复杂，日志数据传入后，经过必要的数据处理和清洗工作进入 Talos（小米自研消息队列），通过 Flink 任务以明细数据的形式实时写入到 Doris 表中，同时 Talos 数据也会同步到 Hive 表进行备份，以便问题排查和数据修复。</p><p>出于对高效写入以及字段增减需求的考虑，<strong>Doris 明细表以 Duplicate 模型来建模</strong>：</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">dwd_xxxxxx</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">olap_date</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">11</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"分区日期"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">user_id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">varchar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"用户id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">exp_id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">varchar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">512</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"实验组ID"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">dimension1</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">varchar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">dimension2</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">varchar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">256</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">dimensionN</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">bigint</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">index1</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">decimal</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">indexN</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">11</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ENGINE</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">OLAP</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">DUPLICATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token punctuation" style="color:#393A34">(</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">olap_date</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">user_id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">COMMENT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"OLAP"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> RANGE</span><span class="token punctuation" style="color:#393A34">(</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">olap_date</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> p20221101 </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221101"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221102"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> p20221102 </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221102"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221103"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">PARTITION</span><span class="token plain"> p20221103 </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221103"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"20221104"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">DISTRIBUTED</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">HASH</span><span class="token punctuation" style="color:#393A34">(</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">user_id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> BUCKETS </span><span class="token number" style="color:#36acaa">300</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">数据现状分析</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在提速之前，小米 A/B 实验平台完成实验报告查询的 P95 时间为<strong>小时级</strong>，实验报告使用数据的方式存在诸多的性能问题，直接影响业务部门做运营和决策的效率。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="报告查询基于明细"><strong>报告查询基于明细</strong><a class="hash-link" href="#报告查询基于明细" title="标题的直接链接">​</a></h2><p>当前报告查询的数据来源为明细表，而明细表的数据量巨大：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0930be665be34ef7bdeba0e81b2dea68~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>而且，实验报告的查询条件中时间范围常常横跨多天。基于历史查询报告统计，查询条件中时间范围大于一天的报告占比 69.1%，具体的时间跨度占比分布如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/941e6cc43f344dc29cfe43f19ac02cc1~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>明细数据的巨大扫描量给集群带来了不小的压力，且由于报告查询存在并发以及 SQL 的拆分，如果一个 SQL 请求不能快速的返回结果释放资源，也会影响到请求的排队状况。因此在工作时间段内 Doris 集群BE节点 CPU 负载状况基本是持续满载，磁盘 IO 也持续处于高负荷状态，如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b5b7d2593f7f46758502bb87ca05d72d~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>BE节点CPU使用率</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9a5b015ff44141edb47838b8dc791869~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>BE节点磁盘IO</p><p><strong>个人思考：</strong></p><ul><li>当前报告所有查询基于明细数据，且平均查询时间跨度为 4 天，查询扫描数据量上百亿。由于扫描数据量级大，计算成本高，给集群造成较大压力，导致数据查询效率不高。</li><li>如果通过对数据进行预聚合处理，控制 Scan Rows 和 Scan Bytes，减小集群的压力，查询性能会大幅提升。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="字段查询热度分层分布">字段查询热度分层分布<a class="hash-link" href="#字段查询热度分层分布" title="标题的直接链接">​</a></h2><p>由于之前流程管控机制相对宽松，用户添加的埋点字段都会进入到明细表中，导致字段冗余较多。统计历史查询报告发现，明细表中常用的维度和指标只集中在部分字段，且查询热度分层分布：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f13a26d96a3c4bf381d5f36bdfec7e05~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b77b0b97c67491492819e3c29b09b22~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>参与计算的指标也集中在部分字段，且大部分都是聚合计算(sum)或可以转化为聚合计算(avg)：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b57d1ab7552c45d4a0031f369d33f34f~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><strong>个人思考：</strong></p><ul><li>明细表中参与使用的维度只占 54.3%，高频使用的维度只占 15.2%，维度查询频次分层分布。</li><li>数据聚合需要对明细表中维度字段做取舍，选择部分维度进行上卷从而达到合并的目的，但舍弃部分字段必然会影响聚合数据对查询请求的覆盖情况。而维度查询频次分层分布的场景非常适合根据维度字段的热度做不同层次的数据聚合，同时兼顾聚合表的聚合程度和覆盖率。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="实验组-id-匹配效率低"><strong>实验组 ID 匹配效率低</strong><a class="hash-link" href="#实验组-id-匹配效率低" title="标题的直接链接">​</a></h2><p>当前明细数据的格式为：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fb5655d0051b46acae29c77a40c3b38c~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>明细数据中的实验组 ID 以逗号分隔的字符串形式聚拢在一个字段中，而实验报告的每条查询语句都会使用到<code>exp_id</code>过滤，查询数据时使用 LIKE 方式匹配，查询效率低下。</p><p><strong>个人思考：</strong></p><ul><li>将实验组 ID 建模成一个单独的维度，可使用完全匹配代替 LIKE 查询，且可利用到 Doris 索引，提高数据查询效率。</li><li>将逗号分隔的实验组 ID 直接打平会引起数据量的急剧膨胀，因此需要设计合理的方案，同时兼顾到数据量和查询效率。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="进组人数计算有待改进"><strong>进组人数计算有待改进</strong><a class="hash-link" href="#进组人数计算有待改进" title="标题的直接链接">​</a></h2><p>进组人数查询是实验报告的必查指标，因此其查询速度很大程度上影响实验报告的整体查询效率，当前主要问题如下：</p><ul><li>当进组人数作为独立指标计算时，使用近似计算函数<code>APPROX_COUNT_DISTINCT</code>处理，是通过牺牲准确性的方式提升查询效率。</li><li>当进组人数作为复合指标的分母进行计算时，使用<code>COUNT DISTINCT</code>处理，此方式在大数据量计算场景效率较低。</li></ul><p><strong>个人思考：</strong></p><ul><li>AB实验报告的数据结论会影响到用户决策，牺牲准确性的方式提升查询效率是不可取的，特别是广告这类涉及金钱和业绩的业务场合，用户不可能接受近似结果。</li><li>进组人数使用的<code>COUNT DISTINCT</code>计算需要依赖明细信息，这也是之前查询基于明细数据的重要因素。必须为此类场景设计新的方案，使进组人数的计算在保证数据准确的前提下提高效率。</li></ul><h1>数据优化方案</h1><p>基于以上的数据现状，我们优化的核心点是将明细数据预聚合处理，通过压缩数据来控制 Doris 查询的 Scan Rows 和 Scan Bytes。与此同时，使聚合数据尽可能多的覆盖报告查询。从而达到，减小集群的压力，提高查询效率的目的。</p><p>新的数据流程如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/57d5553100244922963cdda8f1f46ca6~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>整个流程在明细链路的基础上增加聚合链路，Talos 数据一方面写入 Doris 明细表，另一方面增量落盘到 Iceberg 表中，Iceberg 表同时用作回溯明细数据以及生成聚合数据，我们通过工场 Alpha（小米自研数据开发平台）的实时集成和离线集成保证任务的稳定运行和数据的一致性。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="选取高频使用维度聚合"><strong>选取高频使用维度聚合</strong><a class="hash-link" href="#选取高频使用维度聚合" title="标题的直接链接">​</a></h2><p>在生成数据聚合的过程中，聚合程度与请求覆盖率是负相关的。使用的维度越少，能覆盖的请求就越少，但数据聚合程度越高；使用的维度越多，覆盖的请求也越多，但数据粒度就越细，聚合程度也越低。因此需要在聚合表建模的过程中取得一个平衡。</p><p><strong>我们的具体做法是</strong>：拉取历史（近半年）查询日志进行分析，根据维度字段的使用频次排序确认进入聚合表的优先级。在此基础上得出聚合表的覆盖率和数据量随着建模字段增加而变化的曲线，如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/424e6cb12a40429ba7be6c790a9f3cb3~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>其中覆盖率根据历史请求日志代入聚合表计算得出。</p><p>我们的原则是：针对 OLAP 查询，聚合表的数据量应尽可能的控制在单日 <strong>1 亿条</strong>以内，请求覆盖率尽可能达到 <strong>80%以上</strong>。</p><p>因此不难得出结论：选择 14 个维度字段对聚合表建模比较理想，数据量能控制到单日 <strong>8 千万</strong>条左右，且请求覆盖率约为 <strong>83%</strong> 。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用物化视图"><strong>使用物化视图</strong><a class="hash-link" href="#使用物化视图" title="标题的直接链接">​</a></h2><p>在分析报告历史查询日志时，我们发现不同的维度字段查询频次有明显的分层：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e474e049415d4407bb5027259152b743~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>Top7 维度字段几乎出现在所有报告的查询条件之中，对于如此高频的查询，值得做进一步的投入，使查询效率尽可能的提升到最佳。<strong>Doris 的物化视图能够很好的服务于此类场景。</strong></p><p><strong>什么是物化视图？</strong></p><p>物化视图是一种特殊的<strong>物理表</strong>，其中保存基于基表(base table)部分字段进一步<strong>上卷聚合</strong>的结果。</p><p>虽然在物理上独立存储，但它是对<strong>用户透明</strong>的。为一张基表配置好物化视图之后，不需要为其写入和查询做任何额外的工作：</p><ul><li>当向基表写入和更新数据时，集群会自动同步到物化视图，并通过事务方式保证数据一致性。</li><li>当对基表进行查询时，集群会自动判断是否路由到物化视图获取结果。当查询字段能被物化视图完全覆盖时，会优先使用物化视图。</li></ul><p>因此我们的查询路由如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fef5e54272684d0485c6abe559a6c1dd~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>用户的查询请求会尽可能的路由到聚合表物化视图，然后是聚合表基表，最后才是明细表。</p><p>如此使用<strong>多梯度</strong>的聚合模型的配合来应对<strong>热度分层</strong>的查询请求，使聚合数据的效能尽可能的发挥到最大。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="精确匹配取代-like-查询"><strong>精确匹配取代 LIKE 查询</strong><a class="hash-link" href="#精确匹配取代-like-查询" title="标题的直接链接">​</a></h2><p>既然物化视图这么好用，为什么我们不是基于 Doris 明细表配置物化视图，而是单独开发聚合表呢？是因为明细数据中的实验组ID字段存储和查询方式并不合理，聚合数据并不适合通过明细数据直接上卷来得到。</p><p>上文中已经提到，<code>exp_id</code>（实验组ID）在明细表中以逗号分隔的字符串进行存储，查询数据时使用 LIKE 方式匹配。作为 AB 实验报告查询的<strong>必查条件</strong>，这种查询方式无疑是<strong>低效</strong>的。</p><p>我们希望的聚合方式如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/846019d1281b475f9464ad4f889109c9~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>我们需要将<code>exp_id</code>字段拆开，把数据打平，使用精确匹配来取代LIKE查询，提高查询的效率。</p><p><strong>控制聚合表数据量</strong></p><p>如果只做拆分打平的处理必然会导致数据量的激增，未必能达到正向优化的效果，因此我们还需要想办法来压缩<code>exp_id</code>打平后的数据量：</p><ul><li>聚合表选取维度字段建模的时候，除了上文提到的，以字段的使用频次热度作为依据之外，也要关注字段的<strong>取值基数</strong>，进行综合取舍。如果取值基数过高的维度字段进入聚合表，必然会对控制聚合表的数据量造成阻碍。因此，我们在保证聚合表请求覆盖量的前提下，酌情舍弃部分<strong>高基数</strong>（取值有十万种以上）的维度。</li><li>从业务的角度尽可能过滤无效数据（比如一个实验组的流量为 0% 或者 100%，业务上就没有对照的意义，用户也不会去查，这样的数据就不需要进入聚合表）。</li></ul><p>经过这一系列步骤，最终聚合表的数据量被控制在单日约 8000 万条，并没有因为 <code>exp_id</code>打平而膨胀。</p><p>值得一提的是，<code>exp_id</code>字段拆分后，除了查询从LIKE匹配变为精确匹配，还额外带来了两项收益：</p><ul><li>字段从<code>String</code>类型变为<code>Int</code>类型，作为查询条件时的比对效率变高。</li><li>能利用Doris的<strong>前缀索引</strong>和<strong>布隆过滤器</strong>等能力，进一步提高查询效率。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用-bitmap-去重代替-count-distinct"><strong>使用 BITMAP 去重代替</strong> <strong>COUNT DISTINCT</strong><a class="hash-link" href="#使用-bitmap-去重代替-count-distinct" title="标题的直接链接">​</a></h2><p>要提速实验报告查询，针对进组人数（去重用户数）的优化是非常重要的一个部分。作为一个对明细数据强依赖的指标，我们如何在不丢失明细信息的前提下，实现像 Sum,Min,Max 等指标一样高效的预聚合计算呢？<strong>BITMAP 去重计算可以很好的满足我们的需求。</strong></p><p><strong>什么是BITMAP去重？</strong></p><p>BITMAP 去重简单来说就是建立一种数据结构，表现形式为内存中连续的二进制位(bit)，参与去重计算的每个元素（必须为整型）都可以映射成这个数据结构的一个 bit 位的下标，如下图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/930a9d6a58b54a6697c8821d766c40f5~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>计算去重用户数时，数据以 <code>bit_or</code>的方式进行合并，以<code>bit_count</code>的方式得到结果。更重要的是，如此能实现去重用户数的预聚合。BITMAP 性能优势主要体现在两个方面：</p><ul><li>空间紧凑：通过一个 bit 位是否置位表示一个数字是否存在，能节省大量空间。以 Int32 为例，传统的存储空间为 4 个字节，而在 BITMAP 计算时只需为其分配 1/8 字节（1个 bit 位）的空间。</li><li>计算高效：BITMAP 去重计算包括对给定下标的 bit 置位，统计 BITMAP 的置位个数，分别为 O(1) 和 O(n) 的操作，并且后者可使用 CLZ，CTZ 等指令高效计算。此外，BITMAP 去重在 Doris 等 MPP 执行引擎中还可以并行加速处理，每个节点各自计算本地子 BITMAP，而后进行合并。</li></ul><p>当然，以上只是一个简化的介绍，这项技术发展至今已经做了很多优化实现，比如RoaringBitmap，感兴趣的同学可以看看：<a href="https://github.com/RoaringBitmap/RoaringBitmap" target="_blank" rel="noopener noreferrer">https://github.com/RoaringBitmap/RoaringBitmap</a></p><p><strong>全局字典</strong></p><p>要实现 BITMAP 去重计算，必须保证参与计算的元素为 UInt32 / UInt64，而我们的<code>user_id</code>为<code>String</code>类型，因此我们还需设计维护一个全局字典，将<code>user_id</code>映射为数字，从而实现 BITMAP 去重计算。</p><p>由于聚合数据目前只服务于离线查询，我们选择基于Hive表实现全局字典，其流程如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/501ed8ef2903406e88bc5cdebed2a516~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><strong>指标聚合</strong></p><p>生成 Doris 聚合表时，将 <code>user_id</code>作为查询指标以 BITMAP 类型来存储，其他常规查询指标则通过 COUNT/SUM/MAX/MIN 等方式聚合：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/263174085bbe4bd6b006745a9b5ef0ae~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>如此明细表和聚合表的指标计算对应关系如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eb65d625fcaf4a838a221af0b682d388~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><h1>优化效果</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="sql视角"><strong>SQL视角</strong><a class="hash-link" href="#sql视角" title="标题的直接链接">​</a></h2><p>查询请求转换成 SQL 之后，在明细表和聚合表的表现对比如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/902452d3ffd5442bb46850a4fa0b1f05~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><ul><li>常规聚合指标查询的性能提升自不必说（速度提升 <strong>50~60</strong> 倍）</li><li>进组人数查询性能的提升也非常可观（速度提升 <strong>10</strong> 倍左右）</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="集群视角"><strong>集群视角</strong><a class="hash-link" href="#集群视角" title="标题的直接链接">​</a></h2><p>SQL 查询的快进快出，使查询占用的资源能快速释放，对集群压力的缓解也有正向的作用。</p><p>Doris 集群 BE 节点 CPU 使用情况和磁盘IO 状况的改变效果显著：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c12eb5a3e08b431b9ab7ef261f702210~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>需要说明的是，集群状况的改善（包括实验报告查询 P95 提升）并不全归功于数据预聚合优化工作，这是各方合力协作（如产品业务形态调整，后端查询引擎排队优化，缓存调优，Doris 集群调优等）的综合结果。</p><h1>小技巧</h1><p>由于业务查询需求的多样，在查询明细表时，会出现一个字段<strong>既作为维度又作为指标</strong>来使用的情况。</p><p>如广告业务表中的<code>targetConvNum</code>(目标转化个数)字段，此字段的取值为 0 和 1，查询场景如下：</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--作为维度</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> targetConvNum</span><span class="token punctuation" style="color:#393A34">,</span><span class="token function" style="color:#d73a49">count</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">distinct</span><span class="token plain"> user_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> analysis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">doris_xxx_event </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">where</span><span class="token plain"> olap_date </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20221105</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> event_name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'CONVERSION'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> exp_id </span><span class="token operator" style="color:#393A34">like</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'%154556%'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">group</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">by</span><span class="token plain"> targetConvNum</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">--作为指标</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetConvNum</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> analysis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">doris_xxx_event </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">where</span><span class="token plain"> olap_date </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20221105</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> event_name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'CONVERSION'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> exp_id </span><span class="token operator" style="color:#393A34">like</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'%154556%'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如果这个字段被选取进入聚合表，应该如何处理呢？</p><p><strong>我们的处理方式是：</strong></p><ul><li>在聚合表中把这类字段建模成<strong>维度</strong></li><li>聚合表中需要一个计数指标 cnt，表示聚合表中一条数据由明细表多少条数据聚合得</li><li>当这类字段被作为指标查询时，可将其与cnt指标配合计算得到正确结果</li></ul><p><strong>明细表查询：</strong></p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetConvNum</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> analysis</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">doris_xxx_event </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">where</span><span class="token plain"> olap_date </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20221105</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> event_name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'CONVERSION'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> exp_id </span><span class="token operator" style="color:#393A34">like</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'%154556%'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>对应的聚合表查询：</strong></p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetConvNum </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> cnt</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> agg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">doris_xxx_event_agg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">where</span><span class="token plain"> olap_date </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20221105</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> event_name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'CONVERSION'</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">and</span><span class="token plain"> exp_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">154556</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h1>结束语</h1><p>经过这一系列基于 Doris 的性能优化和测试，A/B 实验场景查询性能的提升超过了我们的预期。值得一提的是，<strong>Doris 较高的稳定性和完备的监控、分析工具也为我们的优化工作提效不少。</strong> 希望本次分享可以给有需要的朋友提供一些参考。</p><p>最后，<strong>感谢 SelectDB 公司和 Apache Doris 社区对我们的鼎力支持</strong>。Apache Doris 是小米集团内部应用最为广泛的 OLAP 引擎之一，目前集团内部正在推进最新的向量化版本升级工作。未来一段时间我们将会把业务优化工作和 Doris 最新的向量化版本进行适配，进一步助力业务的正向发展。</p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[万亿数据秒级响应，Apache Doris 在 360 数科实时数仓中的应用]]></title>
            <link>https://selectdb.com/blog/万亿数据秒级响应，Apache Doris 在360 数科实时数仓中的应用</link>
            <guid>/万亿数据秒级响应，Apache Doris 在360 数科实时数仓中的应用</guid>
            <pubDate>Wed, 23 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[作者｜360数科中间件团队]]></description>
            <content:encoded><![CDATA[<p>作者｜360数科中间件团队</p><p>编辑整理｜SelectDB</p><p>作为以人工智能驱动的金融科技平台，360数科携手金融合作伙伴，为尚未享受到普惠金融服务的优质用户提供个性化的互联网消费金融产品，致力于成为连接用户与金融合作伙伴的科技平台。360数科旗下产品主要有 360借条、360小微贷、360分期等，截止目前，已累计帮助 141 家金融机构为 4300 万用户提供授信服务、为2630万用户提供借款服务、单季促成交易金额1106.75亿元。同时作为国内领先的信贷科技服务品牌，360数科在三季度累计注册用户数首次突破 2 亿。</p><h1>业务需求</h1><p>随着金融科技业务的不断发展，对数据的安全性、准确性、实时性提出了更严格的要求，早期 Clickhouse 集群用于分析、标签业务场景，但是存在稳定性较低、运维复杂和表关联查询较慢等问题，除此之外，我们业务中有部分报表数据分散存储在各类 DB 中，这也导致维护管理复杂度较高，亟需做出优化和重构。</p><h1>系统选型及对比</h1><p>基于以上需求及痛点，我们对实时数仓的选型目标提出了明确的需求，我们希望新的 MPP 数据库具有以下几个特点：</p><ul><li><p>数据写入性能高，查询秒级</p></li><li><p>兼容标准的 SQL 协议</p></li><li><p>表关联查询性能优秀</p></li><li><p>丰富的数据模型</p></li><li><p>运维复杂度低</p></li><li><p>社区活跃</p></li><li><p>对商业友好，无法律风险</p></li></ul><p>2022年3月开始，我们对符合以上特点的数据库 Apache Doris 展开了为期两个月的调研测试。以下是 Apache Doris 1.1.2 在各个方面的满足情况。
<img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b63a70b808c24362ac64906cc5e730ab~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>基于上述情况，我们决定采用 Apache Doris，除了可以满足上文提到的几个特点，我们还考虑以下几个方面：</p><ol><li><p>Clickhouse 由于 Join 查询限制、函数局限性、数据模型局限性（只插入，不更新）、以及可维护性较差等原因，更适合日志存储以及保留当前存量业务，不满足我们当前的业务需求。</p></li><li><p>目前Apache Doris 社区活跃、技术交流更多，SelectDB 针对社区有专职的技术支持团队，在使用过程中遇到问题均能快速得到响应解决。</p></li><li><p>Apache Doris 风险更小，对商业友好，无法律风险。大数据领域 Apache 基金会项目构成了事实标准，在 360数科内部已有广泛应用，且Apache 开源协议对商业友好、无法律风险，不会有协议上的顾虑。</p></li></ol><h1>平台架构</h1><p>360数科大数据平台（毓数）提供一站式大数据管理、开发、分析服务，覆盖大数据资产管理、数据开发及任务调度、自助分析及可视化、统一指标管理等多个数据生命周期流程。在整个 OLAP 中，目前 Apache Doris 主要运用离线数仓分析加速、自助 BI 报表等业务场景。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d465343f74f4451da16716c30dbe17fc~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>在引入 Doris 后，考虑已有数据分析业务以及数据规模，Doris 集群将先同步部分业务上优先级更高的数据。通过上述架构图可以看到，依托 Doris 强大的查询性能，我们将把 Doris 架设在 Hive 数仓的上层，为特定场景进行查询加速，这样的架构建设起来成本很低，只需要完成数据从 Hive 数仓到 Doris 集群的导入适配，因为 Doris 集群并没有产生任何新表，可以直接复用已经建设好的数据血缘关系。</p><p><strong>数据导入方案</strong>，我们在调研了 Stream Load 和 Broker Load 之后，从导入性能、开发成本上进行了评估，在导入性能上，Broker Load 要比 Stream Load 略胜一筹，而在开发成本上两种方式并没有明显的差异。而且对于大表的同步，Broker Load 的导入方式可以做到单表一次导入一个事务，而 Stream Load 在单表数据量超 10G 时则需要拆分后进行数据导入。因此数据导入选择使用 Broker Load 来进行。</p><p><strong>数仓即席查询方案</strong>，我们自行开发的查询引擎支持多查询引擎动态切换的机制，通过识别查询数据的元信息对本次查询做自动的查询引擎（Doris/Presto/Spark/Hive）路由和故障切换。</p><p>Doris 支持原生 MySql 协议，对标准 SQL 支持良好，使得 Doris 可以和一些 BI 工具（帆软、观远等）无缝结合，因此单独搭建了一个 Doris 报表分析集群作为 BI 工具数据源。</p><h1>应用实践</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="doris-对-hive数仓的查询加速方案"><strong>Doris 对</strong> <strong>Hive</strong>数仓的查询加速方案<a class="hash-link" href="#doris-对-hive数仓的查询加速方案" title="标题的直接链接">​</a></h2><p>在即席查询场景中，传统的查询引擎（Hive/Spark/Presto）越来越满足不了数据开发者、数据分析师对查询响应性能提出的高要求，动辄几十秒甚者分钟级的查询耗时极大的限制了相关场景的开发效率。</p><p>为提高查询性能，我们通过架设的 Doris 数仓加速层来缩短查询耗时，目前我们在不开启 Doris 缓存、不开启用物化视图等优化策略的情况下，命中 Doris 即席查询平均耗时即可从几分钟缩短至 5 秒内。</p><p>未来我们将通过分析相关查询的特征，通过开启缓存、创建相关物化视图等策略来进一步优化 Doris 的查询性能。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a9133991b3c84887bec8bb3d8cb668ae~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>实现 Doris 加速的核心是支持查询引擎动态切换，查询引擎动态切换的工作机制如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/02ceaf1903cd4235b1f063b459ba0eef~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>查询引擎会及时收集 Hive 和 Doris 的元信息，包括库、表、表字段、表行数等信息，在用户提交即席查询请求时，首先会解析出用户查询的表，并按照如下顺序判断：</p><ul><li><p>查询的表是否已在 Doris 同步</p></li><li><p>Doris 表和 Hive 表结构是否相同</p></li><li><p>Doris 表和 Hive 表表行数是否一致</p></li></ul><p>如果以上要求均被满足，则会将该查询路由到 Doris，否则会依次按照 Presto、Spark、Hive 的顺序进行路由查询，当查询出现异常时，也会按照该顺序依次进行故障转移。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="慢查询慢导入分析"><strong>慢查询慢导入分析</strong><a class="hash-link" href="#慢查询慢导入分析" title="标题的直接链接">​</a></h2><p>对于慢查询和慢导入，Doris 提供了完善的 Profile 机制，在了解相关技术细节后，我们在线上集群开启了 Profile 收集，通过调度任务定时收集慢查询、慢导入的 Profile 信息并落库。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/803bafba761f46cbafad56f0d0a42ba1~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>Doris 提供的 Profile 信息非常详细，例如<code> OLAP_SCAN_NODE </code>提供了原始的扫描行数，各个索引的过滤行数，每个 Instance 的 <code>EXCHANGE_NODE</code> 提供了接收的数据总行数和接收的数据量大小。这些信息为查询调优提供了详细的依据，我们在使用过程中针对快速定位查询性能的瓶颈进行了优化，取得了良好的效果。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="建表规范"><strong>建表规范</strong><a class="hash-link" href="#建表规范" title="标题的直接链接">​</a></h2><p>在我们的使用场景中，有下列类型的表：</p><ul><li><p>pda表：每日全量更新，即每日分区存储全量快照数据</p></li><li><p>pdi表： 每日增量更新，即每日分区存储增量数据</p></li><li><p>a表：全量不分区表</p></li><li><p>s表：静态非每日更新数据</p></li></ul><p>由于当前 Doris 集群中所有的表都是基于 Hive 数仓中各层级的表同步而来，因此目前仅使用了 Duplcate 模型和 Unique 模型，对于 pda、pdi 和 a 表，为了降低 Doris 表的分区数，减轻 FE 元数据管理压力，我们在建 Doris 表时均启用了根据日期划分的动态分区特性，较久远的历史数据我们按年、月的维度分区归档，近期的数据按日、小时分区，未来我们计划通过程序自动识别完成历史分区的归档合并。</p><p><strong>对于 pda 表使用场景</strong>，pda 表需要每日同步全量数据，我们采用了 Duplicate 模型，不考虑使用 Unique 模型数据去重的原因是 Doris 的导入模型本身就提供了基于任务 Label 的数据一致性保证，同步时一次调度周期的 pda 表的一个分区的导入任务能产生唯一且不变的 Label，因此我们可以保证即使错误执行了多次，该分区的数据仍然不会重复。另外，因为 Duplicate 模型相比于 Unique 模型，在导入和查询阶段均不会做预聚合去重，所以可以一定程度上加速导入和查询的性能。</p><p><strong>对于 pdi 表使用场景</strong>，因在实际使用中 pdi 表存在少数对历史数据的部分更新场景（绝大部分是数据更新场景，基本没有数据删除场景），考虑到 Doris 数据表的分区可用性，我们采用了 Unique 模型，这样在更新历史分区的数据时不必做重建分区操作。</p><p><strong>对于 a 表使用场景</strong>，因业务上可以接受短时间数据不可用情况，我们启用了动态分区，在做数据导入时，每次导入都会先删除历史分区，然后将全量数据导入今天的分区内，这样做的考虑是杜绝重建表操作，且实施成本相对比较低，因此我们没有采取动态更新视图绑定当日分区的方案。</p><p>在 Doris 之前的版本中，尚未实现 Hive 元数据变更同步和管理功能，为了提高效率开发了 Doris 建表工具，我们通过选择和配置数仓集群、Hive 表名、数据模型、Bucket 数量等参数，自动关联 Hive 表，解析表字段并生成对应的建表语句。经过与社区沟通得知，最近即将发布的 1.2 新版本中已经实现 Multi Catalog，支持 Hive 元数据的对接和 Schema 的自动同步，可以极大程度上减少这一部分的工作。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="监控体系"><strong>监控体系</strong><a class="hash-link" href="#监控体系" title="标题的直接链接">​</a></h2><p>当前 Doris 集群监控体系分为主机指标监控告警、日志告警和集群指标监控告警，总体监控体系如下。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/08e798388cdf4b168b8dacad830b8624~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>主机指标监控</strong>是基于 Open-Falcon 开发的监控告警平台，主要采集 Doris 集群节点的 CPU、IO、内存、磁盘等相关指标并进行监控告警。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/870409dffc58460981cff173d3df5c6f~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>集群指标监控</strong>参考了 Doris 官方文档提供的基于 <a href="https://prometheus.io/" target="_blank" rel="noopener noreferrer">Prometheus</a> 和 <a href="https://grafana.com/" target="_blank" rel="noopener noreferrer">Grafana</a> 和集群指标监控方案。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/40f4b870126c4d5e9c31cba6a1ec4c95~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>日志告警</strong>仍然是基于我们的监控告警平台，主要用于监控 Doris 服务日志中容易识别但其他监控方式成本较高的监控、告警场景，是其他两种监控的补充。通过日志监控告警，我们能够准确识别数据导入任务的失败原因并能进行及时的推送通知。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="问题排查和审计日志"><strong>问题排查和审计日志</strong><a class="hash-link" href="#问题排查和审计日志" title="标题的直接链接">​</a></h2><p>为了及时排查一些极端的集群问题，上述针对 Doris 的监控体系建设仍然是不够的。为了在集群 BE 出现异常宕机时快速定位堆栈，需要在所有的 BE 节点开启 Core Dump。除此之外，审计日志在集群的日常运维中也发挥了重要作用。</p><p>对于 Doris 集群的审计日志收集一般可以通过 2 种方式：</p><ul><li><p>第一种方式是通过日志收集组件、收集各个 FE 节点上的<code> fe.audit.log</code></p></li><li><p>第二种方式是通过安装 Doris 提供的 Auditloader 插件（下载 Doris 源码，该插件在 <code>doris/fe_plugins/auditloader</code>，具体使用文档可参考官方文档：<a href="https://doris.apache.org/zh-CN/docs/ecosystem/audit-plugin" target="_blank" rel="noopener noreferrer">审计日志插件</a>）。</p></li></ul><p>考虑到第二种方式操作更简单，因此采用此方式进行日志采集。不过在使用 Auditloader 插件的过程中，陆续发现和修复了一些插件问题，并向社区提交了 PR，与此同时，我们定制开发了内部控制台，便于查看集群的同步任务情况，数据分布情况以及进行审计日志的检索。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cc56e4c650ff4528a65a5a62ea15c2a8~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>审计日志为集群 BE 崩溃时具体 SQL 定位、客户端访问统计、查询 SQL 耗时统计、访问 SQL 特征分析等提供了详细的信息。例如，数据开发曾经反馈查询 Doris SQL 失败，检索日志出现了大量连接数超限的异常，我们通过审计日志，迅速定位到了问题原因是由于上游导入工作流 Bug 在短时间内创建较多的数据库连接。另外，对于曾经使用的低版本 Doris 出现数次 BE 异常宕机问题，我们通过 gdb 调试工具定位到崩溃时 SQL 的 <code>query_id</code> 后，配合审计日志也能快速的定位到导致崩溃的具体 SQL。</p><h1>优化实践</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据导入实践和调优"><strong>数据导入实践和调优</strong><a class="hash-link" href="#数据导入实践和调优" title="标题的直接链接">​</a></h2><p>初期数据源主要来自 Hive 数仓，因此大部分数据导入以 Broker Load 方式为主。大数据平台自助导入任务工作流适配了 Doris Broker Load 导入方式，数据开发零代码——通过简单的勾选配置即可完成自助的 Doris 数据导入工作流创建。</p><p>而在 Broker Load 的使用过程中，我们也陆续遇到了一些问题，这里拿出几个典型的问题和一些调优经验来分享。</p><p><strong>在 Broker Load 导入时遇到的问题：</strong></p><ol><li>因表分桶数设置过少造成 Broker Load 导入失败，具体表现为导入任务失败且异常信息为：</li></ol><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">tablet writer write failed, tablet_id=xxx, txn_id=xxx, err=-238</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>我们推测造成 -238 错误的原因可能是分桶设置太少，接着我们通过 BE 节点的挂载数据来查看单个 Tablet 下的文件大小，我们发现单个 Tablet 的文件占用空间远大于官方推荐的 10GB 上限范围，这也证明了我们的推测正确，因此我们通过适当提高 Doris 表的分桶数，使得这个问题有了较大的缓解。</p><p>顺便说一下，如果出现 -235（旧版本是-215）异常，一般是由于 Compaction 过慢导致 Tablet 版本堆积超过限制，这个时候通过 Grafana 看到 BE Compaction Score 在导入前后有明显的波动，而且绝对值很高。如果遇到此问题可以参阅 ApacheDoris 公众号文章：<a href="https://mp.weixin.qq.com/s/cZmXEsNPeRMLHp379kc2aA" target="_blank" rel="noopener noreferrer">Doris 最佳实践-Compaction调优(3) 对Compaction过程进行调优。</a></p><ol start="2"><li>因 Hive 表字段变更导致 Broker Load 导入失败：</li></ol><p>Hive 表在使用过程中会有一些 DDL 的执行，从而导致表字段新增，我们数仓的 Hive 表均使用 ORC 格式存储，那么就会导致 Hive 表中部分历史分区的 ORC 文件中字段信息缺失（缺失新增字段），而新分区的 ORC 文件中字段是正常的，这个时候如果对历史数据重新导入，就会有下面的异常信息：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">detailMessage: ParseError : Invalid column selected xxx</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在阅读了 Broker Load 相关代码后确认了问题原因：在一次 Broker Load 导入过程中，导入任务的字段解析器会读取一个 ORC 文件头解析字段信息，但解析器只会解析一次，如果一次导入过程中同时有新、历史分区的 ORC 文件，那么就可能导致任务失败。</p><p>修复的方法也很简单，只需针对每个 ORC 文件重新解析一次文件头的字段信息即可。在了解问题原因及分析解决思路后，我们也和社区的同学一起修复了这个问题并提交了相关 PR。</p><ol start="3"><li>遇到空 ORC 文件时 Broker Load 导入失败：</li></ol><p>这个问题的错误表现和问题 2 比较类似，具体原因是 Broker Load 导入过程没有对 ORC 文件做判空，遇到空 ORC 文件仍会尝试解析 ORC 文件字段信息导致报错，我们把这个问题反馈给社区后，社区的同学很快修复了该问题。</p><ol start="4"><li>Broker Load 导入任务出现 <code>Broker list path exception. path=hdfs:xxx</code></li></ol><p>创建 Broker Load 任务，使用 Kerberos 认证访问 HDFS 的 Hive 文件导入数据，Hive 文件路径中分区和下一级目录使用通配符 *，访问所有分区所有文件，任务提交后隔 40 多秒出现如下的错误：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">type:ETL_RUN_FAIL; msg:errCode = 2, detailMessage = Broker list path exception. path=hdfs:xxx</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在阅读了 Broker Load 的访问 HDFS 相关代码后确认了问题原因，Broker Load 调用 HDFS 的 LS、DU 方法时会获取文件目录信息，由于路径下的文件过多导致耗时会超过 45 秒，而 Thrift 设置的 Socket 请求超时默认小于 40 秒，所以出现了上述的 RPC 异常，问题反馈社区后，对 FE 增加了配置参数<code>broker_timeout_ms</code>，设置为 90 秒后解决问题。</p><p><strong>关于 Broker Load 的导入性能调优策略</strong></p><p>我们针对 Broker Load 导入调优的主要方向在确保 Doris 集群不承压的情况下尽可能提高导入并发度，下面根据 2 个典型的案例来说明：</p><ol><li>部分 pdi/pda 表因为数据规模太大导致全量导入耗时过长 <strong>（导入数据源是</strong> <strong>Hive分区表</strong>）</li></ol><p>部分 pdi/pda 表数据规模在 T 级别，在进行全量导入时，如果只提交一个 Broker Load Job ，将因为导入任务的并发不够，导致导入耗时达到 5-6 小时。针对此问题，我们可以对导入任务进行 Job 拆分，在大数据平台也适配这种场景，支持任务的自动拆分和重试机制，具体的拆分方式如下图：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7e47b8037681449b90c87833eb325516~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>不过要注意的是，拆分后可能会对集群有较高的写入压力，要及时监控导入任务和集群的状态，特别针对 -235 的情况可能需要进行 Compaction 调优。</p><ol start="2"><li>部分 ads 表因为数据规模太大导致全量导入耗时过长 （<strong>导入数据源是Hive非分区表</strong>）</li></ol><p>数据开发对部分报表的同步时效提出了很高的要求，我们在针对性的优化表同步时效时，发现一些表导入耗时较长，但通过集群监控体系发现相关表同步期间，BE、FE 节点的 CPU、内存、磁盘 IO 、网卡 IO 并没有达到瓶颈，集群的 Compaction Score 在此期间也一直稳定在低位，且整个同步过程同步任务均未出现-235、-238等相关的错误，我们推测瓶颈可能还是在导入任务的并发程度上。</p><p>因为有些表在 Hive 数仓是非分区的表，所以第 1 种通过划分分区范围拆分多个导入 Job 的方式就行不通了，理论上仍然可以通过划分不同的 HDFS 文件来拆分 Job，但是这种方式在毓数大数据平台还需要进一步去适配，所以我们还是优先考虑通过调整集群配置的方式彻底解决此问题：</p><p>首先可以通过适当调高 FE 的<code>max_broker_concurrency</code>去提高 Scan HDFS 文件阶段的并发度（最高调高至 BE 节点数），而对于 Table Sink 阶段，可通过调高 FE 的<code>default_load_parallelism</code>（设置<code>fe.conf</code>，可调整到 BE 节点数）和 <code>send_batch_parallelism</code>参数（ SQL Session 执行<code>set global send_batch_parallelism=5</code>或在提交 Broker Load 中的 PROPERTIES 中指定，最高调整到 5，如果超过此值，需要同步调整 <code>be.conf </code>的 <code>max_send_batch_parallelism_per_job</code> 参数），提高该阶段并发度。通过提高 Broker Load Job 各阶段导入的并发度，相关报表的同步时效显著提升，这里我们选取 5 张典型表为例，优化前后的同步时效表现如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b4a5dd5b8e9409c89852e9031392f5e~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="双机房容灾建设"><strong>双机房容灾建设</strong><a class="hash-link" href="#双机房容灾建设" title="标题的直接链接">​</a></h2><p>为了保障 Doris 集群的可用性，我们需要为 Doris 集群提供双机房容灾能力。Doris 目前虽然可以通过不同的 Tag 将 BE 分组部署在多个机房，但是无法解决机房出现问题时的 FE 可用性问题。经过方案调研分析，我们决定通过自行开发 Replicator 主从同步插件去实施双机房容灾建设，具体的架构如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f158f0f8e8de45d2a09f79054977f9e8~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>通过在主集群安装 Replicator 插件，Replicator 插件会拦截并解析主集群执行的全量 SQL，然后经过过滤操作，筛选涉及库、表结构变更和数据增、删、改相关的 SQL，并将相关 SQL（部分 SQL 需要改写）发送到备集群进行重放。除此之外，我们在 Doris 控制台开发了 Validator 数据校验程序，定期校验主备集群间的数据结构差异和数据差异并上报，在主集群因各种问题导致不可用时，直接通过切换 DNS 解析地址到备集群 LVS 地址完成主备集群的切换。</p><h1>总结规划</h1><h3 class="anchor anchorWithStickyNavbar_LWe7" id="效果总结">效果总结<a class="hash-link" href="#效果总结" title="标题的直接链接">​</a></h3><p>从 2022 年3月份开始进行对实时数仓沟通进行调研，7月份正式上线生产，集群数据规模快速增长。目前，生产环境共有 2 个集群，数百张表，几十 TB 数据，每日有数百个同步工作流在运行，几十亿规模的数据新增/更新。在此规模下，Doris 对业务支持良好，稳定运行。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a25f929ab96c44a89800d3fa814d1e57~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ul><li>Doris 集群架构清晰简单，不依赖其他组件，数据模型简单，数据导入方式多样化且适配成本很低，使得我们可以快速完成前期的调研测试并在短时间内上线实施。</li></ul><ul><li>Doris 集群作为目前公司 BI 工具的重要数据源，承载了相当一部分的报表分析业务，极大加速了报表分析的时效性。Doris 上线 3+月的时间，已经承载了小部分即席查询场景，大大缩短了相关查询的耗时。</li></ul><ul><li>Doris 具有完善的监控机制和审计机制，极大的降低了我们的运维工作</li></ul><ul><li>Doris 社区十分活跃，在我们使用 Doris 过程中遇到的一些疑难问题，官方也可以及时进行响应、处理。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="未来规划">未来规划<a class="hash-link" href="#未来规划" title="标题的直接链接">​</a></h3><p>在近期的规划中，我们希望 Doris 能支撑更多的业务场景、发挥更大价值，例如基于 Doris 建立实时数仓、基于 Doris 重构用户行为画像、Doris HIVE 外表特性等。同时我们计划通过分析用户的查询 SQL 特征，结合 Doris 的查询缓存和物化视图特性，进一步提升查询效率。通过开发集群探查工具，实时探测集群数据表的数据分布情况，比如 Tablet 有没有过大，Tablet 数据分布是否均匀等，综合探查集群的运行情况并自动给出优化建议。</p><p>目前我们使用了 Doris 有大半年时间，在这半年期间一直保持和社区同学进行交流（提交 Issues 和 PRs），非常感谢 SelectDB 团队一直以来对我们的技术支持。最后祝 Apache Doris 越来越好，为基础软件建设添砖加瓦。</p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[张文歆：思维需碰撞，才有更大的“火花”｜对话 Doris]]></title>
            <link>https://selectdb.com/blog/张文歆：思维需碰撞，才有更大的"火花"</link>
            <guid>/张文歆：思维需碰撞，才有更大的"火花"</guid>
            <pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[「 对话 Doris」是 SelectDB 推出的系列专访栏目，本栏目将对 Apache Doris 社区优秀开发者进行访谈，希望通过他们在社区的成长经历，传递出对开源的态度、参与开源方法论以及实践经验，让开发者更加了解开源、了解 Doris，并呼吁大家参与到社区建设中来。]]></description>
            <content:encoded><![CDATA[<p>「 <strong>对话 Doris</strong>」是 SelectDB 推出的系列专访栏目，本栏目将对 Apache Doris 社区优秀开发者进行访谈，希望通过他们在社区的成长经历，传递出对开源的态度、参与开源方法论以及实践经验，让开发者更加了解开源、了解 Doris，并呼吁大家参与到社区建设中来。</p><p>第 1 期我们邀请了 Apache Doris Committer、SelectDB 查询优化器研发 Leader 张文歆，一起来看看他在参与社区建设中都有哪些经验和大家分享吧。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/27a117fa0cd444ccb447cd26f2f0db82~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p><strong><em>推荐语：</em></strong></p><p>从 15 年参加工作开始，也接触了许多开源项目，Apache Doris 是我遇到的最活跃的开源社区之一，“事事有回应，件件有着落”这句话用来形容 Apache Doris 社区再合适不过了。</p><p>—— Apache Doris Committer 张文歆</p><p><strong>Q 1</strong>：<strong>你好，很开心可以与你交流，简单介绍下自己吧</strong></p><p>我叫张文歆，2022 年初加入 SelectDB，主要负责 Apache Doris 新查询优化器 Nereids 的开发工作。我从 15 年研究生毕业后进入百度，17 年初转入离线计算引擎组，自此之后一直从事大数据相关的工作，在离线计算，数据湖，在离线一体平台等领域都有所涉猎。</p><p><strong>Q 2：你是如何看待开源的，简单谈谈你对开源的看法</strong></p><p>我是很拥抱开源的。</p><p>从我开始工作起，就开始接触开源软件，当时我们大量使用开源软件来构建系统，在做爬虫系统时，我们使用了 Selenium、Chromium 来抓取一些特定的网页。后来转入到离线计算组之后，经历了将自研引擎切换到了开源引擎 Spark，后来也相继使用过 Iceberg，Hudi 等，并于 2022 年初加入了 Apache Doris 社区，也很荣幸在 Apache Doris 社区成长为 Committer。从以上经历中即可看出，我无时无刻都在与开源软件打交道，开源也给我带来许多成长与蜕变。</p><p>从个人角度来讲，开源项目能够帮助我快速的成长。一方面，阅读优秀开源项目的源码和文档，可以不断的获取这一领域最前沿的思想，更新自己的知识库。而使用这些优秀的开源项目，能够让我们站到巨人的肩膀上，更容易达成项目的目标；另一方面，当我们向开源项目提交代码时，既能够通过回馈开源社区获得一定的成就感，也能够通过与社区开发者进行代码层级的交流，来提升自己的代码能力。</p><p>从公司的角度来看，无论是主导一个开源项目，还是使用一个开源项目，都能受到启发、从中收益。开源社区可以快速的帮助一个项目发展成熟，加速功能迭代，提升自己的产品功能与稳定性。</p><p>开源项目对于行业的进步也起到了非常大的作用。一方面，行业内的公司使用开源项目，可以极大的避免重复建设，将更多的精力投入到新思想、新方法的尝试与开发中；另一方面，通过与开源社区同学进行交流，会碰撞出更大的思维火花，更多且更快的产生解决问题的办法与思路，这些新的方法，经过公司内部的检验，又会回馈给开源社区，从而形成一个正反馈，不断的加速行业的进步。</p><p><strong>Q 3：如何成长为 Committer，可以分享下经验吗</strong></p><p>我参与到 Apache Doris 社区的时间并不算长。最初也是因为工作需要开始接触 Doris。在使用过程中，逐渐发现了一些用起来不太顺手的地方，便开始给 Doris 提交 PR，一开始也只是进行一些小修小补；后来，我们发现 Doris 旧的查询优化器代码结构不是很合理，既不容易阅读理解，也难以修改。当我们在增加期望的功能时，就遇到了比较多的困难，也由此有了为 Doris 重新写一个全新查询优化器的想法，非常巧的是，Doris 社区当时也创建了一个新查询优化器的 SIG，并和大家一起交流、设计并实现新的查询优化器。</p><p>在参与 Doris 的开发建设中，我积累了一些参与开源的经验，在此分享给大家。如果想深入到一个开源项目中，或者成为开源项目的 Committer 甚至是 PMC ，我认为在最初接触时，一定要有学习了解该项目的动力，动力来源也许是自驱或者工作需要；在了解该项目后，则需要持续的使用，并及时解决在使用中遇到的问题，不吝啬的将代码贡献到社区，在这个过程中，你会对项目有越来越深入的了解。对于遇到的问题，一定要在社区中发出自己的声音，这非常的重要，这可以让社区及时感知到这个问题，并且需要持续的和社区保持交流，增加对问题的理解；最后来借助社区的力量来一起解决这些问题，回馈社区。</p><p>除自身之外，我认为社区氛围也是非常重要的因素之一， Apache Doris 开源社区是我遇到的最活跃社区之一，“事事有回应，件件有着落”这句话用来形容 Apache Doris 社区再合适不过了。</p><p><strong>Q 4：分享你在 Doris 社区遇到最有趣、有意义的事情吧</strong></p><p>在我印象中，最深刻的是和优化器 SIG 的同学们一起做新优化器基础数据结构设计这件事，由于 SIG 的同学们在这方面都没有完整的从 0 到 1 的经验，这就需要大家一起探索最适合 Doris 新优化器的数据结构，我们经历了超过一个月的密集讨论，基础数据结构前后做了三版概念验证，最终找到了适合 Doris 的设计方案。在讨论的过程中，我深刻体会到了开源社区所带来的优势，SIG 内的同学来自不同的公司、拥有不同的背景知识，在之前项目开发中遇到过不同的问题。在设计的过程中，我们将问题分类整理，充分讨论，从而避免了很多弯路。有了良好的基础数据结构设计，Doris 的新优化器正在快速构建中，相信不久的将来就能和大家正式见面。</p><p>同时借此机会也想呼吁更多小伙伴一起加入新优化器的开发建设中来，贡献一份自己的力量。</p><p><strong>Q 5：你认为 Doris 哪些做的比较好，哪些需要优化呢？</strong></p><p>在 2022年10月初，基于 Apache Doris 内核打造的云原生实时数据库 SelectDB 发布了 ClickBench 的测试成绩， SelectDB 在 c6a.4xlarge, 500gb gp2 机型上排名第一，在所有机型的总榜上排名第二。值得一提的是，SelectDB 所有的性能优化点，都将毫无保留的回馈给 Doris 社区，这意味着 Doris 将具有极致的宽表性能表现。同时，在即将发布的 Doris 1.2 版本中，从 TPC-H 和 SSB 测试集上的性能测试表明，Doris 相较于之前的版本，在多表执行性能上也将有巨大的提升。</p><p>除此之外，我们在某些方面还有很大的优化空间，在 Doris1.0 发布之后，Doris 增加了非常多的功能，包括新的 Unique Key 表模型、全新的便于接入数据湖的外部数据源接口、半结构化数据类型等等，这些功能扩展了 Doris 的使用场景，使得更多的用户可以从 Doris 的高性能引擎中受益。但我想，我们在这些方面还是有更进一步的空间，有待于 Doris 社区同学一起来努力来实现。</p><p>在性能方面，我们还需要持续优化在复杂场景上的查询性能，尤其是在查询规划器的性能优化方面，以便应对未来更加复杂的用户场景。</p><p>在功能方面，我们将探索更多的可能性，向着流批一体，在离线一体等方向探索，用于满足用户在复杂场景中期望将基础设施化繁为简的强烈诉求。</p><p><strong>Q 6：对于 Doris 社区未来的发展，有什么期望及建议？</strong></p><p>随着 Doris 这半年多的时间持续的在多个维度持续发力，社区力量逐渐的强大起来，社区用户和开发者也越来越多，Contributor 数量和 Commit 数量屡创新高，这为 Doris 的持久的高质量发展提供了坚实的基础。</p><p>我对Doris社区的未来有两个期望：</p><p>第一个是希望 Doris 能够建立更完善的发展路线图。在功能实际开发前，能够在社区开展更大范围的讨论，让社区中的每个人对于 Doris 的发展能够做到心中有数，增强使用 Doris 的信心。</p><p>第二个是希望能够吸引到更多的国外的开发者。将国外在数据分析领域的诉求和发展方向带给 Doris，使得 Doris 成为一个更具国际化的开源项目。</p><p><strong>Q 7</strong>：<strong>最后，你想对广大的开发者说些什么？</strong></p><p>通过参与开源社区的开发建设，不仅可以提升我们的代码能力，认识更多志同道合的小伙伴；更重要的是，通过对开源项目的持续贡献，留下一段美好而有意义的人生足迹。</p><p>最后，期待 Apache Doris 社区更多小伙伴加入到 Doris 的开发队伍中来，一起进行 Doris 的开发与建设，与 Doris 共同成长，为国产数据库走向世界贡献微薄而不可或缺的一份力量，让自己和 Doris 都拥有更加美好的未来。</p>]]></content:encoded>
            <category>人物专访</category>
        </item>
        <item>
            <title><![CDATA[DDL 毫秒级同步，Light Schema Change 的设计与实现]]></title>
            <link>https://selectdb.com/blog/DDL 毫秒级同步，Light Schema Change 的设计与实现</link>
            <guid>/DDL 毫秒级同步，Light Schema Change 的设计与实现</guid>
            <pubDate>Fri, 11 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[作者介绍：]]></description>
            <content:encoded><![CDATA[<blockquote><p><strong>作者介绍：</strong></p><p>刘常良：Apache Doris Contributor，SelectDB 存储层研发工程师。</p><p>吴迪：Apache Doris Committer，SelectDB 生态研发工程师。</p></blockquote><p>在 OLAP 的业务场景中，Schema Change 是一个相对常见的业务需求，当上游数据源维度发生变化时，通常需要将数仓中的表结构进行相应的变更。相对于业界其他 OLAP 数据库，Apache Doris 对于 Schema Change 的支持非常友好，可支持 Online Schema Change，进行加减列或修改列类型时无须停服，保证了系统的高可用和业务的平稳运转。但在部分场景下，Schema Change 也存在一定的瓶颈，例如：在面对大数据量宽表场景下， Schema Change 执行效率相对较低、耗费时间较长；另外基于 Flink 和 Doris 构建实时数仓时，因 Schema Change 是异步作业，一旦上游表发生维度变化，需要自己维护 Schema Change 的执行状态，并在完成后重启 Flink Job，无法做到自动化变更，冗长复杂的操作流程无疑增加了许多开发和运维的成本，且可能会带来消费数据的积压。</p><p>基于此，Apache Doris 将在 1.2.0 版本推出新功能 Light Schema Change ，我们将通过本文为大家揭秘 Light Schema Change 的设计与实现，并分享其如何提升表结构变更的执行效率、以及如何在 Flink + Doris 实时数仓场景中实现 DDL 操作的自动同步。</p><h1>需求背景</h1><p>在正式介绍之前，需要认识一下 Apache Doris 1.2.0 版本之前支持的 3 种 Schema Change 方式，均是异步作业，分别为：</p><ol><li><p><strong>Hard Linked Schema Change：</strong> 无需转换数据，直接完成。新摄入的数据都按照新的Schema处理，对于旧数据，新加列的值直接用对应数据类型的默认值填充。例如加列操作 <code>ALTER TABLE site_visit ADD COLUMN click bigint SUM default '0';</code></p></li><li><p><strong>Hard Sort Schema Change：</strong> 改变了列的排序方式，需对数据进行重新排序。例如删除排序列中的一列, 字段重排序 <code>ALTER TABLE site_visit DROP COLUMN city;</code></p></li><li><p><strong>Hard Direct Schema Change：</strong> 重刷全量数据，成本最高。当修改列的类型，稀疏索引中加一列时需要按照这种方法进行 <code>ALTER TABLE site_visit MODIFY COLUMN username varchar(64);</code></p></li></ol><p>而值得注意的是，<strong>1.2.0 版本新增的 Light Schema Change</strong> <strong>新特性正是</strong> <strong>替换了</strong> <strong>加减列操作的</strong> <strong>Hard Linked Schema Change 流程</strong> <strong>。</strong> 在了解 Light Schema Change 的优势之前，我们需要先知道 Hard Linked Schema Change 的技术原理。</p><p>Hard Linked Schema Change 在作用于加减 Value 列时，当接收到加减 Value 列的 DDL，Doris FE（下文简称 FE）会发起一个异步的作业，并立刻返回。作业主要做以下事情：</p><ol><li><p>创建新的 Tablet。</p></li><li><p>等待已经开始的导入完成。</p></li><li><p>将当前已有的数据文件 Hard Link 到新 Tablet 对应的数据目录下。</p></li></ol><p>在使用过程中，我们发现 Hard Linked Schema Change <strong>存在几个明显的问题：</strong></p><ol><li><p>当集群规模和表数据量达到一定数量时，Hard Linked Schema Change 等待时间就会明显增加。</p></li><li><p>在 Hard Linked Schema Change 过程中，如果有导入任务，为保证 Schema Change 的事务性，会对新/旧 Tablet 进行双写，Schema Change 则需要等待导入任务完成之后才可以进行。在遇到这种情况时，用户往往只有 2 个选择：一个是等待 Schema Change 完成再进行导入；一个是接受双写的代价。而这两种选择对用户都不太友好。</p></li><li><p>Hard Linked Schema Change 无法处理 Delete Predicate。如果用户在 Schema Change 之前调用过 Delete 语句，Doris 不会立刻删除数据，而是记一个Rowset，并把 Delete Predicate 和此 Rowset 进行关联；如果在做 Hard Linked Schema Change 过程中，发现 Delete Predicate，则会转化为 Sort/Direct Schema Change，对数据进行重写。</p></li></ol><h1>Light Schema Change 的设计与实现</h1><p>相较于 Hard Linked Schema Change 的作业流程，Apache Doris 1.2.0 新版本的 Light Schema Change 的实现原理就要简单的多，只需要在加减 Value 列的时候，对 FE 中表的元数据进行修改并持久化。在设计过程中，需要考虑到以下实现细节：</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="解决-schema-不一致问题">解决 Schema 不一致问题<a class="hash-link" href="#解决-schema-不一致问题" title="标题的直接链接">​</a></h2><p>由于 Light Schema Change 只修改了 FE 的元数据，没有同步给 BE，而 BE 对读写操作依赖于自身的 Schema，这时候就会出现 Schema 不一致的问题。为了解决此问题，我们对 BE 读写流程进行了修改。主要包含以下方面：</p><ol><li><p>读取数据的时候下发 Schema 。Schema 每一列都有相应的 Unique ID，该 Unique ID 由 BE 的每个 Tablet 负责产生和赋值，但对于所有的 Tablet，其 Schema 列的 Unique ID 都是一致的，因此将此过程移在 FE 上去实现。FE 在生成查询计划时，会把最新的 Schema 附在其中，并一起发给 BE，BE 会拿最新的 Schema 读取数据，以此来解决<strong>读过程</strong>中 Schema 的不一致。</p></li><li><p>将 Schema 持久化到 Rowset 的元数据中。FE 在发起导入任务的时候，会把最新的 Schema 一起下发给 BE，BE 会根据最新的 Schema 对数据进行写入并与 Rowset 绑定，将该 Schema 持久化到 Rowset 的元数据之中，实现了 Rowset 数据的自解析，解决了<strong>写过程</strong>中 Schema 的不一致 <strong>。</strong></p></li><li><p>在进行 Compaction 的时候，选取需要进行 Compaction 的 Rowset 中最新的 Schema，作为Compaction 之后 Rowset 所对应的 Schema，以此来解决拥有不同 Schema 的 Rowset 合并问题。</p></li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="全局-schema-cache">全局 Schema Cache<a class="hash-link" href="#全局-schema-cache" title="标题的直接链接">​</a></h2><p>由于 Rowset 的元数据一直存储在内存中，如果每个 RowsetMeta 都存储一份 Schema，会对内存造成较大的压力。为了解决这个问题，实现了一个全局的 Schema Cache 管理相同的 Schema，这样就算有成千上万个 Rowset，只要 Schema 相同，内存中只会存在一份 Schema。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="支持物化视图">支持物化视图<a class="hash-link" href="#支持物化视图" title="标题的直接链接">​</a></h2><p>Light Schema Change 也实现了对物化视图的支持。对读写流程修改之后，物化视图也可以正常读写。同时，如果要删除的列在物化视图中是 Value 列，则会与主表一起触发 Light Schema Change；如果主表的 Value 列是物化视图中的 Key 列，则需要发起异步任务，对物化视图进行 Sort/Direct Schema Change。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="解决数据重写问题"><strong>解决数据重写问题</strong><a class="hash-link" href="#解决数据重写问题" title="标题的直接链接">​</a></h2><p>由于 Delete Predicate 绑定了 Rowset，且每个 Rowset 都绑定了Schema，当 Delete Predicate 所涉及的列被删除后，可以通过寻找到对应的 Rowset，Merge 该列的信息进当前的 Schema 中，这样对 Delete Predicate 之前的数据也可以正常过滤。解决了数据中有 Delete Predicate 需要重写数据的问题。</p><p>以上就是 Light Schema Change 功能实现过程中对 Doris 进行的修改，在使用的时候只需在建表的时候指定参数即可打开 Light Schema Change 功能，如下所示：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE IF NOT EXISTS `customer` (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_custkey` int(11) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_name` varchar(26) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_address` varchar(41) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_city` varchar(11) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_nation` varchar(16) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_region` varchar(13) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_phone` varchar(16) NOT NULL COMMENT "",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  `c_mktsegment` varchar(11) NOT NULL COMMENT ""</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DUPLICATE KEY(`c_custkey`)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DISTRIBUTED BY HASH(`c_custkey`) BUCKETS 32</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PROPERTIES (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"replication_num" = "1",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"light_schema_change" = "true"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h1>性能对比</h1><p>为进一步体验 Light Schema Change 的执行效率，我们在 1 FE 1 BE 的集群上对加减列操作分别在有导入任务时和无导入任务时进行了对比。硬件配置为 16C 64G，数据均在 SSD 盘，使用了TPC-H SF100 的 lineitem 表，数据量约74G，具体测试对比如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5ff37d7d45164649aa6049d10197258c~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="无导入任务时">无导入任务时<a class="hash-link" href="#无导入任务时" title="标题的直接链接">​</a></h2><p><strong>加列：</strong></p><ol><li><strong>Hard Linked Schema Change：</strong> 耗时 1s 310ms。</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/81d2bcfd6d9d40929ed61eae760fb5e2~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol start="2"><li><strong>Light Schema Change：</strong> 耗时 7ms</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cb7352ecb8984fe98a7101380a6e6ef2~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>减列:</strong></p><ol><li><strong>Hard Linked Schema Change：</strong> 耗时 1s 438ms</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/39b3073748de4cfe9238ae5642f03314~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol start="2"><li><strong>Light Schema Change：</strong> 耗时 3ms</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/46e15abda4dd41e7b08d569ee4256f8f~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>由上面测试可以看出，Light Schema Change 加减列速度远快于 Hard Linked Schema Change，并且随着 BE 节点和表数据量的增多，Hard Linked Schema Change 的耗时是远高于 Light Schema Change 的，原因是 Light Schema Change 只需要和 FE Master 进行交互，并可以实现同步返回。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="有导入任务时">有导入任务时<a class="hash-link" href="#有导入任务时" title="标题的直接链接">​</a></h2><p><strong>加列</strong></p><ol><li><strong>Hard edLink Schema Change：</strong> 耗时 13 mins</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5231f18d60d64701a82074af28c167c2~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/65a5863f532042fc837bf97a999b589a~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>Light Schema Change：</strong> 耗时 3 ms</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9fff32f459f145638a80ba8d822f387a~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>从上面测试可以看出，同样的加列行为，如果有导入任务，Hard Linked Schema Change 需要等待导入的完成，才可以做 Schema Change，而 Light Schema Change 无需等待毫秒内即可完成导入。</p><p>欢迎读者尝试上手做一下有导入任务时的减列对比测试，也可以做一下在 Schema Change 过程中进行导入的情况下，两种 Schema Change 的速度对比。</p><h1>Flink 结合 Light Schema Change 实现 DDL 同步</h1><p>之前在基于 Apache Doris 和 Apache Flink 构建实时数仓时，当上游数据源发生表结构变更时，Doris 在同步 DDL 操作时主要有以下痛点：</p><ol><li><p>在发起 Schema Change 后为了避免双写对集群产生的压力，通常会选择阻塞上游数据，在等待 Schema Change 操作执行完成后再解除阻塞，这个时候如果遇到一些数据量特别大的表时，往往会造成 Flink 上游数据积压；</p></li><li><p>需要自己处理解析 SQL 语句、发起 Schema Change 及维护执行状态等操作</p></li><li><p>修改 Schema 后，需要同步修改 Flink 中 Schema 相关的参数并重启 Job</p></li></ol><p>而在 1.2.0 新版本实现 Light Schema Change 后，DDL 的同步就变得非常简易。利用 Flink CDC 的 DataStream API，可获取到上游业务数据库的 DDL 变更记录，在 Doris 对应数据表中开启 Light Schema Change，即可实现 DDL 自动同步。核心步骤如下：</p><ol><li><p>在 CDC Source 中开启 DDL 变更同步；</p></li><li><p>在 Doris Sink 中对上游数据进行判断，识别 DDL 操作(add/drop column)并解析；</p></li><li><p>对 Table 进行校验，判断是否可以进行 Light Schema Change 操作；</p></li><li><p>对 Table 发起 Schema Change 操作。</p></li></ol><p>Light Schema Change 可以保证 DDL 在毫秒级执行完成，避免了双写以及阻塞数据的问题；</p><p>同时 Flink Doris Connector 封装了序列化类 JsonDebeziumSchemaSerializer，在作业启动的时候，只需指定序列化方式即可，无需关心 Schema Change 的底层逻辑，以及无需重启Job。</p><h1>总结</h1><p>通过 Light Schema Change ，使得 Apache Doris 在面对上游数据表维度变化时，可以更加快速稳定实现表结构同步，保证系统的高效且平稳运转，具体体现在：</p><ol><li><p>执行效率提升明显，且存在导入任务时效率提升更为显著。Light Schema Change 无需对 Tablet 双写，无需等待导入任务完成。在相同集群下，相较于过去版本，Schema Change 效率从数秒或数分钟提升至数毫秒，极大幅度提升了执行效率。</p></li><li><p>作业流程更加简单，加减列只需修改 FE 元数据，不需要与 BE 进行交互，实现同步返回，避免较长等待时间，以及因异步长时间执行而可能导致的误操作行为，提升了系统容错性和稳健性。</p></li><li><p>Flink CDC 结合 Light Schema Change 快速实时同步 DDL ，有效解决了双写以及阻塞数据等问题，避免了增删列需要修改程序且需要停服重启的操作，实现 DDL 毫秒级快速同步，进一步提升了实时数仓数据处理和分析链路的时效性与便捷性。</p></li></ol><p>Apache Doris 1.2.0 版本即将发布，如果想体验最新特性欢迎大家扫码加入下方社群中，如有任何问题/建议可通过下方论坛进行反馈，社区专家将帮助你更快定位和解决问题。</p><p>GitHub 论坛：<a href="https://github.com/apache/doris/discussions/12134" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/discussions/12134</a></p>]]></content:encoded>
            <category>技术分享</category>
        </item>
        <item>
            <title><![CDATA[查询性能显著提升，Apache Doris 向量化版本在小米A/B实验场景的调优实践]]></title>
            <link>https://selectdb.com/blog/查询性能显著提升，Apache Doris 向量化版本在小米A:B实验场景的调优实践</link>
            <guid>/查询性能显著提升，Apache Doris 向量化版本在小米A:B实验场景的调优实践</guid>
            <pubDate>Thu, 10 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读： 长期以来，Apache Doris在小米集团都有着广泛的应用。随着小米互联网业务的快速发展，用户对Apache Doris的查询性能提出了更高的要求，Doris 向量化版本在小米内部上线已经迫在眉睫。在 SelectDB 公司和 Apache Doris 社区的鼎力支持下，我们在小米 A/B实验场景对 Doris 1.1.2 向量化版本进行了一系列的调优操作，使得查询性能和稳定性有了显著地提升。]]></description>
            <content:encoded><![CDATA[<blockquote><p><strong>导读：</strong> 长期以来，Apache Doris在小米集团都有着广泛的应用。随着小米互联网业务的快速发展，用户对Apache Doris的查询性能提出了更高的要求，Doris 向量化版本在小米内部上线已经迫在眉睫。在 SelectDB 公司和 Apache Doris 社区的鼎力支持下，我们在小米 A/B实验场景对 Doris 1.1.2 向量化版本进行了一系列的调优操作，使得查询性能和稳定性有了显著地提升。</p></blockquote><h2 class="anchor anchorWithStickyNavbar_LWe7" id="背景">背景<a class="hash-link" href="#背景" title="标题的直接链接">​</a></h2><p>2019 年 9 月，为了满足小米互联网增长分析业务中近实时、多维分析查询的需求，小米集团首次引入了Apache Doris。在过去的三年时间里，Apache Doris 已经在小米内部得到了广泛的应用，支持了集团数据看板、广告投放、广告BI、新零售、用户行为分析、A/B实验平台、天星数科、小米有品、用户画像、小米造车等小米内部数十个业务，并且在小米内部形成了一套以 Apache Doris 为核心的数据生态 <strong>。</strong> 小米集团作为 Apache Doris 最早期的用户之一，一直深度参与社区建设，参与 Apache Doris 的稳定性打磨。</p><p>为了保证线上服务的稳定性，小米内部基于 Apache Doris 社区的 0.13 版本进行迭代，为小米的业务提供稳定的报表分析和 BI看板服务，经过业务的长时间打磨，内部 Doris 0.13 版本已经非常稳定。但是，随着小米互联网业务的发展，用户对 Doris 的查询性能提出了更高的要求，Doris 0.13 版本在某些场景下逐渐难以满足业务需求了。与此同时，Apache Doris 社区在快速发展，社区发布的 1.1 版本已经在计算层和存储层全面支持了向量化，查询性能相比非向量化版本有了明显地提升，基于此，小米内部的 Apache Doris 集群进行向量化版本升级势在必行。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="场景介绍">场景介绍<a class="hash-link" href="#场景介绍" title="标题的直接链接">​</a></h2><p>小米的 A/B实验平台对 Doris 查询性能的提升有着迫切的需求，因此我们选择优先在小米的 A/B实验平台上线 Apache Doris 向量化版本，也就是 1.1.2 版本。</p><p>小米的A/B实验平台是一款通过 A/B测试的方式，借助实验分组、流量拆分与科学评估等手段来辅助完成科学的业务决策，最终实现业务增长的一款运营工具产品。在实际业务中，为了验证一个新策略的效果，通常需要准备原策略A 和新策略B 两种方案。 随后在总体用户中取出一小部分，将这部分用户完全随机地分在两个组中，使两组用户在统计角度无差别。将原策略A和新策略B分别展示给不同的用户组，一段时间后，结合统计方法分析数据，得到两种策略生效后指标的变化结果，并以此来判断新策略B 是否符合预期。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61b2f087bd1d46ecb083d5baa8e21b14~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图1-小米的A/B实验简介</p><p>小米的A/B实验平台有几类典型的查询应用：用户去重、指标求和、实验协方差计算等，查询类型会涉及较多的 Count(distinct)、Bitmap计算、Like语句等。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="上线前验证">上线前验证<a class="hash-link" href="#上线前验证" title="标题的直接链接">​</a></h2><p>我们基于 Doris 1.1.2 版本搭建了一个和小米线上 Doris 0.13 版本在机器配置和机器规模上完全相同的测试集群，用于向量化版本上线前的验证。验证测试分为两个方面：<strong>单 SQL 串行查询测试和批量 SQL 并发查询测试</strong>。在这两种测试中，我们在保证两个集群数据完全相同的条件下，分别在 Doris 1.1.2 测试集群和小米线上 Doris 0.13 集群执行相同的查询 SQL 来做性能对比。<strong>我们的目标是，Doris 1.1.2 版本在小米线上 Doris 0.13 版本的基础上有 1 倍的查询性能提升。</strong></p><p>两个集群配置完全相同，具体配置信息如下：</p><ul><li><p>集群规模：3 FE + 89 BE</p></li><li><p>BE节点CPU: Intel(R) Xeon(R) Silver 4216 CPU @ 2.10GHz 16核 32线程 × 2</p></li><li><p>BE节点内存：256GB</p></li><li><p>BE节点磁盘：7.3TB × 12 HDD</p></li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="单sql串行查询测试">单SQL串行查询测试<a class="hash-link" href="#单sql串行查询测试" title="标题的直接链接">​</a></h3><p>在该测试场景中，我们选取了小米A/B 实验场景中 7 个典型的查询 Case，针对每一个查询 Case，我们将扫描的数据时间范围分别限制为 1 天、7 天和 20 天进行查询测试，其中单日分区数据量级大约为 31 亿（数据量大约 2 TB），测试结果如图所示：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4c2efd0e3dd948978277679f70885af7~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图2-单日分区查询耗时</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b4fa02ab3d54b4fbc6611483a7ade71~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图3-7日分区查询耗时</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d4014982c06445a78f252db32289d824~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图4-20日分区查询耗时</p><p>根据以上小米A/B 实验场景下的单SQL串行查询测试结果所示，Doris 1.1.2 版本相比小米线上Doris 0.13版本至少有 3~5 倍的性能提升，效果显著，提升效果远高于预期。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="批量-sql-并发查询测试">批量 SQL 并发查询测试<a class="hash-link" href="#批量-sql-并发查询测试" title="标题的直接链接">​</a></h3><p>在并发测试中，我们将小米A/B 实验场景的查询 SQL 按照正常的业务并发分别提交到 Doris 1.1.2 测试集群和小米线上 Doris 0.13 集群，对比观察两个集群的状态和查询延迟。测试结果为，在完全相同的机器规模、机器配置和查询场景下，Doris 1.1.2 版本的查询延迟相比线上 Doris 0.13 版本整体上升了 1 倍，查询性能下降非常明显，另外，Doris 1.1.2 版本稳定性方面也存在比较严重的问题，查询过程中会有大量的查询报错。Doris 1.1.2 版本在小米A/B 实验场景并发查询测试的结果与我们的预期差别较大。并发查询测试过程中，我们遇到了几个比较严重的问题：</p><ul><li>CPU使用率上不去</li></ul><p>查询下发到 Doris 1.1.2 版本所在的集群，CPU 使用率最多只能打到 50% 左右，但是完全相同的一批查询下发到线上 Doris 0.13 版本的集群，CPU使用率可以打到接近 100%。因此推测 Doris 1.1.2 版本在小米 A/B 实验场景中将机器的 CPU 利用不起来造成了查询性能大幅度降低。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f80e307968db4d9a9485dbce69aec0c5~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图5-Doris 1.1.2版本和Doris 0.13版本CPU使用率对比</p><ul><li>查询持续报错</li></ul><p>用户并发提交查询的时候会出现如下报错，后续的查询任务均无法执行，集群完全处于不可用的状态，只有重启 BE 节点才能恢复。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">RpcException, msg: timeout when waiting for send fragments RPC. Wait(sec): 5, host: 10.142.86.26</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>用户提交查询的时候也会频繁出现如下报错：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">detailMessage = failed to initialize storage reader. tablet=440712.1030396814.29476aaa20a4795e-b4dbf9ac52ee56be, res=-214, backend=10.118.49.24</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>Like 语句查询较慢</li></ul><p>在小米 A/B实验场景有较多的使用 Like 语句进行字符串模糊匹配的查询，在并发测试过程中，该类查询普遍性能较低。</p><ul><li>内存拷贝耗时较长</li></ul><p>并发查询测试过程中，SQL 整体执行较慢，通过抓取查询过程中的 CPU 火焰图，发现读取字符串类型数据的时候内存拷贝会占用较多时间。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dcfea26ac13c4c489e46bc41edb3459c~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图6-CPU火焰图</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="调优实践">调优实践<a class="hash-link" href="#调优实践" title="标题的直接链接">​</a></h2><p>为了解决 Doris 1.1.2 版本在小米 A/B实验场景并发测试过程中暴露出的性能和稳定性问题，推动 Doris 向量化版本尽快在小米 A/B实验平台上线，我们和 SelectDB 公司以及 Apache Doris 社区一起对 Doris 1.1.2 版本进行了一系列的调优工作。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="提升-cpu-使用率">提升 CPU 使用率<a class="hash-link" href="#提升-cpu-使用率" title="标题的直接链接">​</a></h3><p>针对并发查询时 CPU 使用率上不去的问题，我们截取了查询过程中BE进程的函数调用栈，通过分析发现，有较多的内存分配和释放操作在等锁，这可能会造成 CPU 使用率上不去。</p><ul><li>函数调用栈</li></ul><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#0  sys_futex (v3=0, a2=0x0, t=0x7f786c9e7a00, v=&lt;optimized out&gt;, o=128, a=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at /root/doris/doris/be/src/gutil/linux_syscall_support.h:2419</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#1  SpinLockDelay (loop=1822369984, value=2, w=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at /root/doris/doris/be/src/gutil/spinlock_linux-inl.h:80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#2  base::internal::SpinLockDelay (w=w@entry=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;, value=2, loop=loop@entry=20) at /root/doris/doris/be/src/gutil/spinlock_linux-inl.h:68</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#3  0x000056044cfd825d in SpinLock::SlowLock (this=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at src/base/spinlock.cc:118</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#4  0x000056044f013a25 in Lock (this=&lt;optimized out&gt;) at src/base/spinlock.h:69</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#5  SpinLockHolder (l=&lt;optimized out&gt;, this=0x7f786c9e7a90) at src/base/spinlock.h:124</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#6  (anonymous namespace)::do_malloc_pages(tcmalloc::ThreadCache*, unsigned long) () at src/tcmalloc.cc:1360</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">#0  sys_futex (v3=0, a2=0x0, t=0x7f7494858b20, v=&lt;optimized out&gt;, o=128, a=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at /root/doris/doris/be/src/gutil/linux_syscall_support.h:2419</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#1  SpinLockDelay (loop=-1803179840, value=2, w=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at /root/doris/doris/be/src/gutil/spinlock_linux-inl.h:80</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#2  base::internal::SpinLockDelay (w=w@entry=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;, value=2, loop=loop@entry=2) at /root/doris/doris/be/src/gutil/spinlock_linux-inl.h:68</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#3  0x000056044cfd825d in SpinLock::SlowLock (this=0x560451827c48 &lt;tcmalloc::Static::pageheap_lock_&gt;) at src/base/spinlock.cc:118</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#4  0x000056044f01480d in Lock (this=&lt;optimized out&gt;) at src/base/spinlock.h:69</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#5  SpinLockHolder (l=&lt;optimized out&gt;, this=0x7f7494858bb0) at src/base/spinlock.h:124</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#6  (anonymous namespace)::do_free_pages(tcmalloc::Span*, void*) [clone .constprop.0] () at src/tcmalloc.cc:1435</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li>Doris 内存管理机制</li></ul><p>Doris 中使用 TCMalloc 进行内存管理。根据所分配和释放内存的大小，TCMalloc 将内存分配策略分为小内存管理和大内存管理两类。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/53147d69f99643c28261d87b5e437776~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图7-TCMalloc内存管理机制</p><p>（1）小内存管理</p><p>TCMalloc 使用了 ThreadCache、CentralCache 和 PageHeap 三层缓存来管理小内存的分配和释放。</p><p>对于每个线程，TCMalloc 都为其单独维护了一个 ThreadCache，每个 ThreadCache 中包含了多个单独的 FreeList，每个 FreeList 中缓存了 N 个固定大小的可供分配的内存单元。进行小内存分配时，会直接从 ThreadCache 中进行内存分配，相应地，小内存的回收也是将空闲内存重新放回 ThreadCache 中对应的 FreeList 中。由于每个线程都有自己独立的 ThreadCache，因此从 ThreadCache 中分配或回收内存是不需要加锁的，可以提升内存管理效率。</p><p>内存分配时，如果 ThreadCache 中对应的 FreeList 为空，则需要从 CertralCache 中获取内存来补充自身的 FreeList。CentralCache 中维护了多个 CentralFreeList 链表来缓存不同大小的空闲内存，供各线程的 ThreadCache 取用。由于 CentralCache 是所有线程共用的，因此 ThreadCache 从 CentralCache 中取用或放回内存时是需要加锁的。为了减小锁操作的开销，ThreadCache 一般从 CentralCache 中一次性申请或放回多个空闲内存单元。</p><p>当 CentralCache 中对应的 CentralFreeList 为空时，CentralCache 会向 PageHeap 申请一块内存，并将其拆分成一系列小的内存单元，添加到对应的 CentralFreeList 中。PageHeap 用来处理向操作系统申请或释放内存相关的操作，并提供了一层缓存。PageHeap 中的缓存部分会以 Page 为单位、并将不同数量的 Page 组合成不同大小的 Span，分别存储在不同的 SpanList 中，过大的 Span 会存储在一个 SpanSet 中。CentralCache 从 PageHeap 中获取的内存可能来自 PageHeap 的缓存，也可能是来自 PageHeap 向系统申请的新内存。</p><p>（2）大内存管理</p><p>大内存的分配和释放直接通过 PageHeap 来实现，分配的内存可能来自 PageHeap 的缓存，也可能来自 PageHeap 向系统申请的新内存。PageHeap 向系统申请或释放内存时需要加锁。</p><p>TCMalloc 中的 <code>aggressive_memory_decommit </code>参数用来配置是否会积极释放内存给操作系统。当设置为<code> true </code>时，PageHeap 会积极地将空闲内存释放给操作系统，节约系统内存；当该配置设置为 <code>false </code>时，PageHeap 会更多地将空闲内存进行缓存，可以提升内存分配效率，不过会占用更多的系统内存；在 Doris 中该参数默认为 <code>true</code>。</p><p>通过分析查询过程中的调用栈发现，有比较多的线程卡在 PageHeap 向系统申请或释放内存的等锁阶段，因此，我们尝试将<code> aggressive_memory_decommit </code>参数设为<code>false</code>，让 PageHeap 对空闲内存进行更多的缓存。果然，调整完成之后，CPU 使用率可以打到几乎 100%。在 Doris 1.1.2 版本，数据在内存中采用列式存储，因此，会相比于 Doris 0.13 版本行存的方式有更大的内存管理开销。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0b20d82371cf4a49ae146ad9f44ebf07~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图8-调优后Doris 1.1.2测试集群的CPU使用率</p><p>社区相关的PR：</p><p><a href="https://github.com/apache/doris/pull/12427" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/12427</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="缓解-fe-下发-fragment-超时的问题">缓解 FE 下发 Fragment 超时的问题<a class="hash-link" href="#缓解-fe-下发-fragment-超时的问题" title="标题的直接链接">​</a></h3><p>在 Doris 1.1.2 版本，如果一个查询任务的 Fragment 数量超过一个，查询计划就会采用两阶段执行(<code>Two Phase Execution</code>)策略。在第一阶段，FE 会下发所有的 Fragment 到 BE 节点，在 BE 上对 Fragment 执行相应的准备工作，确保 Fragment 已经准备好处理数据；当 Fragment 完成准备工作，线程就会进入休眠状态。在第二阶段，FE 会再次通过 RPC 向 BE 下发执行 Fragment 的指令，BE 收到执行 Fragment 的指令后，会唤醒正在休眠的的线程，正式执行查询计划。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">RpcException, msg: timeout when waiting for send fragments RPC. Wait(sec): 5, host: 10.142.86.26</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在用户执行查询时，会持续有上面的报错，并导致任何查询无法执行。通过截取进程的调用栈，分析发现大量的线程均在休眠状态，均阻塞在 Fragment 完成准备工作并休眠等待被唤醒的状态。排查发现，查询计划的两阶段执行机制中存在 Bug，如果执行计划被FE取消，BE 上已经完成 Fragment 准备工作并休眠等待的线程就不会被唤醒，导致 BE 上的 Fragment 线程池被耗尽，后续所有查询任务的 Fragment 下发到 BE 节点之后，因为没有线程资源都会等待直到 RPC 超时。</p><p>为了解决这个问题，我们从社区引入了相关的修复 Patch，为休眠的线程增加了超时唤醒机制，如果线程被超时唤醒，Fragment 会被取消，进而释放线程资源，极大地缓解了 FE 下发执行计划时 RPC 超时的问题。</p><p>该问题还未完全解决，当查询并发很大时还会偶发地出现。另外，我们还引入了 Doris 社区相关的其他 Patch 来缓解该问题，比如：减小执行计划的 Thrift Size，以及使用池化的 RPC Stub 替换单一的 RPC Stub 。</p><p>社区相关的PR如下：</p><p><a href="https://github.com/apache/doris/pull/12392" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/12392</a></p><p><a href="https://github.com/apache/doris/pull/12495" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/12495</a></p><p><a href="https://github.com/apache/doris/pull/12459" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/12459</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="修复-tablet-元数据汇报的-bug">修复 Tablet 元数据汇报的 Bug<a class="hash-link" href="#修复-tablet-元数据汇报的-bug" title="标题的直接链接">​</a></h3><p>在 Doris 中，BE 会周期性地检查当前节点上所有 Tablet 是否存在版本缺失，并向 FE 汇报所有 Tablet 的状态和元信息，由 FE 对每一个 Tablet 的三副本进行对比，确认其中的异常副本，并下发 Clone 任务，通过 Clone 正常副本的数据文件来恢复异常副本缺失的版本。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">detailMessage = failed to initialize storage reader. tablet=440712.1030396814.29476aaa20a4795e-b4dbf9ac52ee56be, res=-214, backend=10.118.49.24</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在该报错信息中，错误代码<code>res=-214 </code>（OLAP_ERR_VERSION_NOT_EXIST）表示查询计划执行过程中在 BE 上初始化 Rowset Reader 的时候出现异常，对应的数据版本不存在。在正常情况下，如果 Tablet 的某一个副本存在版本缺失，FE 生成执行计划的时候就不会让查询落在该副本上，然而，查询计划在 BE 上执行的过程中却发现版本不存在，则说明 FE 并没有检测到该副本存在版本缺失。</p><p>通过排查代码发现，BE 的 Tablet 汇报机制存在 Bug，当某一个副本存在版本缺失时，BE 并没有将这种情况正常汇报给 FE，导致这些存在版本缺失的异常副本并没有被 FE 检测到，因此不会下发副本修复任务，最终导致查询过程中会发生<code>res=-214</code>的报错。</p><p>社区相关的 PR 如下：</p><p><a href="https://github.com/apache/doris/pull/12415" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/12415</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="优化-like-语句性能">优化 Like 语句性能<a class="hash-link" href="#优化-like-语句性能" title="标题的直接链接">​</a></h3><p>在 Doris 1.1.2 版本中使用 Like 语句进行字符串模糊匹配查询时，Doris 底层其实是使用了标准库中的<code>std::search()函数</code>对存储层读出的数据进行逐行匹配，过滤掉不满足要求的数据行，完成 Like 语句的模糊匹配。通过调研和对比测试发现，GLIBC 库中的<code>std::strstr()函数</code>针对字符串匹配比<code>std::search()函数</code>有 1 倍以上的性能提升。最终我们使用<code>std::strstr()函数</code>作为 Doris 底层的字符串匹配算法，将 Doris 底层字符串匹配的性能可以提升 1 倍。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="优化内存拷贝">优化内存拷贝<a class="hash-link" href="#优化内存拷贝" title="标题的直接链接">​</a></h3><p>在小米的场景中有很多字符串类型的查询字段，Doris 1.1.2 版本使用 ColumnString 对象来存储内存中的一列字符串数据，底层使用了 PODArray 结构来实际存储字符串。执行查询时，需要从存储层逐行读取字符串数据，在这个过程中需要多次对 PODArray 执行 Resize 操作来为列数据申请更大的存储空间，执行 Resize 操作会引起对已经读取的字符串数据执行内存拷贝，而查询过程中的内存拷贝非常耗时，对查询性能影响极大。</p><p>为了降低字符串查询过程中内存拷贝的开销，我们需要尽量减少对 PODArray 执行 Resize 操作的次数。鉴于小米 A/B实验场景中同一列不同行的字符串长度相对比较均匀，我们尝试预先为需要读取的字符串申请足够的内存来减少 Resize 的次数，进而降低内存拷贝的开销。在数据扫描时，每个 Batch 需要读取的数据行数是确定的（假设为 n），当字符串数据读取完指定的前 m（在小米的场景中，该值配置为100，m &lt; n）行时，我们根据前 m 行的 PODArray 大小预估所有 n 行字符串数据需要的 PODArray 大小，并为其提前申请内存，避免后面逐行读取时多次执行内存申请和内存拷贝。</p><p>内存预估公式为：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">所需PODArray总大小 = （当前PODArray总大小 / m）* n</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38af90922d24437b92066182b80815fd~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图9-优化内存拷贝开销</p><p>当然，该方法只是对所需的内存进行了预估，根据预估的大小提前申请了内存，减少了后面逐行读取字符串时大量的 Resize 操作，减少了内存申请和内存拷贝的次数，并不能完全消除字符串读取过程中的内存拷贝。该优化方案只对一列中字符串长度比较均匀的情况有效，内存的预估相对会比较接近实际内存。如果一列中字符串长度差别较大，该方法的效果可能不甚明显，甚至可能会造成内存浪费。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="调优测试结果">调优测试结果<a class="hash-link" href="#调优测试结果" title="标题的直接链接">​</a></h2><p>我们基于小米的 A/B实验场景对 Doris 1.1.2 版本进行了一系列调优，并将调优后的 Doris 1.1.2 版本与小米线上 Doris 0.13 版本分别进行了并发查询测试。测试情况如下：</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="测试1">测试1<a class="hash-link" href="#测试1" title="标题的直接链接">​</a></h3><p>我们选择了 A/B 实验场景中一批典型的用户去重、指标求和以及协方差计算的查询 Case（SQL 总数量为 3245）对两个版本进行并发查询测试，测试表的单日分区数据大约为 31 亿（数据量大约 2 TB），查询的数据范围会覆盖最近一周的分区。测试结果如图所示，Doris 1.1.2 版本相比 Doris0.13版本，总体的平均延迟降低了大约 48%，P95 延迟降低了大约 49%。在该测试中，Doris 1.1.2 版本相比 Doris0.13 版本的查询性能提升了接近 1 倍。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/dedc099ed3c040518ae71e67cff11793~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图10-查询平均延迟和P95延迟</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="测试2">测试2<a class="hash-link" href="#测试2" title="标题的直接链接">​</a></h3><p>我们选择了 A/B实验场景下的 7 份 A/B 实验报告对两个版本进行测试，每份 A/B 实验报告对应小米 A/B实验平台页面的两个模块，每个模块对应数百或数千条查询 SQL。每一份实验报告都以相同的并发向两个版本所在的集群提交查询任务。测试结果如图所示，Doris 1.1.2 版本相比 Doris 0.13 版本，总体的平均延迟降低了大约 52%。在该测试中，Doris 1.1.2 版本相比 Doris 0.13 版本的查询性能提升了超过 1 倍。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b10840d1e8542d79b92290fe65ba34d~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图11-查询平均延迟</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="测试3">测试3<a class="hash-link" href="#测试3" title="标题的直接链接">​</a></h3><p>为了验证调优后的 Doris 1.1.2 版本在小米 A/B 实验场景之外的性能表现，我们选取了小米用户行为分析场景进行了 Doris 1.1.2 版本和 Doris 0.13 版本的并发查询性能测试。我们选取了 2022年10月24日、25日、26日和 27日这 4 天的小米线上真实的行为分析查询 Case 进行对比查询，测试结果如图所示，Doris 1.1.2 版本相比 Doris 0.13 版本，总体的平均延迟降低了大约7 7%，P95 延迟降低了大约 83%。在该测试中，Doris 1.1.2 版本相比 Doris 0.13 版本的查询性能有 4~6 倍的提升。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c2978f37b3048869b91e1b1c5aea465~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>图12-查询平均延迟和P95延迟</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="结束语">结束语<a class="hash-link" href="#结束语" title="标题的直接链接">​</a></h2><p>经过一个多月的性能调优和测试，Apache Doris 1.1.2 版本在查询性能和稳定性方面已经达到了小米 A/B实验平台的上线要求，在某些场景下的查询性能甚至超过了我们的预期，希望本次分享可以给有需要的朋友一些可借鉴的经验参考。</p><p>最后，感谢 SelectDB 公司和 Apache Doris 社区对我们的鼎力支持，感谢衣国垒老师在我们版本调优和测试过程中的全程参与和陪伴。Apache Doris 目前已经在小米集团内部得到了广泛地应用，并且业务还再持续增长，未来一段时间我们将逐步推动小米内部其他的 Apache Doris 业务上线向量化版本。</p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[人群圈选效率提升 30 倍，云积互动基于 Apache Doris 构建统一数仓的实践]]></title>
            <link>https://selectdb.com/blog/人群圈选效率提升 30 倍，云积互动基于 Apache Doris 构建统一数仓的实践</link>
            <guid>/人群圈选效率提升 30 倍，云积互动基于 Apache Doris 构建统一数仓的实践</guid>
            <pubDate>Wed, 26 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读： 随着业务量快速增长，云积互动对数据的实时性及灵活性提出更高要求，早期基于 CDH 的大数据平台已无法满足当前难度以及复杂度较高的的业务需求，因此云积互动于 2021 引进 Apache Doris 在部分业务中使用，并在使用过程中逐渐发掘出 Apache Doris 更多强大之处以及优势，最终决定在 2022 年全面应用 Apache Doris ，基于 Apache Doris 来构建云积互动企业级实时离线统一数仓。]]></description>
            <content:encoded><![CDATA[<p><strong>导读：</strong> 随着业务量快速增长，云积互动对数据的实时性及灵活性提出更高要求，早期基于 CDH 的大数据平台已无法满足当前难度以及复杂度较高的的业务需求，因此云积互动于 2021 引进 Apache Doris 在部分业务中使用，并在使用过程中逐渐发掘出 Apache Doris 更多强大之处以及优势，最终决定在 2022 年全面应用 Apache Doris ，基于 Apache Doris 来构建云积互动企业级实时离线统一数仓。</p><h1>业务背景</h1><p>云积互动，全称深圳市云积分科技有限公司，成立于 2014 年，是国内领先的 AI 驱动的消费者运营服务提供商，致力于发展消费者运营相关理论、技术、算法、模型及软件工具，为全球消费性企业提供基于 AI 的消费者运营系统及运营策略服务，打造消费者运营领域最佳服务和实践标准，帮助企业构建消费者运营核心能力，以应对当前及未来的场景化运营挑战。目前已成为天猫、京东、抖音、腾讯等主流电商和社交平台深度合作伙伴，服务客户 2300+，其中世界 500 强客户超过 18 家，包括全球第一的美妆、日化、医药集团，均深度服务超过 7 年。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务需求">业务需求<a class="hash-link" href="#业务需求" title="标题的直接链接">​</a></h2><p>云积互动的主要业务是以消费者运营为核心，包含了会员通，CRM，策略营销，数据资产等一系列业务，如下图所示。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/262f64ce0ea94947940a8d2090185c63~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p>早期云积互动的大数据需求较少，起步也比较晚，2019 年才开始搭建基于 CDH 的大数据平台，因此大数据平台的主要目的是为了满足早期较为单一的 BI 数据看板及报表功能。近年来，随着业务量快速增长，数据量的增长，业务对数据的实时性及灵活性提出更高的要求，大数据平台也从早期的只需要满足单一的 BI 服务需求，扩展到需要支持各业务线，包含圈人服务，人群分析，AI 智能数据等多种业务需求。早期基于 CDH 的大数据平台已无法满足当前难度以及复杂度较高的的业务需求。</p><h1>大数据平台的迭代</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="早期数仓架构">早期数仓架构<a class="hash-link" href="#早期数仓架构" title="标题的直接链接">​</a></h2><p>早期公司业务量较少，基于 Hive+Spark 构建的离线数仓即可满足早期大数据的需求。早期架构主要用于支持 BI 相关功能，数据大屏，自助报表等应用，大部分的指标仅要求 T+1 的指标。</p><p>下图为云积互动早期数仓架构，早期的数据源主要为业务数据库 MySQL 以及日志，数据通过 Streamsets 实时采集数据并经 ETL 后传入 ODS 层，存储到 Kudu 中，通过 Impala 对 ODS 层的数据进行处理，实现实时查询业务的需求。通过 Hive 构建了离线数仓的 DWD、DWS 以及 DIM 层，使用 Spark 进行离线任务的计算与调度，最终处理并计算完成的数据输出到 MySQL 和 Kylin 中，应用于上层业务应用及分析。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cb0cbb5c6d09405da93258d3efa69a9f~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><strong>存在的问题</strong></p><ul><li>查询效率低：使用 Impala 多表查询速度太慢，亿级别表 Join 时，查询时间基本上在 3 分钟以上，部分复杂查询会在超过 3 分钟左右判定超时；同时使用 Impala 并行查询对内存消耗较大，影响其他任务运行。</li><li>存储成本太高：使用多个系统存储数据（ Hive，Hbase，Kudu），存储成本较高，随着业务量的增长，数据量指数级的增多，存储成本更是成倍数增加。</li><li>开发难度大：数仓开发基于代码，不能满足灵活的指标需求，当分析需求越来越多时，存在多个场景组合查询、自定义等查询场景，面对这样的场景，必须进行再开发，开发和时间成本都很高。</li><li>数据链路长：较长的链路使得数据的一致性很难保证，数据在某一环节出现问题，排查难度高，运维成本也会增加。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="技术选型">技术选型<a class="hash-link" href="#技术选型" title="标题的直接链接">​</a></h2><p>基于业务对数据实时性及灵活性更高的要求，我们在 2021 年初对当前市面上较为流行的分析引擎 ClickHouse 和 Apache Doris 进行了调研，调研中我们发现， Apache Doris 具有高性能、简单易用、实现成本低等诸多优势。基于此，我们决定在部分业务上开始使用 Apache Doris，在使用的过程中逐渐发掘出 Apache Doris 更多强大之处以及优势，Apache Doris 在很多方面十分贴合我们的诉求，因此，我们决定在 2022 年全面应用 Apache Doris 在数据仓库中，基于 Apache Doris 构建云积互动企业级数仓，选择 Apache Doris 的主要原因如下：</p><p><strong>1. 该架构开发效率高，查询性能远高于 Hive。</strong></p><ul><li>数仓 ETL 由原来的 Spark 任务改为 Doris SQL 任务，使用 SQL 开发模式可进行快速迭代，开发效率提升了近一倍。</li><li>Doris 查询支持物化视图索引加速，Doris 在 1.0 版本开始引入了向量化引擎，性能提升 2~3 倍，平均查询耗时降低了 60%。</li></ul><p><strong>2. 该架构对 OLAP 支持更好，支持更为灵活的查询。</strong></p><ul><li>Doris 支持 Cube 函数，实现了 Kylin 的多维计算功能，极大的减少了 SQL 的开发量。</li><li>Doris 支持 Bitmap 类型，可实现人群之间的快速交并差计算并落地新人群。</li></ul><p><strong>3. 支持主键唯一和聚合模型的表，极大的减少了开发难度。</strong></p><ul><li>使用主键唯一模型可做到依据主键数据覆盖，自动实现数据更新功能。</li><li>使用聚合模型的表，减少了 ETL 过程中的 Join 操作，同时解决了上层数据到达时间不一致而导致的数据关联不上的问题。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="新数仓架构">新数仓架构<a class="hash-link" href="#新数仓架构" title="标题的直接链接">​</a></h2><p>最开始使用新的数仓架构主要是要解决圈人速度慢的问题，圈人服务的核心在于人群圈选，通过 SQL 代码或标签取值组合等多种方式，实现人群查找，帮客户找到符合画像的人群，现在各行各业都会设计广告营销场景，其中也包括云积互动，而如何快速准确找到对的人推送广告就成了大数据场景需要解决的问题。当时我们只是在部分功能上使用了 Apache Doris，用 Apache Doris 替代了 Spark+Impala 来实现实时圈人功能，出乎意料的是，<strong>Apache Doris 投入使用之后效果极佳，新版架构的实时圈人业务平均每个任务耗时由 3~5 分钟降低到 10 秒左右，并且在人群落地方面，使用存储更小的 Bitmap 代替原来的人群落地为表，不仅数据管理方便，而且磁盘空间占用减少了 80% 左右。</strong></p><p>除此之外，在一段时间的使用和学习中我们发现 Apache Doris 丰富的功能和核心优势，综上原因，我们产生了用 Apache Doris 数仓替代 Hive 数仓的想法，并迅速的付诸于实践。</p><p>当确定数仓使用 Apache Doris 之后，结合当前的业务需求以及早期架构需要解决的问题，需要将多平台数据打通，构建统一数据口径和数据指标，我们将数据仓库构建分为：ODS 层，DWD 层，DWS 层和 ADS 层，下图为各个分层主要负责的数据类型。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a8426f0768ae4379a9aa3847bc9a11df~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><strong>新数仓的分层逻辑如下：</strong></p><ul><li><strong>ODS 层：</strong> 从业务侧、日志系统和埋点系统等拉取过来的数据，按照原字段名存入 ODS 层。</li><li><strong>DWD 层：</strong> 数据按照维度进行拆分，轻度聚合，将多个平台数据按照同一标准定义进行处理。</li><li><strong>DWS 层：</strong> 主要负责数据的聚合、数据宽表、基于维度的一些计算指标等等。值得注意的是，DWS 层中的部分表使用了 AGGREGATE KEY 模型， AGGREGATE KEY 模型可以提前聚合数据，适合报表和多维度业务，可以有效避免数据汇总时的 Join 操作，部分指标可以使用该表特性实现，无需敲代码，降低了开发成本。</li><li><strong>ADS 层：</strong> 各业务模块根据各自的需求，将数据从 DWS 层汇聚数据指标到 ADS 层。</li></ul><p><strong>新架构设计</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b0d7964d1694425968587231fc58441~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q"></p><p><strong>数据接入：</strong></p><p>业务数据 MySQL 存储在多台 RDS 中，因 Binlog 的留存时间较短，且数据存放于多服务器，同时还进行了分库存储，因此如何接入历史数据以及如何同时接入多个库的数据成了棘手的问题。在调研过程中我们发现 FlinkCDC 可以完美解决上述问题：FlinkCDC 可以在接入历史数据之后自动切换为读取 Binlog，且 2.x 版本已经支持断点续传，支持水平扩展，支持动态添加表。</p><p>日志和埋点数据我们采用 Kafka + Doris Routine Load 导入方式，Routine Load 支持支持用户提交一个常驻的导入任务，通过不断的从指定的数据源读取数据，将数据导入到 Doris 中，支持 Json 解析，并且可以做一些简单的 ETL，极大的减少了代码开发的工作。</p><p><strong>数据加工：</strong></p><p>数据加工采用了 Doris SQL、Insert into 的方式将增量计算完的结果导入到数仓分层中（ODS/DWD/DWS/ADS）。因业务需求对数据的实时性允许存在一点延迟性，因此将 Dolphinscheduler 设置为每 5 分钟调度一次增量 SQL；同时设置数仓每一层错峰执行任务，避免任务堵塞。</p><p>对于数据量比较小的表可以用一个 SQL 完成导入，而对于数据量大的表，为避免 union，需要分成多个 insert into 来执行。但有些大表的逻辑是多个大表的 Join 结果，对于这种场景，我们应用 AGGREGATE KEY 模型的表来解决，利用表的聚合特性来代替 SQL 的 Join 操作。</p><p><strong>任务调度：</strong></p><p>任务调度一直沿用 Dolphinscheduler，页面化的操作简单方便，且对 SQL 的支持友好，整个大数据平台的任务都是通过该调度器完成。目前使用了 2.x 以上的版本，支持使用钉钉报警和邮件报警的功能来监控任务，任务失败将通过钉钉或者邮件发送。</p><p><strong>监控：</strong></p><p>使用 Prometheus+Grafana 对 Doris 集群和 Flink 任务进行监控管理，页面化的监控，极大的减少运维成本。</p><h1>优化方案</h1><ol><li>使用 Flink CDC 启动多个同步任务后，磁盘 IO 飙升导致查询延迟变高。</li></ol><ul><li>对接多个数据源 CDC 任务同步数据时，中间数据写入 Kafka 进行合并和数据消峰，<strong>减少写入 Doris 的任务数。</strong></li><li>调整 DorisSink 写入频率，<strong>控制每批次数据量</strong>。</li><li>优化部分表的分区分桶，<strong>降低数据分片数量</strong>。</li></ul><ol start="2"><li>Doris 1.1.0-rc05 版本偶发后台合并线程持续合并已删除的 Tablet, 合并持续失败且数据版本最多的那个 Tablet 的版本数量升高至 150 左右。</li></ol><ul><li><p>使用元数据管理工具 meta_tool 删除已失效的 Tablet 元数据，<strong>版本数量显著下降，稳定在 30 左右</strong>。</p></li><li><p>对大表的超过两年的数据做冷备份，<strong>减少大表的 Tablet 数</strong>，降低整个 Doris 集群对 Tablet 的管理压力。</p></li></ul><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2cd37ebe7c44812bfbf05e2f53b4cb1~tplv-k3u1fbpfcp-zoom-1.image" alt="图片" class="img_ev3q">3. Bitmap 存储散列用户 ID，使用 Bitmap 相关函数计算时性能较差。</p><ul><li>用户唯一 ID 通过字符串转换生成，基数大且非常稀疏；使用全局字典生成紧凑的 ID 代替，优化后性能提高近五倍。</li></ul><h1>总结与收益</h1><ul><li>Apache Doris 构建的离线+实时数仓一体化，采用 SQL 开发，并用 Dolphinscheduler 一键部署调度，<strong>极大的降低开发难度和开发工作量</strong>，可进行快速迭代以满足目前行业日益增长的数据需求。</li><li>新架构采用 Flink+Doris 的架构体系，FlinkCDC+StreamLoad 可以做到流批一体化数据接入，减少了组件的使用，解决了数据的冗余存储，<strong>服务器资源节省了 30%</strong> ，<strong>数据存储磁盘占用减少 40%</strong> ，同时组件的运维成本大大减少。</li><li>Doris 的易用性极高，支持 MySQL 协议和标准 SQL，各业务线均可通过查询 MySQL 的方式进行数据查询，极大的减少了学习成本。</li><li>从 2021 年 Apache Doris 上线云积互动的第一个业务至今，<strong>Apache Doris 在云积互动内部已成为大数据服务的基础</strong>，承担了包括人群分析、报表查询、指标计算等场景下的在线/离线需求，在较小的集群规模下支持了每天近 2 万次的用户在线分析查询。</li></ul><h1>未来规划</h1><p>目前新的数据仓库已经建设完成，基于 Apache Doris 较多优异特性以及与业务需求较高吻合性，当前团队已经在着手搭建基于 Apache Doris 的数据质量管理和数据血缘，后续我们计划基于 Apache Doris 搭建数据指标体系。下面简单分享一下我们在数据质量管理和数据管理的实现想法。</p><p><strong>1. 数据质量管理：</strong> 在 Apache Doris 的 ODS 层建表时设定一些非空主键，这些字段都是业务逻辑上的必须字段，当数据接入会给定一些默认值，这样就可以清晰的分类出这些数据，在质量分析中进行输出；在 ETL 中也会存在一些逻辑错误数据，这类数据会通过定时的 Doris SQL 脚本进行输出，同时也可以反馈到业务侧进行数据修复。<strong>2. 数据血缘：</strong> 依托 Doris 提供的 SQL 审计功能，使用采集工具 Filebeat/Logstash 持续采集审计日志发送到 Kafka，使用开源的 SQL 解析工具或者抽取 Doris 的 SQL 解析模块针对 DDL 或者 DML 进行解析，解析后的数据存入图数据库或者关系型数据库供业务端展示；该功能的实现对于数据问题排查、数据资产管理均有意义。</p><p>围绕着 Apache Doris 为核心的数据平台建设目前也在一直迭代发展，当然在使用中也发现了该产品的一些需要优化的地方，但不可否认它优秀的性能和丰富的功能，后续我们也将持续不断地进行优化，将优化方案贡献给 Apache Doris 社区。</p><p><strong>作者介绍：</strong></p><p><strong>王杰</strong>：云积互动大数据团队 leader，负责数据平台研发及数据治理</p><p><strong>蒙磊</strong>：云积互动大数据高级开发，负责数据平台研发和数仓开发</p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[全球第一！新一代云原生实时数仓 SelectDB 登顶 ClickBench 榜单！]]></title>
            <link>https://selectdb.com/blog/全球第一！新一代云原生实时数仓 SelectDB 登顶 ClickBench 榜单！</link>
            <guid>/全球第一！新一代云原生实时数仓 SelectDB 登顶 ClickBench 榜单！</guid>
            <pubDate>Mon, 10 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[近期，在 ClickHouse 发起的分析型数据库性能测试排行榜 ClickBench 中，新一代云原生数仓 SelectDB 强势登顶，性能表现超越一众国内外产品，多项指标排行前列，并在业界最为通用的 c6a.4xlarge, 500gb gp2 机型下排行全球第一！]]></description>
            <content:encoded><![CDATA[<p>近期，在 ClickHouse 发起的分析型数据库性能测试排行榜 ClickBench 中，新一代云原生数仓 SelectDB 强势登顶，性能表现超越一众国内外产品，多项指标排行前列，并在业界最为通用的 c6a.4xlarge, 500gb gp2 机型下排行全球第一！</p><blockquote><p>在 ClickBench 性能排行榜中，测试数据均取自真实生产环境、涵盖数据类型多样、覆盖了即席查询和统计报表等典型场景，能真实反映各大数据库在生产环境中的性能，因此吸引了 Snowflake、Redshift、Athena、Greenplum、Druid 等国际知名数据库的参与。所评测的指标为特定机型下导入相同数据集的时间、所占用的存储空间大小以及执行 SQL 的耗时长短，分别用以衡量 数据导入性能、数据压缩比以及查询性能。所有测试结果中表现最优的一条会成为基线，相同测试项的指标会与基线数据进行对比并得出比值，通过这一比值来体现与行业最优的差距。当有新的测试结果超越原有的基线后，将自动成为新的基线。
就查询性能而言，会分别对每条 SQL 执行 Hot Run 和 Cold Run 来统计时长，即重复执行 3 次 SQL 并取其中耗时最短的一次以及启动并清理内存后直接执行，最终对所有 SQL 的执行耗时与基线的比值进行几何平均，即为最终测试结果。因此 ClickBench 更关注的是数据库在所有测试场景下都有着优异的表现，而非某一个或某几个场景，这使得数据库需要全方位的能力提升。</p></blockquote><p>在本次提交的测试结果中，SelectDB 以强悍的查询性能表现强势登顶。在未进行任何调优的情况下，<strong>SelectDB 查询性能在 c6a.4xlarge, 500gb gp2 同机型的所有产品中位列第一，</strong>Hot Run 和 Cold Run 性能表现分别领先第二位 35% 和 25%。在全部 43 个 SQL 中，有近半数的查询语句 SelectDB 性能表现最优，成为新的性能标杆。与此同时，SelectDB 数据写入效率在同机型所有产品中位列第二，压缩前 70G 数据写入仅耗时 482s，<strong>单节点写入速度超过 140MB/s</strong>，在实现极致查询性能的同时也保证了高效的写入效率。</p><div align="center"><p><img loading="lazy" src="/assets/images/boxcndbSPyyU7hqhnqeSIovtTSe-76573d1a15eacc75d767ab407188b66d.png" width="1641" height="1237" class="img_ev3q"></p></div><p>从查询耗时的角度来看，SelectDB 相较于排行前列的其他产品，总体查询耗时分别降低了 63%、75%、78%、99% 以及 459%，性能大幅领先于同类产品。</p><div align="center"><p><img loading="lazy" src="/assets/images/boxcnJGjMwxMTevp8XDjhaGbagd-9455c0aa087f2e09f28323d6fea6ec6f.png" width="674" height="435" class="img_ev3q"></p></div><p>在汇集了多个不同机型和系统的总榜中（包括全球知名的云数仓 Snowflake、Redshift 等），SelectDB 在所有同类型产品中依旧取得了 Cold Run 查询性能第一，Hot Run 查询性能第二的优异成绩，彰显了强大的性能优势。</p><div align="center"><p><img loading="lazy" src="/assets/images/boxcnBspqwBjAixmqWGzxZ0OTtb-6815f5347bf7a8f1ff6e3b7275e2abde.png" width="1859" height="981" class="img_ev3q"></p></div><div align="center"><p><img loading="lazy" src="/assets/images/boxcnAl0HhLdZTvDlByh5bMnvEh-76573d1a15eacc75d767ab407188b66d.png" width="1641" height="1237" class="img_ev3q"></p></div>而在另一通用机型 c6a.metal, 500gb gp2 的测试结果中，相较于排行前列的其他产品，SelectDB 在 Cold Run 场景下总体查询耗时只有同类产品的 1/4 至 1/10。在同未经过任何调优的情况下，查询性能最高领先同类产品近 11 倍，呈现巨大的领先优势。<div align="center"><p><img loading="lazy" src="/assets/images/boxcna6XZIaMuJnYjclgw7rbTId-3aa45de6d7b164ae51fea67f69d844c8.png" width="610" height="408" class="img_ev3q"></p></div>登顶榜单只是 SelectDB 前进道路上的小目标之一，作为基于 Apache Doris 打造的运行于多云之上的新一代云原生数仓，SelectDB 具备了极速、易用、实时、统一的核心特性，并提供了多云一致体验和多数据类型支持，未来还将有更多重要能力呈现给大家，欢迎大家申请体验测试。<p>当前 SelectDB 已与阿里云、腾讯云、亚马逊云科技等知名云厂商开展了深入合作，后续也将进一步拓宽与全球知名云厂商的合作。另外，11 月我们将推出 SelectDB Cloud 2.0 版本，新的版本会有很多惊喜给大家呈现，也请大家关注和参与我们后续的产品发布会活动~</p><p><strong>这世界就是一拨人在昼夜不停地高速运转，另一拨人起床发现世界变了。</strong>尽管性能不是数据库的全部，但一直是亘古不变的追求方向之一。取得这一显著成果的背后，离不开 SelectDB 技术团队日以继夜的辛勤付出、更离不开站在 Apache Doris 这一巨人的肩膀上。</p><p><strong>因此，所有针对 SelectDB 的性能优化点，后续都将不做半点保留、全部贡献回 Apache Doris 社区</strong>。目前有一部分代码已经合入社区 Master 版本， 还有许多 PR 在陆续合入的过程中。相信在不久后所有社区用户就可以切身体会到性能飞跃带来的极致体验。我们也期待能有更多开发者与开源爱好者能够一同加入 Apache Doris 社区，共襄盛举，将国人开源的优秀项目推广到全球，成为现代数据分析技术的新标杆。</p><div align="center"><p><img loading="lazy" src="/assets/images/boxcndRKrzXYbwSKJRVogfhXahb-79255629ca7654077b973f0d3a03f363.jpeg" width="1080" height="459" class="img_ev3q"></p></div>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[Apache Doris 在橙联的应用实践：数仓架构全面革新，千万数据计算时间从 2 小时变成 3 分钟]]></title>
            <link>https://selectdb.com/blog/Apache Doris 在橙联的应用实践：数仓架构全面革新，千万数据计算时间从 2 小时变成 3 分钟</link>
            <guid>/Apache Doris 在橙联的应用实践：数仓架构全面革新，千万数据计算时间从 2 小时变成 3 分钟</guid>
            <pubDate>Sun, 09 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[作者：付帅，橙联（中国）有限公司数字化团队，大数据研发经理，负责数字化团队数据中台的研发以及 OLAP 引擎的应用落地及性能优化。]]></description>
            <content:encoded><![CDATA[<blockquote><p>作者：付帅，橙联（中国）有限公司数字化团队，大数据研发经理，负责数字化团队数据中台的研发以及 OLAP 引擎的应用落地及性能优化。</p></blockquote><h1>业务背景</h1><p>橙联股份是一家服务全球跨境电商的科技公司，致力于通过市场分析、系统研发及资源整合，为客户提供物流、金融、大数据等多方面的服务产品，为全球跨境电商提供高品质、全方位的服务解决方案。</p><p>随着公司业务的发展和数据的不断增长，早期基于 MySQL 的传统数仓架构已经无法应对公司数据的快速增长。业务的需求和运营的决策对于数据时效性的要求越来越高，对数仓准实时能力的需求越发强烈。为了适应快速的增长需求，橙联于 2022 年正式引入 Apache Doris，以 Apache Doris 为核心构建了新的数仓架构，构建过程中对服务稳定性、查询稳定性、数据同步等多方面进行了优化，同时建立了以 Apache Doris 为核心的数据中台，在这一过程中积累了诸多使用及优化经验，在此分享给大家。</p><h1>数仓架构演进</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="早期数仓架构">早期数仓架构<a class="hash-link" href="#早期数仓架构" title="标题的直接链接">​</a></h2><p>公司在成⽴初期业务量不⼤，数据团队规模⽐较⼩，对数据的需求仅局限于少量 T + 1 定制化报表需求。因此，早期数仓架构十分简单，如下图所示，直接使用 MySQL 构建数仓集市层，使用数仓集市层的数据进行报表开发，基于商业化的报表平台向需求方提供 T + 1 的数据报表服务。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/84bc4cdfa0964a33a71b784687f2d5f5~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>存在的问题</strong></p><ol><li><p>随着公司业务规模扩大、数据量激增以及对数据时效性要求不断提高，使⽤ MySQL 进⾏数据分析越来越不能满⾜业务⽅的要求。</p></li><li><p>没有对数仓进⾏分层划域，烟囱式的开发模式数据复⽤性很差，开发成本⾼，业务⽅提出的需求不能快速得到响应。</p></li><li><p>对数据的质量和元数据的管理缺乏管控。</p></li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="新数仓架构">新数仓架构<a class="hash-link" href="#新数仓架构" title="标题的直接链接">​</a></h2><p>为了解决旧架构日益凸显的问题，适应快速增长的数据和业务需求，今年正式引入 Apache Doris 构建新的数仓架构。</p><p><strong>选择 Apache Doris 的原因：</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ffb93ffe395a40e885772477b292f6d2~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>易⽤性</strong> - 在当前应用场景下，引入新技术，将面临大量报表迁移问题，因此必须要考虑的产品易用性问题，而 Apache Doris 在学习成本、报表迁移成本、服务运维成本上有着非常优秀的表现，具体包括：</p><ol><li>采⽤ MySQL 协议和语法，⽀持标准 SQL，可以通过各类客户端⼯具来访问 Doris，能够与 BI ⼯具⽆缝对接</li><li>⽀持多表 Join，针对不同场景的 Join 提供了多种优化⽅案</li><li>⽣态扩展完善，离线数据的⾼效批量导⼊、流式数据的低延迟实时导⼊都有很好的⽀持</li><li>相较于业界其他热⻔ OLAP 数据库，Doris 的分布式架构⾮常简洁，只有 FE、BE 两个进程，运⾏不依赖任何第三⽅系统</li><li>⽀持弹性伸缩，对于部署、运维⾮常友好</li></ol><p><strong>性能</strong> - 当前报表存在大量降耦聚合操作，对多表关联的查询性能和实时查询的时效性有着十分高的要求，而 Apache Doris 基于 MPP 架构实现，并自带了⾼效的列式存储引擎，可以支持：</p><ol><li>数据的预聚合以及预聚合结果的⾃动更新</li><li>数据的实时更新</li><li>⾼并发查询</li></ol><p>基于以上的原因，最终选择了以 Apache Doris 为核心构建新的数仓。</p><p><strong>架构介绍</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bcbbd668ee7b4cd29f5430a2688f6baf~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>Apache Doris 的数仓架构十分简洁，不依赖 Hadoop 生态组件，构建及运维成本较低。</p><p>如以上架构图所示，我们的数据源共有 4 种，业务数据 MySQL、文件系统 CSV、埋点数据和第三方系统 API；针对不同的需求，使用了不同的数据导入方式，文件数据导入使用 Doris Stream Load，离线数据使用 DataX doriswriter 进行数据初始化，实时增量数据使用 Flink CDC + Flink Doris Connector 的方式进行数据同步；数据存储和计算层使用了 Doris ，在分层设计上采用 ODS(Operation Data Store 数据准备区，也称为贴源层)、 明细层 DWD、中间层 DWM、服务层 DWS、应用层 ADS 的分层思想，ODS 之后的分层数据通过 DolphinScheduler 调度 Doris SQL 进行增量和全量的数据更新。最终上层数据应用使用自研的一站式数据服务平台，可以和 Apache Doris 无缝对接，提供自助报表、自助取数、数据大屏、用户行为分析的数据应用服务。</p><p>基于 Apache Doris 的数仓架构方案可同时支持离线和准实时应用场景，<strong>准实时的 Apache Doris 数仓可以覆盖 80% 以上的业务场景。这套架构大大降低了研发成本，提高了开发效率。</strong></p><p>当然在架构构建过程中也遇到一些问题和挑战，我们针对问题进行了相应的优化。</p><h1>Apache Doris 构建数仓优化方案</h1><p>在数仓的使用过程中，主要遇到三方面问题。首先是服务稳定性问题，其次是查询速度逐渐变慢的问题，最后是 Doris 数据同步和 Doris SQL 调度问题。具体体现在以下：</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="服务稳定性">服务稳定性<a class="hash-link" href="#服务稳定性" title="标题的直接链接">​</a></h2><p><strong>优化前</strong></p><p>在 Apache Doris 使用初期，FE 和 BE 的部署方式如下：</p><ul><li><p>FE 负责元数据的管理、用户请求接入、查询的解析规划，资源占用较低，因此将 FE 和其他大数据组件混合部署 FE<!-- -->*<!-- -->3。</p></li><li><p>BE 负责数据存储、计算、查询计划的执行，资源占用较大，因此 BE 进行独立部署且分配了较多的资源 BE（16C 128G 4T<em>1）</em>7。</p></li></ul><p>基于以上方式部署，使用初期运行的稳定性还不错。然而在使用了一段时间之后，这种部署方式暴露的问题就越来越明显。</p><p><strong>存在的问题</strong></p><ul><li><p>首先，FE 混合部署存在资源竞争。其他组件与 FE 服务竞争资源，导致 FE 资源不足，服务运行稳定性不好。具体问题表现在：每当机器资源使用率打满，就会导致 FE 节点无法连接，长时间获取不到心跳而被 FE 集群判定为离线。</p></li><li><p>其次，BE 单磁盘存在 Compaction 效率低的问题。初期，我们在部署 BE 时，每个节点只分配了 1 块 4T 的磁盘，虽然磁盘的空间并不小，但是磁盘的数量比较少，Compaction 线程数只有 2，Compaction 效率很低，这是导致 BE Compaction Score 不健康的原因之⼀。</p></li><li><p>Compaction 配置参数</p></li></ul><p><code>compaction_task_num_per_disk </code>每个磁盘上的任务数，默认为 2</p><p><code>max_compaction_threads Compaction </code>线程的总数，默认为 10</p><p><code>total_permits_for_compaction_score Compaction </code>任务配额，默认 10000</p><blockquote><p>Compaction 工作机制：Apache Doris 的数据写⼊模型使⽤了与 LSM-Tree 类似的数据结构。数据以追加（Append）的⽅式写⼊磁盘，在读逻辑中，需要通过 Merge-on-Read 合并处理写入的数据。Merge-on-Read 会影响读取的效率，为了降低数据读取时需要合并的数据量，使⽤ LSM-Tree 的系统会引⼊后台数据合并逻辑，以⼀定策略定期的对数据进⾏合并。</p></blockquote><p><strong>优化后</strong></p><p>为了解决以上的问题，对部署方式进行了优化以提升服务的稳定性：</p><ul><li><p>FE 进行独⽴部署，避免了 FE 混合部署资源竞争问题</p></li><li><p>BE 进行磁盘拆分，多磁盘部署，从原来一块 4T 磁盘变更为 5 块 1T 磁盘，使 BE Compaction 线程数提升 5 倍，Compaction 效率、磁盘 I/O 带宽也得到了提升</p></li><li><p>增加客户端连接代理层 ProxySQL，对 FE 连接进⾏负载均衡，解决 FE 连接单点问题</p></li><li><p>增加 Supervisor 对 FE、BE 服务进程状态监控，FE、BE 进程意外宕机可以快速恢复</p></li></ul><p>经过以上对部署的优化，Apache Doris 服务的稳定性有了很大的提升，基本可以满足目前对稳定性的需求。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="查询稳定性">查询稳定性<a class="hash-link" href="#查询稳定性" title="标题的直接链接">​</a></h2><p>初期刚部署时，无论进行数据导入还是数据查询，执行起来都比较顺畅。但随着承载的表和数据导入作业数量不断增多，查询稳定性问题逐渐暴露出来。</p><p><strong>优化前</strong></p><p><strong>存在的问题</strong></p><p>随着使用时间和数据量的增加，集群开始频繁出现不可用的问题，主要体现在以下几个方面：</p><ul><li><p>DDL 操作很难执行，查询速度变得比较缓慢</p></li><li><p>FE 服务频繁出现 OOM 宕机，有时候甚至出现无法连接的情况</p></li></ul><p>下图是生产环境某张表的体积的大小和 Tablet 数量的情况。这张表的体积只有 275M，但是 Tablet 的数量却达到了 7410，这非常不合理。进一步排查确认整个集群 Tablet 数量非常庞大，集群只有 5T 的数据量，然而 Tablet 数量达到 150 万。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ce40468c1474f16b4e6ce856087462f~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>最初我们对 Apache Doris 表数据量大小、分区粒度、Bucket 数量、Tablet 数量的关系及 Tablet 数量对集群的影响没有清晰的概念。开发人员在 Apache Doris 使用中更多的是追求查询速度，将大部分的动态分区表的分区粒度设置的比较小，分区 Bucket 数量设置却比较大。</p><p>经过与 Apache Doris 社区小伙伴的沟通交流，了解到 Tablet 数量过大可能会导致元数据管理和运维压力增大，出现查询速度缓慢、FE 不稳定的问题。</p><p><strong>优化方案</strong></p><p>首先明确 Tablet 数量的计算方式，Tablet 数量 = 分区数量 <em> Bucket 数量 </em> 副本数。结合当前实际使用情况，确认可以在分区粒度和 Bucket 数量上进⾏优化。我们将分区的粒度从按天、按周分区更改为按月分区，Bucket 数量按照数据体积大小进行合理的配置。如下图所示，是建议数据体积大小对应的 Bucket 数量设定。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50be2018ccb644419de5dada9f8af3fd~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>本次的优化目标是将 Tablet 数量从 150 万降低到 15 万，同时我们也对未来的增长速度进行了规划，在三副本情况下，期望 Tablet 数量增长速度是 30000/TB。</p><p><strong>优化后</strong></p><p>实际上，在仅对 ODS 表进⾏了分区粒度和 Bucket 数量调整后，集群 Tablet 数量从 150 万下降到了 50 万，效果显著。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/02656dba4afb41738fd15703a61a1891~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>优化前的</strong> <strong>FE</strong></p><p>下图是 FE JVM Heap Stat 的监控情况，每当 FE 执行 Checkpoint 时，元数据就会在内存中复制一份。体现在 FE JVM Heap Stat 上就是形成一个个的波峰。优化之前 FE 对内存占用几乎持续在 90% 以上，而且每一个波峰都非常的尖锐。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6884c908f1114fbe9594e326c7331d71~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>优化后的</strong> <strong>FE</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d2b13ad158f1466bb52af2ed3abbfd4b~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>优化后，FE 堆内存占用明显下降，波峰也变得很平缓。FE 的稳定性得到了比较明显的提升。</p><p><strong>优化前、后的 BE</strong></p><p>BE Compaction Score 监控反映版本的堆积情况，版本堆积的数值在 100 内属于正常范围，超过 100 说明集群可能存在潜在风险。上文也讲到，查询时需要先进行文件合并，再进行数据查询，如果 Tablet 版本过多，版本合并会影响到查询的速度和稳定性。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/73d05d45fd484f85ac1c54c8601762c1~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>经过磁盘的部署优化和 Tablet 优化后，BE Compaction Score 可以稳定在 50 以内，查询的稳定性和性能都得到了非常大的提升。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据同步优化">数据同步优化<a class="hash-link" href="#数据同步优化" title="标题的直接链接">​</a></h2><p><strong>优化前：</strong></p><p>MySQL 数据同步使用 Flink CDC -&gt; Kafka -&gt; Flink Doris Connector -&gt; Doris 的方式全量 + 增量进入 Apache Doris。</p><p>在这个方案中，虽然 Flink CDC 支持全量历史数据的初始化，但由于历史遗留问题，部分表数据量较大，单表有几亿数据，而且这种表大多是没有设置任何分区和索引，在执行简单的 COUNT 查询时都需要花费十几分钟的时间。</p><p>其次，Flink CDC 虽然可以进行增量数据同步，但对于这类表的全量数据初始化几乎是不能实现的，因为 Flink CDC 做全量同步要先读取全量数据，然后对数据分块，再做数据同步，这种情况下，读取是非常非常缓慢的。</p><p><strong>优化后</strong></p><p><strong>针对这种情况，在数据同步上，我们做了以下优化：</strong></p><p>全量同步使用 MySQL Dump -&gt; CSV -&gt; Doris Stream Load -&gt; Doris</p><p>增量同步使用 Flink CDC -&gt; Kafka -&gt; Flink Doris Connector -&gt; Doris</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据调度优化"><strong>数据调度优化</strong><a class="hash-link" href="#数据调度优化" title="标题的直接链接">​</a></h2><p>我们在使用 DolphinScheduler 进行 Doris SQL 的任务调度时，同一 node 下配置多条 SQL 时会出现 node 执行状态异常的情况，导致工作流 DAG 的 node 依赖失效，前一个节点未执行完，后一个节点就开始执行，结果会有缺数据甚至没有数据的情况。这个问题是因为 DolphinScheduler 2.x 在同一个 node 下不支持按顺序执行 MySQL 的多段 SQL，而 Doris 在 DolphinScheduler 中使用 MySQL 数据源创建连接。</p><p>此问题在 DolphinScheduler 3.0.0 版本被修复，配置中可以设置多段 SQL 的分隔符，解决了 DAG 依赖关系失效的问题。</p><h1>Apache Doris 元数据管理和数据血缘实现方案</h1><p>在没有元数据管理和数据血缘之前，我们经常会遇到一些问题，比如想找一个指标，却不知道指标在哪张表，只能找相关开发人员来确认，当然也存在开发人员忘记指标的位置和逻辑的情况。因此只能通过层层筛选确认，此过程十分耗费时间。</p><p>之前我们将表的分层划域、指标口径、负责人等信息放在 Excel 表中，这种维护方式很难保证其完整性，维护起来也比较困难。当需要对数仓进行优化时，无法确认哪些表是可以复用的、哪些表是可以合并的。当需要对表结构进行变更时，或者需要对指标的逻辑进行修改时，也无法确定变更是否会对下游的报表产生影响。</p><p>在以上问题背景下，我们经常遭到用户的投诉，接下来介绍如何通过元数据管理和数据血缘分析方案来解决这些问题。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="实现方案"><strong>实现方案</strong><a class="hash-link" href="#实现方案" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d524fc9d3aff4f6b840d1ae55fe5d380~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>元数据管理和数据血缘是围绕 Apache Doris 展开，同时对 DolphinScheduler 的元数据进行了整合。</p><p>我们将元数据分为物理元数据和业务元数据两大类：</p><ul><li><p>物理元数据维护表的属性信息和调度信息</p></li><li><p>业务元数据维护数据在应用过程中约定的口径和规范信息</p></li></ul><p>数据血缘实现了表级血缘和字段级血缘：</p><ul><li><p>表级血缘支持粗粒度表关系和跨层引用分析</p></li><li><p>字段级血缘支持细粒度的影响分析</p></li></ul><p>上图中，右侧表格是物理元数据业务，元数据指标和血缘分析能够提供的数据服务。</p><p>接下来，一起看下元数据管理和数据血缘的架构和工作原理。</p><p><strong>架构介绍</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9af4b5f7fd674c5bab476a20e7ebdeaa~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>元数据管理和数据血缘实现方案技术栈</strong></p><p>数据采集：使用 Apache Doris 提供的审计日志插件 Doris Audit Plugin 进行数据采集</p><p>数据存储：对审计日志插件做了定制化开发，使用 Kafka 存储 Doris 审计日志数据</p><p>血缘解析：使用 Druid 进行 Doris SQL 解析</p><p>血缘关系存储：使用 Nebula Graph 存储血缘关系数据</p><p>业务元数据：因为业务元数据经常发生 CRUD，因此使用 MySQL 存储业务元数据信息</p><p>搜索数据：使用 ElasticSearch 存储血缘关系查询索引以及表和字段的搜索索引数据</p><p>接下来介绍一下个架构四个组成部分：审计日志的采集和清洗服务、血缘解析服务、元数据信息整合服务、应用接口服务。</p><p><strong>Apache Doris 审计日志的采集/清洗服务</strong></p><p>考虑到如果将数据清洗逻辑放在审计日志插件中，当数据清洗逻辑发生变更，可能会出现数据遗漏，这样会对血缘分析和元数据管理产生影响，所以我们将审计日志插件数据采集和数据清洗进行了解耦，对 Apache Doris 的审计日志插件进行了改造，改造后审计日志插件可以实现审计日志数据的格式化以及将数据发送到 Kafka 的功能。</p><p>数据清洗服务，首先在清洗逻辑中增加数据重排逻辑，针对多个审计日志插件发送的数据进行重新排序，解决数据乱序的问题。其次把非标准 SQL 转化成标准 SQL，虽然 Apache Doris 支持 MySQL 协议以及标准 SQL 语法，但有一些建表语句、SQL 查询语法与标准 SQL 存在一定差异，因此将非标准 SQL 转化为 MySQL 的标准语句，最后将数据发送到 ES 和 Kafka 中。</p><p><strong>血缘解析服务</strong></p><p>血缘解析服务使用 Druid 进行 Doris SQL 的解析，通过 Druid 抽象语法树逐层递归获取表和字段的血缘关系，最后将血缘关系数据封装发送到图数据库、血缘查询索引发送到 ES 。进行血缘解析的同时会将物理元数据和业务元数据发送到对应存储位置。</p><p><strong>元数据信息整合服务</strong></p><p>元数据信息整合服务借鉴了 Metacat 的架构实现方案。</p><p>Connector Manager 负责创建 Apache Doris 和 DolphinScheduler 的元数据链接，同时也支持后续其他类型数据源接入的扩展。</p><p>Meta Service 负责元数据信息获取的具体实现。Apache Doris 元数据信息主要从 information Schema 库、Restful API、以及 SHOW SQL 的查询结果三种途径来获取。DolphinScheduler 的工作流元数据信息和调度记录信息从 DolphinScheduler 元数据库获取。</p><p><strong>应用接口服务</strong></p><p>我们提供了 3 种类型的应用接口服务，分别是血缘应用接口服务、元数据应用接口服务和数据行为分析应用接口服务。</p><ul><li><p>血缘应用接口服务提供表、字段、血缘关系、影响分析的查询服务。</p></li><li><p>元数据应用接口服务提供元数据的查询和字段搜索的服务。</p></li><li><p>数据行为分析应用接口服务提供表结构变更记录、数据读写记录、产出信息的查询服务。</p></li></ul><p>以上就是元数据管理和数据血缘分析架构的整体方案的全部内容介绍。</p><h1>总结及收益</h1><p>今年我们完成了以 Apache Doris 为核心的准实时数仓建设，Apache Doris 经过半年的使用和优化，现在已经趋于稳定，能够满足我们生产的要求。</p><ol><li>新的准实时数仓，对数据计算效率、数据时效的提升是巨大的。</li></ol><p>以 On Time Delivery 业务场景报表计算为例，计算 1000w 单轨迹节点时效变化，使用 <strong>Apache Doris 之前</strong>需要计算 2 个多小时，并且计算消耗的资源非常大，只能在空闲时段进行错峰计算；<strong>使用 Apache Doris 之后</strong>，只需要 3min 就可以完成计算，之前每周更新一次的全链路物流时效报表，现在可以做到每 10 分钟更新最新的数据，达到了准实时的数据时效。</p><ol start="2"><li>得益于 Apache Doris 的标准化 SQL，上手难度小，学习成本低，报表的迁移工作全员都可以参与。</li></ol><p><strong>原来</strong>报表使用 PowerBI 进行开发，需要对 PowerBI 有非常深入的了解，学习成本很高，开发周期也很长，而且 PowerBI 不使用标准 SQL，代码可读性差；<strong>现在</strong>基于 Doris SQL 加上自研的拖拉拽形式的报表平台，报表的开发成本直线下降，大部分需求的开发周期从周下降到了天。</p><h1>未来规划</h1><p>后续我们也将继续推进基于 Apache Doris 的数据中台建设，对元数据管理的完善、数据血缘的解析率持续进行优化，考虑到数据血缘是大家都渴望的应用，在未来血缘解析成熟后，我们会考虑将其贡献给社区。</p><p>与此同时，我们正在着手进行用户行为分析平台的构建，也在考虑使用 Apache Doris 作为核心的存储和计算引擎。目前 Apache Doris 在部分分析场景支持的函数还不够丰富，例如在有序窗口漏斗分析场景，虽然 Apache Doris 已经支持了 window_funnel 函数，但是每层漏斗转化的计算需要用到的 Array 相关计算函数还没有得到很好的支持。不过好在即将发布的 Apache Doris 1.2 版本将包含了 Array 类型以及相关函数，相信未来在越来越多的分析场景中 Apache Doris 都将得到落地。</p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[打造自助对话式数据分析场景，Apache Doris 在思必驰的应用实践]]></title>
            <link>https://selectdb.com/blog/打造自助对话式数据分析场景，Apache Doris 在思必驰的应用实践｜最佳实践</link>
            <guid>/打造自助对话式数据分析场景，Apache Doris 在思必驰的应用实践｜最佳实践</guid>
            <pubDate>Tue, 27 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[作者：赵伟，思必驰大数据高级研发，10 年大数据开发和设计经验，负责大数据平台基础技术和 OLAP 分析技术开发。社区贡献：Doris-spark-connector 的实时读写和优化。]]></description>
            <content:encoded><![CDATA[<blockquote><p>作者：赵伟，思必驰大数据高级研发，10 年大数据开发和设计经验，负责大数据平台基础技术和 OLAP 分析技术开发。社区贡献：Doris-spark-connector 的实时读写和优化。</p></blockquote><h1>业务背景</h1><p>思必驰是国内专业的对话式人工智能平台公司，拥有全链路的智能语音语言技术，致力于成为全链路智能语音及语言交互的平台型企业，自主研发了新一代人机交互平台 DUI 和人工智能芯片 TH1520，为车联网、IoT 及政务、金融等众多行业场景合作伙伴提供自然语言交互解决方案。</p><p>思必驰于 2019 年首次引入 Apache Doris ，基于 Apache Doris 构建了实时与离线一体的数仓架构。相对于过去架构，Apache Doris 凭借其灵活的查询模型、极低的运维成本、短平快的开发链路以及优秀的查询性能等诸多方面优势，如今已经在实时业务运营、自助/对话式分析等多个业务场景得到运用，满足了 设备画像/用户标签、业务场景实时运营、数据分析看板、自助 BI、财务对账等多种数据分析需求。在这一过程中我们也积累了诸多使用上的经验，在此分享给大家。</p><h1>架构演进</h1><p>早期业务中离线数据分析是我们的主要需求，近几年，随着业务的不断发展，业务场景对实时数据分析的要求也越来越高，早期数仓架构逐渐力不从心，暴露出很多问题。为了满足业务场景对查询性能、响应时间及并发能力更高的要求，2019 年正式引入 Apache Doris 构建实时离线一体的数仓架构。</p><p>以下将为大家介绍思必驰数仓架构的演进之路，早期数仓存在的优缺点，同时分享我们选择 Apache Doris 构建新架构的原因以及面临的新问题与挑战。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="早期数仓架构及痛点">早期数仓架构及痛点<a class="hash-link" href="#早期数仓架构及痛点" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1565c372d64b4dd086de1e3635138c21~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>如上图所示，早期架构基于 Hive +Kylin 来构建离线数仓，实时数仓架基于 Spark+MySQL 来构建实时分析数仓。</p><p>我们业务场景的数据源主要分为三类，业务数据库如 MySQL，应用系统如 K8s 容器服务日志，还有车机设备终端的日志。数据源通过 MQTT/HTTP 协议、业务数据库 Binlog 、Filebeat 日志采集等多种方式先写入 Kafka 。在早期架构中，数据经 Kafka 后将分为实时和离线两条链路，首先是实时部分，实时部分链路较短，经过 Kafka 缓冲完的数据通过 Spark 计算后放入 MySQL 中进行分析，对于早期的实时分析需求，MySQL 基本可以满足分析需求。而离线部分则由 Spark 进行数据清洗及计算后在 Hive 中构建离线数仓，并使用 Apache Kylin 构建 Cube，在构建 Cube 之前需要提前做好数据模型的的设计，包括关联表、维度表、指标字段、指标需要的聚合函数等，通过调度系统进行定时触发构建，最终使用 HBase 存储构建好的 Cube。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="早期架构的优势"><strong>早期架构的优势：</strong><a class="hash-link" href="#早期架构的优势" title="标题的直接链接">​</a></h3><ol><li><p>早期架构与 Hive 结合较好，无缝对接 Hadoop 技术体系。</p></li><li><p>离线数仓中基于 Kylin 的预计算、表关联、聚合计算、精确去重等场景，查询性能较高，在并发场景下查询稳定性也较高。</p></li></ol><p>早期架构解决了当时业务中较为紧迫的查询性能问题，但随着业务的发展，对数据分析要求不断升高，早期架构缺点也开始逐渐凸显出来。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="早期架构的痛点"><strong>早期架构的痛点：</strong><a class="hash-link" href="#早期架构的痛点" title="标题的直接链接">​</a></h3><ol><li><p>依赖组件多。Kylin 在 2.x、3.x 版本中强依赖 Hadoop 和 HBase ，应用组件较多导致开发链路较长，架构稳定性隐患多，维护成本比很高。</p></li><li><p>Kylin 的构建过程复杂，构建任务容易失败。Kylin 构建需要进行打宽表、去重列、生成字典，构建 Cube 等如果每天有 1000-2000 个甚至更多的任务，其中至少会有 10 个甚至更多任务构建失败，导致需要大量时间去写自动运维脚本。</p></li><li><p>维度/字典膨胀严重。维度膨胀指的是在某些业务场景中需要多个分析条件和字段，如果在数据分析模型中选择了很多字段而没有进行剪枝，则会导致 Cube 维度膨胀严重，构建时间变长。而字典膨胀指的是在某些场景中需要长时间做全局精确去重，会使得字典构建越来越大，构建时间也会越来越长，从而导致数据分析性能持续下降。</p></li><li><p>数据分析模型固定，灵活性较低。在实际应用过程中，如果对计算字段或者业务场景进行变更，则要回溯部分甚至全部数据。</p></li><li><p>不支持数据明细查询。早期数仓架构是无法提供明细数据查询的，Kylin 官方给的解决方法是下推给 Presto 做明细查询，这又引入了新的架构，增加了开发和运维成本。</p></li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="架构选型">架构选型<a class="hash-link" href="#架构选型" title="标题的直接链接">​</a></h2><p>为解决以上问题，我们开始探索新的数仓架构优化方案，先后对市面上应用最为广泛的 Apache Doris、Clickhouse 等 OLAP 引擎进行选型调研。相较于 ClickHouse 的繁重运维、各种各样的表类型、不支持关联查询等，结合我们的 OLAP 分析场景中的需求，综合考虑，Apache Doris 表现较为优秀，最终决定引入 Apache Doris 。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="新数仓架构">新数仓架构<a class="hash-link" href="#新数仓架构" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38481d46abf14667863edf126efbb1a3~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>如上图所示，我们基于 Apache Doris 构建了实时+离线一体的新数仓架构，与早期架构不同的是，实时和离线的数据分别进行处理后均写入 Apache Doris 中进行分析。</p><p>因历史原因数据迁移难度较大，离线部分基本和早期数仓架构保持一致，在 Hive 上构建离线数仓，当然完全可以在 Apache Doris 上直接构建离线数仓。</p><p>相对早期架构不同的是，离线数据通过 Spark 进行清洗计算后在 Hive 中构建数仓，然后通过 Broker Load 将存储在 Hive 中的数据写入到 Apache Doris 中。这里要说明的， Broker Load 数据导入速度很快，天级别 100-200G 数据导入到 Apache Doris 中仅需要 10-20 分钟。</p><p>实时数据流部分，新架构使用了 Doris-Spark-Connector 来消费 Kafka 中的数据并经过简单计算后写入 Apache Doris 。从架构图所示，实时和离线数据统一在 Apache Doris 进行分析处理，满足了数据应用的业务需求，实现了实时+离线一体的数仓架构。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="新架构的收益"><strong>新架构的收益：</strong><a class="hash-link" href="#新架构的收益" title="标题的直接链接">​</a></h3><ol><li><p>极简运维，维护成本低，不依赖 Hadoop 生态组件。Apache Doris 的部署简单，只有 FE 和 BE 两个进程， FE 和 BE 进程都是可以横向扩展的，单集群支持到数百台机器，数十 PB 的存储容量，并且这两类进程通过一致性协议来保证服务的高可用和数据的高可靠。这种高度集成的架构设计极大的降低了一款分布式系统的运维成本。在使用 Doris 三年时间中花费的运维时间非常少，相比于基于 Kylin 搭建的早期架构，新架构花费极少的时间去做运维。</p></li><li><p>链路短，开发排查问题难度大大降低。基于 Doris 构建实时和离线统一数仓，支持实时数据服务、交互数据分析和离线数据处理场景，这使得开发链路变的很短，问题排查难度大大降低。</p></li><li><p>支持 Runtime 形式的 Join 查询。Runtime 类似 MySQL 的表关联，这对数据分析模型频繁变更的场景非常友好，解决了早期结构数据模型灵活性较低的问题。</p></li><li><p>同时支持 Join、聚合、明细查询。解决了早期架构中部分场景无法查询数据明细的问题。</p></li><li><p>支持多种加速查询方式。支持上卷索引，物化视图，通过上卷索引实现二级索引来加速查询，极大的提升了查询响应时间。</p></li><li><p>支持多种联邦查询方式。支持对 Hive、Iceberg、Hudi 等数据湖和 MySQL、Elasticsearch 等数据库的联邦查询分析。</p></li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="问题和挑战"><strong>问题和挑战：</strong><a class="hash-link" href="#问题和挑战" title="标题的直接链接">​</a></h3><p>在建设新数仓架构过程中，我们遇到了一些问题：</p><ul><li><p>高并发场景对 Apache Doris 查询性能存在一定影响。我们分别在 Doris 0.12 和 Doris 1.1 版本上进行测试，同一时间同样的 SQL，10 并发和 50 并发进行访问，性能差别较大。</p></li><li><p>在实时写入场景中，当实时写入的数据量比较大时，会使得 IO 比较密集，导致查询性能下降。</p></li><li><p>大数据量下字符串精确去重较慢。目前使用的是 count distinct 函数、Shuffle 和聚合算子去重，此方式算力比较慢。当前业内常见的解决方法一般是针对去重列构建字典，基于字典构建 Bitmap 索引后使用 Bitmap 函数去重。目前 Apache Doris 只支持数字类型的 Bitmap 索引，具有一定的局限性。</p></li></ul><h1>业务场景的应用</h1><p>Apache Doris 在思必驰最先应用在实时运营业务场景以及自助/对话式分析场景，本章节将介绍两个场景的需求及应用情况。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="实时运营业务场景">实时运营业务场景<a class="hash-link" href="#实时运营业务场景" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b781f56cf9924d9f96fd5bf3090c21ac~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>首先是实时运营业务场景，如上图所示，实时运营业务场景的技术架构和前文所述的新版数仓架构基本一致：</p><ul><li><p>数据源：数据源新版架构图中一致，包括 MySQL 中的业务数据，应用系统埋点数据以及设备和终端日志。</p></li><li><p>数据导入：离线数据导入使用 Broker Load，实时数据导入使用 Doris-Spark-Connector 。</p></li><li><p>数据存储与开发：几乎所有的实时数仓全部在 Apache Doris 构建，有一部分离线数据放在 Airflow 上执行 DAG 跑批任务。</p></li><li><p>数据应用：最上层是业务侧提出的业务分析需求，包括大屏展示，数据运营的实时看板、用户画像、BI 看板等。</p></li></ul><p><strong>在实时运营业务场景中，数据分析的需求主要有两方面：</strong></p><ul><li><p>由于实时导入数据量比较大，因此对实时数据的查询效率要求较高</p></li><li><p>在此场景中，有 20+ 人的团队在运营，需要同时开数据运营的看板，因此对实时写入的性能和查询并发会有比较高的要求。</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="自助对话式分析场景">自助/对话式分析场景<a class="hash-link" href="#自助对话式分析场景" title="标题的直接链接">​</a></h2><p>除以上之外，Apache Doris 在思必驰第二个应用是自助/对话式分析场景。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b4281deca1d46649ee8977b5282a6a1~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>如上图所示，在一般的 BI 场景中，用户方比如商务、财务、销售、运营、项目经理等会提出需求给数据分析人员，数据分析人员在 BI 平台上做数据看板，最终把看板提供给用户，用户从 BI 看板上获取所需信息，但是有时候用户想要查看明细数据、定制化的看板需求，或者在某些场景需做任意维度的上卷或者下钻的分析，一般场景下 BI 看板是不支持的的，基于以上所述用户需求，我们打造了自助对话式 BI 场景来解决用户定制化的需求。</p><p>与一般 BI 场景不同的是，我们将自助/对话式 BI 场景从数据分析人员方下沉到用户方，用户方只需要通过打字，描述数据分析的需求。基于我们公司自然语言处理的能力，自助/对话式 BI 场景会将自然语言转换成 SQL，类似 NL2SQL 技术，需要说明的是这里使用的是定制的自然语言解析，相对开源的 NL2SQL 命中率高、解析结果更精确。当自然语言转换成 SQL 后，将 SQL 给到 Apache Doris 查询得到分析结果。由此，用户通过打字就可以随时查看任意场景下的明细数据，或者任意字段的上卷、下钻。</p><p>相比 Apache Kylin、Apache Druid 等预计算的 OLAP 引擎，Apache Doris 符合以下几个特点：</p><ul><li><p>查询灵活，模型不固定，支持自由定制场景。</p></li><li><p>支持表关联、聚合计算、明细查询。</p></li><li><p>响应时间要快速。</p></li></ul><p>因此我们很顺利的运用 Apache Doris 实现了自助/对话式分析场景。同时，自助/对话式分析在我们公司多个数据分析场景应用反馈非常好。</p><h1>实践经验</h1><p>基于上面的两个场景，我们使用过程当中积累了一些经验和心得，分享给大家。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="数仓-表设计"><strong>数仓</strong> <strong>表设计：</strong><a class="hash-link" href="#数仓-表设计" title="标题的直接链接">​</a></h3><ol><li><p>千万级(量级供参考，跟集群规模有关系)以下的数据表使用 Duplicate 表类型，Duplicate 表类型同时支持聚合、明细查询，不需要额外写明细表。</p></li><li><p>当数据量比较大时，使用 Aggregate 聚合表类型，在聚合表类型上做上卷索引，使用物化视图优化查询、优化聚合字段。由于 Aggregate 表类型是预计算表，会丢失明细数据，如有明细查询需求，需要额外写一张明细表。</p></li><li><p>当数据量又大、关联表又多时，可用 ETL 先写成宽表，然后导入到 Doris，结合 Aggregate 在聚合表类型上面做优化，也可以使用官方推荐 Doris 的 Join 优化：<a href="https://doris.apache.org/zh-CN/docs/dev/advanced/join-optimization/doris-join-optimization" target="_blank" rel="noopener noreferrer">https://doris.apache.org/zh-CN/docs/dev/advanced/join-optimization/doris-join-optimization</a></p></li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="写入"><strong>写入：</strong><a class="hash-link" href="#写入" title="标题的直接链接">​</a></h3><ol><li><p>通过 Spark Connector 或 Flink Connector 替代 Routine Load： 最早我们使用的是 Routine Load 实时写入 BE 节点， Routine Load 的工作原理是通过 SQL 在 FE 节点起一个类似于 Task Manager 的管理，把任务分发给 BE 节点，在 BE 节点起 Routine Load 任务。在我们实时场景并发很高的情况下，BE 节点 CPU 峰值一般会达到 70% 左右，在这个前提下，Routine Load 也跑到 BE 节点，将严重影响 BE 节点的查询性能，并且查询 CPU 也将影响 Routine Load 导入， Routine Load 就会因为各种资源竞争死掉。面对此问题，目前解决方法是将 Routine Load 从 BE 节点拿出来放到资源调度上，用 Doris-Spark/Flink-Connector 替换 Routine Load。当时 Doris-spark-Connector 还没有实时写入的功能，我们根据业务需求进行了优化，并将方案贡献给社区。</p></li><li><p>通过攒批来控制实时写入频率：当实时写入频率较高时，小文件堆积过多、查询 IO 升高，小文件排序归并的过程将导致查询时间加长，进而出现查询抖动的情况。当前的解决办法是控制导入频次，调整 Compaction 的合并线程、间隔时间等参数，避免 Tablet 下小文件的堆积。</p></li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="查询">查询：<a class="hash-link" href="#查询" title="标题的直接链接">​</a></h3><ol><li><p>增加 SQL 黑名单，控制异常大查询。个别用户在查询时没有加 where 条件，或者查询时选择的时间范围较长，这种情况下 BE 节点的 SQL 会把磁盘的负载和 CPU 拉高，导致其他节点的 SQL 查询变慢，甚至出现 BE 节点宕机的情况。目前的解决方案是使用 SQL 黑名单禁止全表及大量分区实时表的查询。</p></li><li><p>使用 SQL Cache 和 SQL Proxy 实现高并发访问。同时使用 SQL Cache 和 SQL Proxy 的原因在于，SQL Cache 的颗粒度到表的分区，如果数据发生变更， SQL Cache 将失效，因此 SQL Cache 缓存适合数据更新频次较低的场景（离线场景、历史分区等）。对于数据需要持续写到最新分区的场景， SQL Cache 则是不适用的。当 SQL Cache 失效时 Query 将全部发送到 Doris 造成重复的 Runtime 计算，而 SQL Proxy 可以设置一秒左右的缓存，可以避免相同条件的重复计算，有效提高集群的并发。</p></li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="存储">存储：<a class="hash-link" href="#存储" title="标题的直接链接">​</a></h3><p>使用 SSD 和 HDD 做热温数据存储周期的分离，近一年以内的数据存在 SSD，超过一年的数据存在 HDD。Apache Doris 支持对分区设置冷却时间，但只支持创建表分区时设置冷却的时间，目前的解决方案是设置自动同步逻辑，把历史的一些数据从 SSD 迁移到 HDD，确保 1 年内的数据都放在 SSD 上。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="升级">升级：<a class="hash-link" href="#升级" title="标题的直接链接">​</a></h3><p>升级前一定要备份元数据，也可以使用新开集群的方式，通过 Broker 将数据文件备份到 S3 或 HDFS 等远端存储系统中，再通过备份恢复的方式将旧集群数据导入到新集群中。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="升级前后性能对比"><strong>升级前后性能对比</strong><a class="hash-link" href="#升级前后性能对比" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c62a9e18483e4192a6dabb09364180e8~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>思必驰最早是从 0.12 版本开始使用 Apache Doris 的，在今年我们也完成了从 0.15 版本到最新 1.1 版本的升级操作，并进行了基于真实业务场景和数据的性能测试。</p><p>从以上测试报告中可以看到，总共 13 个测试 SQL 中，前 3 个 SQL 升级前后性能差异不明显，因为这 3 个场景主要是简单的聚合函数，对 Apache Doris 性能要求不高，0.15 版本即可满足需求。而在 Q4 之后的场景中 ，SQL 较为复杂，Group By 有多个字段、多个字段聚合函数以及复杂函数，因此升级新版本后带来的性能提升非常明显，平均查询性能较 0.15 版本提升 2-3 倍。由此，非常推荐大家去升级到 Apache Doris 最新版本。</p><h1>总结和收益</h1><ol><li><p>Apache Doris 支持构建离线+实时统一数仓，一个 ETL 脚本即可支持实时和离线数仓，大大缩短开发周期，降低存储成本，避免了离线和实时指标不一致等问题。</p></li><li><p>Apache Doris 1.1.x 版本开始全面支持向量化计算，较之前版本查询性能提升 2-3 倍。经测试，Apache Doris 1.1.x 版本在宽表场景的查询性能已基本与 ClickHouse 持平。</p></li><li><p>功能强大，不依赖其他组件。相比 Apache Kylin、Apache Druid、ClickHouse 等，Apache Doris 不需要引入第 2 个组件填补技术空档。Apache Doris 支持聚合计算、明细查询、关联查询，当前思必驰超 90% 的分析需求已移步 Apache Doris 实现。 得益于此优势，技术人员需要运维的组件减少，极大降低运维成本。</p></li><li><p>易用性极高，支持 MySQL 协议和标准 SQL，大幅降低用户学习成本。</p></li></ol><h1>未来计划</h1><ol><li><p>Tablet 小文件过多的问题。Tablet 是 Apache Doris 中读写数据最小的逻辑单元，当 Tablet 小文件比较多时会产生 2 个问题，一是 Tablet 小文件增多会导致元数据内存压力变大。二是对查询性能的影响，即使是几百兆的查询，但在小文件有几十万、上百万的情况下，一个小小的查询也会导致 IO 非常高。未来，我们将做一个 Tablet 文件数量/大小比值的监控，当比值在不合理范围内时及时进行表设计的修改，使得文件数量和大小的比值在合理的范围内。</p></li><li><p>支持基于 Bitmap 的字符串精确去重。业务中精确去重的场景较多，特别是基于字符串的 UV 场景，目前 Apache Doris 使用的是 Distinct 函数来实现的。未来我们会尝试的在 Apache Doris 中创建字典，基于字典去构建字符串的 Bitmap 索引。</p></li><li><p>Doris-Spark-Connector 流式写入支持分块传输。Doris-Spark-Connector 底层是复用的 Stream Load，工作机制是攒批，容易出现两个问题，一是攒批可能会会出现内存压力导致 OOM，二是当 Doris-Spark-Connector 攒批时，Spark Checkpoint 没有提交，但 Buffer 已满并提交给 Doris，此时 Apacche Doris 中已经有数据，但由于没有提交 Checkpoint，假如此时任务恰巧失败，启动后又会重新消费写入一遍。未来我们将优化此问题，实现 Doris-Spark-Connector 流式写入支持分块传输。</p></li></ol>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[SelectDB 创始人兼CEO 连林江荣获 OSCAR 开源产业大会「尖峰开源人物 」奖项]]></title>
            <link>https://selectdb.com/blog/SelectDB 创始人兼CEO 连林江荣获 OSCAR 开源产业大会「尖峰开源人物 」奖项</link>
            <guid>/SelectDB 创始人兼CEO 连林江荣获 OSCAR 开源产业大会「尖峰开源人物 」奖项</guid>
            <pubDate>Fri, 23 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[2022 年 9 月 16 日，由中国信息通信研究院、中国通信标准化协会联合主办的 “2022 OSCAR 开源产业大会”在京落下帷幕。在本次大会上，中国信息通信研究院发布了多项重大研究成果，并设立了“OSCAR 尖峰开源案例”系列评选，旨在表彰优秀开源案例、树立开源典范，为国内开源生态健康发展树立标杆效应。]]></description>
            <content:encoded><![CDATA[<p>2022 年 9 月 16 日，由中国信息通信研究院、中国通信标准化协会联合主办的 “2022 OSCAR 开源产业大会”在京落下帷幕。在本次大会上，中国信息通信研究院发布了多项重大研究成果，并设立了“OSCAR 尖峰开源案例”系列评选，旨在表彰优秀开源案例、树立开源典范，为国内开源生态健康发展树立标杆效应。</p><p><strong>SelectDB 创始人兼 CEO 连林江经过业内专家的严格考察，凭借其在开源生态中的杰出贡献，被授予「OSCAR 尖峰开源人物」一奖。同时 Apache Doris 也凭借业内领先的技术创新力和影响力从众多开源项目项目中脱颖而出，斩获「OSCAR 尖峰开源项目及社区」一奖。</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac3cca2a2433479c82b884ec51fd058f~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>Apache Doris 是由国人研发并开源的高性能实时 OLAP 数据库， 自 2018 年进入 Apache 基金会孵化后开始受到业界关注。2022 年 6 月，Apache Doris 正式成为 Apache 基金会顶级项目。截止目前，Apache Doris 已经在全球范围内俘获<strong>超过</strong> <strong>700</strong> 家企业的认可，用户覆盖互联网、金融、能源、制造、电信等众多行业。在国内市值或估值<strong>前 50</strong> 的企业中，有<strong>超过 80%</strong> 长期使用 Apache Doris 在其生产环境中。</p><p>作为 Apache Doris 背后的开源技术公司，北京飞轮数据科技有限公司（简称 SelectDB）自创立以来，始终秉持开源开放的核心理念，以<strong>加强开源技术创新、推动开源社区发展、繁荣开源社区生态</strong>为首要己任，在核心功能研发、社区运营推广、用户支持维护等多个方面投入了强有力的资源支持，推动 Apache Doris 成为全球大数据和数据库领域最为活跃和炙手可热的开源项目之一。后续 SelectDB 也将围绕 Apache Doris 开展更多具有价值及挑战的工作，包括新的查询优化器、对湖仓一体化的支持，以及面向云上基础设施的架构演进等。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32ea59781edd4dffbd51f3d284288885~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>与此同时，<strong>SelectDB 基于 Apache Doris 内核打造的 SelectDB Cloud 已于 2022 年 7 月正式对外发布试用</strong>。SelectDB Cloud 是构建于多云之上的云原生实时数据仓库，可以为客户提供极简运维和极致性价比的云上数据仓库服务，目前已有不同行业的客户在 SelectDB Cloud 上构建业务。当前 SelectDB 已与阿里云、腾讯云、亚马逊云科技等知名云厂商开展了深入合作，后续也将进一步拓宽与全球知名云厂商的合作。</p><p>未来 SelectDB 还将投入更多的研发和运营力量，与广大开发者以及开源爱好者一起加强开源技术创新，提升 Apache Doris 在全球范围内的技术影响力，打造繁荣开放、良性发展的开源社区生态。同时 SelectDB 也将致力于打造云原生时代实时数据分析领域的国际工业界标准，实现实时数据分析技术的革新，并为全球客户提供最极致的云上数据分析新体验。</p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[如何高效解决 C++内存问题，Apache Doris 实践之路]]></title>
            <link>https://selectdb.com/blog/如何高效解决 C++内存问题，Apache Doris 实践之路｜技术解析</link>
            <guid>/如何高效解决 C++内存问题，Apache Doris 实践之路｜技术解析</guid>
            <pubDate>Mon, 05 Sep 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：Apache Doris 使用 C++ 语言实现了执行引擎，C++ 开发过程中，影响开发效率的一个重要因素是指针的使用，包括非法访问、泄露、强制类型转换等。本文将会通过对 Sanitizer 和 Core Dump 分析工具的介绍来为大家分享：如何快速定位 Apache Doris 中的 C++ 问题，帮助开发者提升开发效率并掌握更高效的开发技巧。]]></description>
            <content:encoded><![CDATA[<blockquote><p>导读：Apache Doris 使用 C++ 语言实现了执行引擎，C++ 开发过程中，影响开发效率的一个重要因素是指针的使用，包括非法访问、泄露、强制类型转换等。本文将会通过对 Sanitizer 和 Core Dump 分析工具的介绍来为大家分享：如何快速定位 Apache Doris 中的 C++ 问题，帮助开发者提升开发效率并掌握更高效的开发技巧。</p></blockquote><p>Apache Doris 是一款高性能 MPP 分析型数据库，出于性能的考虑，Apache Doris 使用了 C++ 语言实现了执行引擎。在 C++ 开发过程中，影响开发效率的一个重要因素是指针的使用，包括非法访问、泄露、强制类型转换等。Google Sanitizer 是由 Google 设计的用于动态代码分析的工具，在 Apache Doris 开发过程中遭遇指针使用引起的内存问题时，正是因为有了 Sanitizer，使得问题解决效率可以得到数量级的提升。除此以外，当出现一些内存越界或非法访问的情况导致 BE 进程 Crash 时，Core Dump 文件是非常有效的定位和复现问题的途径，因此一款高效分析 CoreDump 的工具也会进一步帮助更加快捷定位问题。</p><p><strong>本文将会通过对 Sanitizer 和 Core Dump 分析工具的介绍来为大家分享：如何快速定位 Apache Doris 中的 C++ 问题，帮助开发者提升开发效率并掌握更高效的开发技巧。</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="sanitizer-介绍">Sanitizer 介绍<a class="hash-link" href="#sanitizer-介绍" title="标题的直接链接">​</a></h2><p>定位 C++ 程序内存问题常用的工具有两个，Valgrind 和 Sanitizer。</p><blockquote><p>二者的对比可以参考：<a href="https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind" target="_blank" rel="noopener noreferrer">https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind</a></p></blockquote><p>其中 Valgrind 通过运行时软件翻译二进制指令的执行获取相关的信息，所以 Valgrind 会非常大幅度的降低程序性能，这就导致在一些大型项目比如 Apache Doris 使用 Valgrind 定位内存问题效率会很低。</p><p>而 Sanitizer 则是通过编译时插入代码来捕获相关的信息，性能下降幅度比 Valgrind 小很多，使得能够在单测以及其它测试环境默认使用 Saintizer。</p><blockquote><p>Sanitizer 的算法可以参考：<a href="https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm" target="_blank" rel="noopener noreferrer">https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm</a></p></blockquote><p>在 Apache Doris 中，我们通常使用 Sanirizer 来定位内存问题。LLVM 以及 GNU C++ 有多个 Sanitizer：</p><ul><li>AddressSanitizer（ASan）可以发现内存错误问题，比如 use after free，heap buffer overflow，stack buffer overflow，global buffer overflow，use after return，use after scope，memory leak，super large memory allocation；</li><li>AddressSanitizerLeakSanitizer （LSan）可以发现内存泄露；</li><li>MemorySanitizer（MSan）可以发现未初始化的内存使用；</li><li>UndefinedBehaviorSanitizer （UBSan）可以发现未定义的行为，比如越界数组访问、数值溢出等；</li><li>ThreadSanitizer （TSan）可以发现线程的竞争行为；</li></ul><p>其中 AddressSanitizer, AddressSanitizerLeakSanitizer 以及 UndefinedBehaviorSanitizer 对于解决指针相关的问题最为有效。</p><p>Sanitizer 不但能够发现错误，而且能够给出错误源头以及代码位置，这就使得问题的解决效率很高，通过一些例子来说明 Sanitizer 的易用程度。</p><blockquote><p>可以参考此处使用 Sanitizer：<a href="https://github.com/apache/doris/blob/master/be/CMakeLists.txt" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/blob/master/be/CMakeLists.txt</a></p></blockquote><p>Sanitizer 和 Core Dump 配合定位问题非常高效，默认 Sanitizer 不生成 Core Dump 文件，可以使用如下环境变量生成 Core Dump 文件，建议默认打开。</p><blockquote><p>可以参考：<a href="https://github.com/apache/doris/blob/master/bin/start_be.sh" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/blob/master/bin/start_be.sh</a></p></blockquote><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export ASAN_OPTIONS=symbolize=1:abort_on_error=1:disable_coredump=0:unmap_shadow_on_exit=1</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>使用如下环境变量让 UBSan 生成代码栈，默认不生成。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export UBSAN_OPTIONS=print_stacktrace=1</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>有时候需要显示指定 Symbolizer 二进制的位置，这样 Sanitizer 就能够直接生成可读的代码栈。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export ASAN_SYMBOLIZER_PATH=your path of llvm-symbolizer</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="sanitizer-使用举例">Sanitizer 使用举例<a class="hash-link" href="#sanitizer-使用举例" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="use-after-free">Use after free<a class="hash-link" href="#use-after-free" title="标题的直接链接">​</a></h3><p>User after free 是指访问释放的内存，针对 use after free 错误，AddressSanitizer 能够报出使用释放地址的代码栈，地址分配的代码栈，地址释放的代码栈。比如：<a href="https://github.com/apache/doris/issues/9525" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/issues/9525</a>中，使用释放地址的代码栈如下：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">82849==ERROR: AddressSanitizer: heap-use-after-free on address 0x60300074c420 at pc 0x56510f61a4f0 bp 0x7f48079d89a0 sp 0x7f48079d8990</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">READ of size 1 at 0x60300074c420 thread T94 (MemTableFlushTh)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #0 0x56510f61a4ef in doris::faststring::append(void const*, unsigned long) /mnt/ssd01/tjp/incubator-doris/be/src/util/faststring.h:120</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// 更详细的代码栈请前往https://github.com/apache/doris/issues/9525查看</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>此地址初次分配的代码栈如下：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">previously allocated by thread T94 (MemTableFlushTh) here:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #0 0x56510e9b74b7 in __interceptor_malloc (/mnt/ssd01/tjp/regression_test/be/lib/palo_be+0x536a4b7)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #1 0x56510ee77745 in Allocator&lt;false, false&gt;::alloc_no_track(unsigned long, unsigned long) /mnt/ssd01/tjp/incubator-doris/be/src/vec/common/allocator.h:223</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #2 0x56510ee68520 in Allocator&lt;false, false&gt;::alloc(unsigned long, unsigned long) /mnt/ssd01/tjp/incubator-doris/be/src/vec/common/allocator.h:104</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>地址释放的代码栈如下：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">0x60300074c420 is located 16 bytes inside of 32-byte region [0x60300074c410,0x60300074c430)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">freed by thread T94 (MemTableFlushTh) here:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #0 0x56510e9b7868 in realloc (/mnt/ssd01/tjp/regression_test/be/lib/palo_be+0x536a868)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #1 0x56510ee8b913 in Allocator&lt;false, false&gt;::realloc(void*, unsigned long, unsigned long, unsigned long) /mnt/ssd01/tjp/incubator-doris/be/src/vec/common/allocator.h:125</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #2 0x56510ee814bb in void doris::vectorized::PODArrayBase&lt;1ul, 4096ul, Allocator&lt;false, false&gt;, 15ul, 16ul&gt;::realloc&lt;&gt;(unsigned long) /mnt/ssd01/tjp/incubator-doris/be/src/vec/common/pod_array.h:147</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>有了详细的非法访问地址代码栈、分配代码栈、释放代码栈，问题定位就会非常容易。</p><blockquote><p>说明：限于文章篇幅，示例中的栈展示不全，完整代码栈可以前往对应 Issue 中进行查看。</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="heap-buffer-overflow">heap buffer overflow<a class="hash-link" href="#heap-buffer-overflow" title="标题的直接链接">​</a></h3><p>AddressSanitizer 能够报出 heap buffer overflow 的代码栈。</p><p>比如<a href="https://github.com/apache/doris/issues/5951" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/issues/5951</a> 里的，结合运行时生成的 Core Dump 文件就可以快速定位问题。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">==3930==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000878 at pc 0x000000ae00ce bp 0x7ffeb16aa660 sp 0x7ffeb16aa658</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">READ of size 8 at 0x60c000000878 thread T0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    #0 0xae00cd in doris::StringFunctions::substring(doris_udf::FunctionContext*, doris_udf::StringVal const&amp;, doris_udf::IntVal const&amp;, doris_udf::IntVal const&amp;) ../src/exprs/string_functions.cpp:98</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="memory-leak">memory leak<a class="hash-link" href="#memory-leak" title="标题的直接链接">​</a></h3><p>AddressSanitizer 能够报出哪里分配的内存没有被释放，就可以快速的分析出泄露原因。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">==1504733==ERROR: LeakSanitizer: detected memory leaks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Direct leak of 688128 byte(s) in 168 object(s) allocated from:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#0 0x560d5db51aac in __interceptor_posix_memalign (/mnt/ssd01/doris-master/VEC_ASAN/be/lib/doris_be+0x9227aac)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#1 0x560d5fbb3813 in doris::CoreDataBlock::operator new(unsigned long) /home/zcp/repo_center/doris_master/be/src/util/core_local.cpp:35</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#2 0x560d5fbb65ed in doris::CoreDataAllocatorImpl&lt;8ul&gt;::get_or_create(unsigned long) /home/zcp/repo_center/doris_master/be/src/util/core_local.cpp:58</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#3 0x560d5e71a28d in doris::CoreLocalValue::CoreLocalValue(long)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p><a href="https://github.com/apache/doris/issues/10926" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/issues/10926</a></p><p><a href="https://github.com/apache/doris/pull/3326" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/3326</a></p></blockquote><h2 class="anchor anchorWithStickyNavbar_LWe7" id="异常分配">异常分配<a class="hash-link" href="#异常分配" title="标题的直接链接">​</a></h2><p>分配过大的内存 AddressSanitizer 会报出 OOM 错误，根据栈以及 Core Dump 文件可以分析出何处分配了过大内存。栈举例如下：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06861813865e4e8d805b02a208e1ada8~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><blockquote><p>Fix PR 见：<a href="https://github.com/apache/doris/pull/10289" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/10289</a></p></blockquote><p>UBSan 能够高效发现强制类型转换的错误，如下方 Issue 链接中描述，它能够精确的描述出强制类型转换带来错误的代码，如果不能在第一现场发现这种错误，后续因为指针错误使用，会比较难定位。</p><blockquote><p>Issue：<a href="https://github.com/apache/doris/issues/9105" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/issues/9105</a></p></blockquote><p>UndefinedBehaviorSanitizer 也比 AddressSanitizer 及其它的更容易发现死锁。</p><blockquote><p>比如：<a href="https://github.com/apache/doris/issues/10309" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/issues/10309</a></p></blockquote><h2 class="anchor anchorWithStickyNavbar_LWe7" id="程序维护内存-pool-时-addresssanitizer-的使用">程序维护内存 Pool 时 AddressSanitizer 的使用<a class="hash-link" href="#程序维护内存-pool-时-addresssanitizer-的使用" title="标题的直接链接">​</a></h2><p>AddressSanitizer 是编译器针对内存分配、释放、访问 生成额外代码来实现内存问题分析的，如果程序维护了自己的内存 Pool，AddressSanitizer 就不能发现 Pool 中内存非法访问的问题。这种情况下需要做一些额外的工作来使得 AddressSanitizer 尽可能工作，主要是使用 ASAN_POISON_MEMORY_REGION 和 ASAN_UNPOISON_MEMORY_REGION 管理内存是否可以访问，这种方法使用比较难，因为 AddressSanitizer 内部有地址对齐等的处理。出于性能以及内存释放等原因，Apache Doris 也维护了内存分配 Pool ，这种方法不能确保 AddressSanitizer 能够发现所有问题。</p><blockquote><p>可以参考：<a href="https://github.com/apache/doris/pull/8148" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/8148</a></p></blockquote><p>当程序维护自己的内存池时，按照 <a href="https://github.com/apache/dorisw/pull/8148" target="_blank" rel="noopener noreferrer">https://github.com/apache/dorisw/pull/8148</a> 中方法，use after free 错误会变成 use after poison。但是 use after poison 不能够给出地址失效的栈（<a href="https://github.com/google/sanitizers/issues/191" target="_blank" rel="noopener noreferrer">https://github.com/google/sanitizers/issues/191</a>），从而导致问题的定位分析仍然很困难。</p><p>因此建议程序维护的内存 Pool 可以通过选项关闭，这样在测试环境就可以使用 AddressSanitizer 高效地定位内存问题。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="core-dump-分析工具">Core dump 分析工具<a class="hash-link" href="#core-dump-分析工具" title="标题的直接链接">​</a></h3><p>分析 C++ 程序生成的 Core Dump 文件经常遇到的问题就是怎么打印出 STL 容器中的值以及 Boost 中容器的值，有如下三个工具可以高效的查看 STL 和 Boost 中容器的值。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="stl-view">STL-View<a class="hash-link" href="#stl-view" title="标题的直接链接">​</a></h3><p>可以将此文件 <a href="https://github.com/dataroaring/tools/blob/main/gdb/dbinit_stl_views-1.03.txt" target="_blank" rel="noopener noreferrer">https://github.com/dataroaring/tools/blob/main/gdb/dbinit_stl_views-1.03.txt</a> 放置到~/.gdbinit 中使用 STL-View。STL-View 输出非常友好，支持 pvector，plist，plist_member，pmap，pmap_member，pset，pdequeue，pstack，pqueue，ppqueue，pbitset，pstring，pwstring。以 Apache Doris 中使用 pvector 为例，它能够输出 vector 中的所有元素。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">(gdb) pvector block.data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">elem[0]: $5 = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      t = 0x606000fdc820</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;std::__shared_ptr&lt;doris::vectorized::IDataType const, (__gnu_cxx::_Lock_policy)2&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;std::__shared_ptr_access&lt;doris::vectorized::IDataType const, (__gnu_cxx::_Lock_policy)2, false, false&gt;&gt; = {&lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      members of std::__shared_ptr&lt;doris::vectorized::IDataType const, (__gnu_cxx::_Lock_policy)2&gt;:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      _M_ptr = 0x6030069e9780,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      _M_refcount = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        _M_pi = 0x6030069e9770</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    static npos = 18446744073709551615,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    _M_dataplus = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;std::allocator&lt;char&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        &lt;__gnu_cxx::new_allocator&lt;char&gt;&gt; = {&lt;No data fields&gt;}, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      members of std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;::_Alloc_hider:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      _M_p = 0x61400006e068 "n_nationkey"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    _M_string_length = 11,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      _M_local_buf = "n_nationkey\000\276\276\276\276",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      _M_allocated_capacity = 7957695015158701934</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">elem[1]: $6 = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      t = 0x6080001ec220</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  type = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ...</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="pretty-printer">Pretty-Printer<a class="hash-link" href="#pretty-printer" title="标题的直接链接">​</a></h3><p>GCC 7.0 开始支持了 Pretty-Printer 打印 STL 容器，可以将以下代码放置到~/.gdbinit 中使 Pretty-Printer 生效。</p><p>注意：/usr/share/gcc/python 需要更换为本机对应的地址。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">python</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import sys</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">sys.path.insert(0, '/usr/share/gcc/python')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from libstdcxx.v6.printers import register_libstdcxx_printers</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">register_libstdcxx_printers (None)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以 vector 为例， Pretty-Printer 能够打印出详细内容。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">(gdb) p block.data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">$1 = std::vector of length 7, capacity 8 = {{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t = 0x606000fdc820</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type = std::shared_ptr&lt;const doris::vectorized::IDataType&gt; (use count 1, weak count 0) = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      get() = 0x6030069e9780</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name = "n_nationkey"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t = 0x6080001ec220</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type = std::shared_ptr&lt;const doris::vectorized::IDataType&gt; (use count 1, weak count 0) = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      get() = 0x6030069e9750</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name = "n_name"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t = 0x606000fd52c0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type = std::shared_ptr&lt;const doris::vectorized::IDataType&gt; (use count 1, weak count 0) = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      get() = 0x6030069e9720</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name = "n_regionkey"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  }, {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    column = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      &lt;COW&lt;doris::vectorized::IColumn&gt;::intrusive_ptr&lt;doris::vectorized::IColumn const&gt;&gt; = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        t = 0x6030069e96b0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      }, &lt;No data fields&gt;},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    type = std::shared_ptr&lt;const doris::vectorized::IDataType&gt; (use count 1, weak count 0) = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      get() = 0x604000a66160</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name = "n_comment"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="boost-pretty-printer">Boost Pretty Printer<a class="hash-link" href="#boost-pretty-printer" title="标题的直接链接">​</a></h3><p>因为 Apache Doris 使用 Boost 不多，因此不再举例。</p><blockquote><p>可以参考：<a href="https://github.com/ruediger/Boost-Pretty-Printer" target="_blank" rel="noopener noreferrer">https://github.com/ruediger/Boost-Pretty-Printer</a></p></blockquote><h2 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a class="hash-link" href="#总结" title="标题的直接链接">​</a></h2><p>有了 Sanitizer 能够在单测、功能、集成、压力测试环境及时发现问题，最重要的是大多数时候都可以给出程序出问题的关联现场，比如内存分配的调用栈，释放内存的调用栈，非法访问内存的调用栈，配合 Core Dump 可以查看现场状态，解决 C++ 内存问题从猜测变成了有证据的现场分析。</p><p><strong>作者介绍：</strong>杨勇强，SelectDB 联合创始人兼产品 VP，同时也是 Apache Doris Committer。曾担任百度智能云存储部总架构师，主导构建了云存储技术产品体系，是 Linux 内核社区贡献者。</p><p>— End —</p><p><strong>相关链接：</strong></p><p>SelectDB 官方网站：</p><p><a href="https://selectdb.com/" target="_blank" rel="noopener noreferrer">https://selectdb.com</a></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org/" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>技术分享</category>
        </item>
        <item>
            <title><![CDATA[Apache Doris 在小米数据场景的应用实践与优化]]></title>
            <link>https://selectdb.com/blog/最佳实践｜Apache Doris 在小米数据场景的应用实践与优化</link>
            <guid>/最佳实践｜Apache Doris 在小米数据场景的应用实践与优化</guid>
            <pubDate>Fri, 12 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[<!-- 导读： 小米集团于 2019 年首次引入了 Apache Doris ，目前 Apache Doris 已经在小米内部数十个业务中得到广泛应用，并且在小米内部已经形成一套以 Apache Doris 为核心的数据生态。 本篇文章转录自 Doris 社区线上 Meetup 主题演讲，旨在分享 Apache Doris 在小米数据场景的落地实践与优化实践。]]></description>
            <content:encoded><![CDATA[<h1>业务背景</h1><p>因增长分析业务需要，小米集团于 2019 年首次引入了 Apache Doris 。经过三年时间的发展，目前 Apache Doris 已经在广告投放、新零售、增长分析、数据看板、天星数科、小米有品、用户画像等小米内部数十个业务中得到广泛应用 <strong>，并且在小米内部已经形成一套以 Apache Doris 为核心的数据生态。</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/25d7c2c45acd4e1c8c1a1742016fc6b9~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>当前 Apache Doris 在小米内部已经具有<strong>数十个</strong>集群、总体达到<strong>数百台</strong> BE 节点的规模，其中单集群最大规模达到<strong>近百台节点</strong>，拥有<strong>数十个</strong>流式数据导入产品线，每日单表最大增量 <strong>120 亿</strong>、支持 <strong>PB 级别</strong>存储，单集群每天可以支持 <strong>2W 次以上</strong>的多维分析查询。</p><h1>架构演进</h1><p>小米引入 Apache Doris 的初衷是为了解决内部进行用户行为分析时所遇到的问题。随着小米互联网业务的发展，各个产品线利用用户行为数据对业务进行增长分析的需求越来越迫切。让每个业务产品线都自己搭建一套增长分析系统，不仅成本高昂，也会导致效率低下。因此能有一款产品能够帮助他们屏蔽底层复杂的技术细节，让相关业务人员能够专注于自己的技术领域，可以极大提高工作效率。基于此，小米大数据和云平台联合开发了增长分析系统 Growing Analytics（下文中简称 GA )，旨在提供一个灵活的多维实时查询和分析平台，统一数据接入和查询方案，帮助业务线做精细化运营。（此处内容引用自：<a href="https://mp.weixin.qq.com/s?__biz=MzUxMDQxMDMyNg==&amp;mid=2247486817&amp;idx=1&amp;sn=99fbef15b4d6f6059c3affbc77517e6e&amp;scene=21#wechat_redirect" target="_blank" rel="noopener noreferrer">基于 Apache Doris 的小米增长分析平台实践</a>）</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/897a0453e1a540ae88cdf05ee9188b56~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>分析、决策、执行是一个循环迭代的过程，在对用户进行行为分析后，针对营销策略是否还有提升空间、是否需要在前端对用户进行个性化推送等问题进行决策，帮助小米实现业务的持续增长。这个过程是对用户行为进行<strong>分析-决策-优化执行-再分析-再决策-再优化执行</strong>的迭代过程。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="历史架构">历史架构<a class="hash-link" href="#历史架构" title="标题的直接链接">​</a></h2><p>增长分析平台立项于 2018 年年中，当时基于开发时间和成本，技术栈等因素的考虑，小米复用了现有各种大数据基础组件（HDFS, Kudu, SparkSQL 等），搭建了一套基于 Lamda 架构的增长分析查询系统。<strong>GA 系统初代版本的架构如下图所示，包含了以下几个方面：</strong></p><ul><li>数据源：数据源是前端的埋点数据以及可能获取到的用户行为数据。</li><li>数据接入层：对埋点数据进行统一的清洗后打到小米内部自研的消息队列 Talos 中，并通过 Spark Streaming 将数据导入存储层 Kudu 中。</li><li>存储层：在存储层中进行冷热数据分离。热数据存放在 Kudu 中，冷数据则会存放在 HDFS 上。同时在存储层中进行分区，当分区单位为天时，每晚会将一部分数据转冷并存储到 HDFS 上。</li><li>计算层/查询层：在查询层中，使用 SparkSQL 对 Kudu 与 HDFS 上数据进行联合视图查询，最终把查询结果在前端页面上进行显示。</li></ul><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9039c4f9ef8a4a3cbfd092b21233e831~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>在当时的历史背景下，初代版本的增长分析平台帮助我们解决了一系列用户运营过程中的问题，但同时在历史架构中也存在了两个问题：</strong></p><p><strong>第一个问题：</strong> 由于历史架构是基于 SparkSQL + Kudu + HDFS 的组合，依赖的组件过多导致运维成本较高。原本的设计是各个组件都使用公共集群的资源，但是实践过程中发现执行查询作业的过程中，查询性能容易受到公共集群其他作业的影响，容易抖动，尤其在读取 HDFS 公共集群的数据时，有时较为缓慢。</p><p><strong>第二个问题：</strong> 通过 SparkSQL 进行查询时，延迟相对较高。SparkSQL 是基于批处理系统设计的查询引擎，在每个 Stage 之间交换数据 Shuffle 的过程中依然需要落盘操作，完成 SQL 查询的时延较高。为了保证 SQL 查询不受资源的影响，我们通过添加机器来保证查询性能，但是实践过程中发现，性能提升的空间有限，这套解决方案并不能充分地利用机器资源来达到高效查询的目的，存在一定的资源浪费。 <strong>（此处内容引用自：<a href="https://mp.weixin.qq.com/s?__biz=MzUxMDQxMDMyNg==&amp;mid=2247486817&amp;idx=1&amp;sn=99fbef15b4d6f6059c3affbc77517e6e&amp;scene=21#wechat_redirect" target="_blank" rel="noopener noreferrer">基于 Apache Doris 的小米增长分析平台实践</a>）</strong></p><p>针对上述两个问题，我们的目标是寻求一款计算存储一体的 MPP 数据库来替代我们目前的存储计算层的组件，<strong>在通过技术选型后，最终我们决定使用 Apache Doris 替换老一代历史架构。</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="基于-apache-doris-的新版架构">基于 Apache Doris 的新版架构<a class="hash-link" href="#基于-apache-doris-的新版架构" title="标题的直接链接">​</a></h2><p>当前架构从数据源获取前端埋点数据后，通过数据接入层打入 Apache Doris 后可以直接查询结果并在前端进行显示。<img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/540f5fa779af4b629869e54b793ea273~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>选择 Doris 原因：</strong></p><ul><li><p>Doris 具有优秀的查询性能，能够满足业务需求。</p></li><li><p>Doris 支持标准 SQL ，用户使用与学习成本较低。</p></li><li><p>Doris 不依赖于其他的外部系统，运维简单。</p></li><li><p>Doris 社区拥有很高活跃度，有利于后续系统的维护升级。</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="新旧架构性能对比">新旧架构性能对比<a class="hash-link" href="#新旧架构性能对比" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ada8246b409a4cb6b11ffd2454aa2b06~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>我们选取了日均数据量大约 10 亿的业务，分别在不同场景下进行了性能测试，其中包含 6 个事件分析场景，3 个留存分析场景以及 3 个漏斗分析场景。<strong>经过对比后，得出以下结论：</strong></p><ul><li>在事件分析的场景下，平均查询所耗时间<strong>降低了 85%</strong> 。</li><li>在留存分析和漏斗分析场景下，平均查询所耗时间<strong>降低了 50%</strong> <strong>。</strong></li></ul><h1>应用实践</h1><p>随着接入业务的增多和数据规模的增长，让我们也遇到不少问题和挑战，下面我们将介绍在<strong>使用 Apache Doris 过程中沉淀出来的一些实践经验</strong>。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据导入">数据导入<a class="hash-link" href="#数据导入" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8afce198933f4ca4b2c97d4cf85b27de~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q">小米内部主要通过 Stream Load 与 Broker Load 以及少量 Insert 方式来进行 Doris 的数据导入。数据一般会先打到 Talos 消息队列中，并分为实时数据和离线数据两个部分。</p><p><strong>实时数据写入 Apache Doris 中：</strong></p><p>一部分业务在通过 Flink 对数据进行处理后，会通过 Doris 社区提供的 Flink Doris Connector 组件写入到 Doris 中，底层依赖于 Doris Stream Load 数据导入方式。也有一部分会通过 Spark Streaming 封装的 Stream Load 将数据导入到 Doris 中。</p><p><strong>离线数据写入</strong> <strong>Apache Doris 中：</strong></p><p>离线数据部分则会先写到 Hive 中，再通过小米的数据工场将数据导入到 Doris 中。用户可以直接在数据工场提交 Broker Load 任务并将数据直接导入 Doris 中，也可以通过 Spark SQL 将数据导入 Doris 中。Spark SQL 方式则是依赖了 Doris 社区提供的 Spark Doris Connector 组件，底层也是对 Doris 的 Stream Load 数据导入方式进行的封装。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据查询">数据查询<a class="hash-link" href="#数据查询" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8c1cd3554e854dbe99aba27499e28118~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>用户通过数据工场将数据导入至 Doris 后即可进行查询，在小米内部是通过小米自研的数鲸平台来做查询的。用户可以通过数鲸平台对 Doris 进行查询可视化，并实现用户行为分析（为满足业务的事件分析、留存分析、漏斗分析、路径分析等行为分析需求，我们为 Doris 添加了相应的 UDF 和 UDAF ）和用户画像分析。</p><p>虽然目前依然需要将 Hive 的数据导过来，但 Doris 社区也正在支持湖仓一体能力，在后续实现湖仓一体能力后，我们会考虑直接通过 Doris 查询 Hive 与 Iceberg 外表。<strong>值得一提的是，Doris 1.1 版本已经实现支持查询 Iceberg 外表能力。</strong> 同时在即将发布的 <strong>1.2 版本</strong>中，还将支持 Hudi 外表并增加了 Multi Catalog ，可以实现外部表元数据的同步，无论是查询外部表的性能还是接入外表的易用性都有了很大的提升。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="compaction-调优">Compaction 调优<a class="hash-link" href="#compaction-调优" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/92ad4ea90c564af2b720080b449c6edf~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>Doris 底层采用类似 LSM-Tree 方式，支持快速的数据写入。每一次的数据导入都会在底层的 Tablet 下生成一个新的数据版本，每个数据版本内都是一个个小的数据文件。单个文件内部是有序的，但是不同的文件之间又是无序的。为了使数据有序，在 Doris 底层就会存在 Compaction 机制，异步将底层小的数据版本合并成大的文件。Compaction 不及时就会造成版本累积，增加元数据的压力，并影响查询性能。由于 Compaction 任务本身又比较耗费机器 CPU、内存与磁盘资源，如果 Compaction 开得太大就会占用过多的机器资源并影响到查询性能，同时也可能会造成 OOM。<strong>针对以上问题，我们一方面从业务侧着手，通过以下方面引导用户：</strong></p><ul><li>通过引导业务侧进行合理优化，对表设置<strong>合理的分区和分桶</strong>，避免生成过多的数据分片。</li><li>引导用户尽量<strong>降低数据的导入频率</strong> <strong>，</strong> <strong>增大单次数据导入的量</strong>，降低 Compaction 压力。</li><li>引导用户<strong>避免过多使用会在底层生成 Delete 版本的 Delete 操作</strong>。在 Doris 中 Compaction 分为 Base Compaction 与 Cumulative Compaction。Cumulative Compaction 会快速的把大量新导入的小版本进行快速的合并，在执行过程中若遇到 Delete 操作就会终止并将当前 Delete 操作版本之前的所有版本进行合并。由于 Cumulative Compaction 无法处理 Delete 版本，在合并完之后的版本会和当前版本一起放到 Base Compaction 中进行。当 Delete 版本特别多时， Cumulative Compaction 的步长也会相应变短，只能合并少量的文件，导致 Cumulative Compaction 不能很好的发挥小文件合并效果。</li></ul><p><strong>另一方面我们从运维侧着手：</strong></p><ul><li><strong>针对不同的业务集群配置不同的 Compaction 参数。</strong> 部分业务是实时写入数据的，需要的查询次数很多，我们就会将 Compaction 开的大一点以达到快速合并目的。而另外一部分业务只写今天的分区，但是只对之前的分区进行查询，在这种情况下，我们会适当的将 Compaction 放的小一点，避免 Compaction 占用过大内存或 CPU 资源。到晚上导入量变少时，之前导入的小版本能够被及时合并，对第二天查询效率不会有很大影响。</li><li><strong>适当降低 Base Compaction 任务优先级并增加 Cumulative Compaction 优先级。</strong> 根据上文提到的内容，Cumulative Compaction 能够快速合并大量生成的小文件，而 Base Compaction 由于合并的文件较大，执行的时间也会相应变长，读写放大也会比较严重。所以我们希望 Cumulative Compaction 优先、快速的进行。</li><li><strong>增加版本积压报警。</strong> 当我们收到版本积压报警时，动态调大 Compaction 参数，尽快消耗积压版本。</li><li><strong>支持手动触发指定表与分区下数据分片的 Compaction 任务。</strong> 由于 Compaction 不及时，部分表在查询时版本累积较多并需要能够快速进行合并。所以，我们支持对单个表或单个表下的某个分区提高 Compaction 优先级。</li></ul><p><strong>目前 Doris 社区针对以上问题已经做了</strong> <strong>一系列的优化</strong> <strong>，在 1.1 版本中</strong> <strong>大幅增强了数据 Compaction 能力，对于新增数据能够快速完成聚合，避免分片数据中的版本过多导致的 -235 错误以及带来的查询效率问题。</strong>\
<strong>首先</strong>，在 Doris 1.1 版本中，引入了 QuickCompaction，增加了主动触发式的 Compaction 检查，在数据版本增加的时候主动触发 Compaction。同时通过提升分片元信息扫描的能力，快速的发现数据版本多的分片，触发 Compaction。通过主动式触发加被动式扫描的方式，彻底解决数据合并的实时性问题。</p><p><strong>同时</strong>，针对高频的小文件 Cumulative Compaction，实现了 Compaction 任务的调度隔离，防止重量级的 Base Compaction 对新增数据的合并造成影响。</p><p><strong>最后</strong>，针对小文件合并，优化了小文件合并的策略，采用梯度合并的方式，每次参与合并的文件都属于同一个数据量级，防止大小差别很大的版本进行合并，逐渐有层次的合并，减少单个文件参与合并的次数，能够大幅的节省系统的 CPU 消耗。<img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cd2f0a547d6e4ddcb027715c4a544c5a~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"><strong>在社区 1.1 新版本的测试结果中，不论是 Compaction 的效率、CPU 的资源消耗，还是高频导入时的查询抖动，效果都有了大幅的提升。</strong></p><p><strong>具体可以参考：</strong> <a href="http://mp.weixin.qq.com/s?__biz=Mzg3Njc2NDAwOA==&amp;mid=2247500848&amp;idx=1&amp;sn=a667665ed4ccf4cf807a47be7c264f69&amp;chksm=cf2fca37f85843219e2f74d856478d4aa24d381c1d6e7f9f6a64b65f3344ce8451ad91c5af97&amp;scene=21#wechat_redirect" target="_blank" rel="noopener noreferrer">Apache Doris 1.1 特性揭秘：Flink 实时写入如何兼顾高吞吐和低延时</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="监控报警">监控报警<a class="hash-link" href="#监控报警" title="标题的直接链接">​</a></h2><p>Doris 的监控主要是通过 Prometheus 以及 Grafana 进行。对于 Doris 的报警则是通过 Falcon 进行。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3fbe6b44f1124a91bf5ee17608f302d5~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q">小米内部使用 Minos 进行集群部署。Minos 是小米内部自研并开源的大数据服务进程管理工具。在完成 Doris 集群部署后会更新至小米内部的轻舟数仓中。在轻舟数仓中的节点注册到 ZooKeeper 后，Prometheus 会监听 ZooKeeper 注册的节点，同时访问对应端口，拉取对应 Metrics 。在这之后，Grafana 会在面板上对监控信息进行显示，若有指标超过预设的报警阈值，Falcon 报警系统就会在报警群内报警，同时针对报警级别较高或某些无法及时响应的警告，可直接通过电话呼叫值班同学进行报警。</p><p>另外，小米内部针对每一个 Doris 集群都有 Cloud - Doris 的守护进程。Could - Doris 最大功能是可以对 Doris 进行可用性探测。比如我们每一分钟对 Doris 发送一次 select current timestamp(); 查询，若本次查询 20 秒没有返回，我们就会判断本次探测不可用。小米内部对每一个集群的可用性进行保证，通过上述探测方法，可以在小米内部输出 Doris 可用性指标。</p><h1>小米对 Apache Doris 的优化实践</h1><p>在应用 Apache Doris 解决业务问题的同时，我们也发现了 Apache Doris 存在的一些优化项，因此在与社区进行沟通后我们开始深度参与社区开发，解决自身问题的同时也及时将开发的重要 Feature 回馈给社区，具体包括 Stream Load 两阶段提交（2PC）、单副本数据导入、Compaction 内存限制等。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="stream-load-两阶段提交2pc">Stream Load 两阶段提交（2PC)<a class="hash-link" href="#stream-load-两阶段提交2pc" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="遇到的问题">遇到的问题<a class="hash-link" href="#遇到的问题" title="标题的直接链接">​</a></h3><p>在 Flink 和 Spark 导入数据进 Doris 的过程中，当某些异常状况发生时可能会导致如下问题：</p><p><strong>Flink 数据重复导入</strong> <strong>：</strong> Flink 通过周期性 Checkpoint 机制处理容错并实现 EOS，通过主键或者两阶段提交实现包含外部存储的端到端 EOS。Doris-Flink-Connector 1.1 之前 UNIQUE KEY 表通过唯一键实现了 EOS，非 UNIQUE KEY 表不支持 EOS。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e7750384cac44a569c8edf6c5de61744~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>Spark SQL 数据部分导入</strong> <strong>：</strong> 通过 SparkSQL 从 Hive 表中查出的数据并写入 Doris 表中的过程需要使用到 Spark Doris Connector 组件，会将 Hive 中查询的数据通过多个 Stream Load 任务写入 Doris 中，出现异常时会导致部分数据导入成功，部分导入失败。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/936ffd500f364f838a9976584727ed42~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="stream-load-两阶段提交设计"><strong>Stream Load 两阶段提交设计</strong><a class="hash-link" href="#stream-load-两阶段提交设计" title="标题的直接链接">​</a></h3><p>以上两个问题可以通过导入支持两阶段提交解决，第一阶段完成后确保数据不丢且数据不可见，这就能保证第二阶段发起提交时一定能成功，也能够保证第二阶段发起取消时一定能成功。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/50e59f3a78f74ba6a8dd2d7960497adb~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>Doris 中的写入事务分为三步：</strong></p><ol><li>在 FE 上开始事务，状态为 Prepare ；</li><li>数据写入 BE；</li><li>多数副本写入成功的情况下，提交事务，状态变成 Committed，并且 FE 向 BE 下发 Publish Version 任务，让数据立即可见。</li></ol><p>引入两阶段提交之后，第 3 步变为状态修改为 Pre Commit，Publish Version 在第二阶段完成。用户在第一阶段完成后（事务状态为 Pre Commit ），可以选择在第二阶段放弃或者提交事务。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="支持-flink-exactly-once-语义">支持 Flink Exactly-Once 语义<a class="hash-link" href="#支持-flink-exactly-once-语义" title="标题的直接链接">​</a></h3><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ef5e0a81b441487ba7c3b3fa22e8c85d~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q">Doris-Flink-Connector 1.1 使用两阶段 Stream Load 并支持 Flink 两阶段提交实现了 EOS，只有全局的 Checkpoint 完成时，才会发起 Sream Load 的第二阶段提交，否则发起第二阶段放弃。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="解决-sparksql-数据部分导入">解决 SparkSQL 数据部分导入<a class="hash-link" href="#解决-sparksql-数据部分导入" title="标题的直接链接">​</a></h3><p>Doris-Spark-Connector 使用两阶段 Stream Load 之后，成功的 Task 通过 Stream Load 第一阶段将写入数据到 Doris （Pre Commit 状态，不可见），当作业成功后，发起所有 Stream Load 第二阶段提交，作业失败时，发起所有 Stream Load 第二阶段取消。这就确保了不会有数据部分导入的问题。<img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/26b11a29566946c99b53ef90e01665ef~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="单副本数据导入优化">单副本数据导入优化<a class="hash-link" href="#单副本数据导入优化" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="单副本数据导入设计">单副本数据导入设计<a class="hash-link" href="#单副本数据导入设计" title="标题的直接链接">​</a></h3><p><strong>Doris 通过多副本机制确保数据的高可靠以及系统高可用。</strong> 写入任务可以按照使用的资源分为计算和存储两类：排序、聚合、编码、压缩等使用的是 CPU 和内存的计算资源，最后的文件存储使用存储资源，三副本写入时计算和存储资源会占用三份。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a0012b34b7404e5482700c281f6c206f~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>那能否只写一份副本数据在内存中，待到单副本写入完成并生成存储文件后，将文件同步到另外两份副本呢？答案是可行的，因此针对三副本写入的场景，我们做了单副本写入设计。<strong>单副本数据在内存中做完排序、聚合、编码以及压缩后，将文件同步至其他两个副本，这样很大程度上可以节省出 CPU 和内存资源。</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e3528e0d75184068aa3b50384cb548d1~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="性能对比测试">性能对比测试<a class="hash-link" href="#性能对比测试" title="标题的直接链接">​</a></h3><p><strong>Broker Load 导入 62G 数据性能对比</strong>
<strong>导入时间：</strong> 三副本导入耗时 33 分钟，单副本导入耗时 31 分钟。</p><p><strong>内存使用：</strong> 内存使用上优化效果十分明显，三副本数据导入的内存使用是单副本导入的三倍。单副本导入时只需要写一份内存，但是三副本导入时需要写三份内存，内存优化达到了 3 倍。</p><p><strong>CPU 消耗对比：</strong> 三副本导入的 CPU 消耗差不多是单副本的三倍。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cbe6bb648e8d47d09c556eed4ffcdfa9~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>并发场景性能对比</strong></p><p>测试中向 100 个表并发导入数据，每个表有 50 个导入任务，任务总数为 5000 个。单个 Stream Load 任务导入的数据行是 200 万行，约为 90M 的数据。测试中开了 128 个并发，<strong>将</strong> <strong>单副本导入和三副本导入进行了对比：</strong></p><p><strong>导入时间：</strong> 3 副本导入耗时 67 分钟，而后单副本耗时 27 分钟完成。导入效率相当提升两倍以上。</p><p><strong>内存使用：</strong> 单副本的导入会更低。</p><p><strong>CPU 消耗对比：</strong> 由于都已经是开了并发在导入，CPU 开销都比较高，但是单副本导入吞吐提升明显。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5a4f5533c4184f8caab39c38d951e410~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="compaction-内存限制">Compaction 内存限制<a class="hash-link" href="#compaction-内存限制" title="标题的直接链接">​</a></h3><p>之前 Doris 在单机磁盘一次导入超过 2000 个 Segment 的情况下，Compaction 有内存 OOM 的问题。对于当天写入但不查当天数据而是查询之前的数据业务场景，我们会把 Compaction 稍微放的小一点，避免占用太大的内存，导致进程 OOM。Doris 之前每个磁盘有固定的线程做存储在这个盘上的数据的 Compaction，没有办法在全局进行管控。因为我们要限制单个节点上面内存的使用，<strong>所以我们将该模式改成了生产者-消费者模式：</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ede14473f9104bdc89213e82398ba32a~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>生产者不停的从所有的磁盘上面生产任务，之后将生产任务提交到线程池中。我们可以很好的把控线程池的入口，达到对 Compaction 的限制。我们在合并时会把底层的小文件进行归并排序，之后在内存里给每一个文件开辟 Block，所以我们可以近似认为占用的内存量与文件的数量是相关的，从而可以通过对单节点上同时执行合并的文件数量做限制，来达到控制内存的效果。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/00803f23d5a0427fb57abde4a2b1ec2d~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>我们增加了对单个 BE Compaction 合并的文件数量的限制。</strong> 若正在进行的 Compaction 的文件数量超过或等于当前限制时，后续提交上来的任务就需要等待，等到前面的 Compaction 任务做完并将指标释放出来后，后边提交进来的那些任务才可以进行。</p><p>通过这种方式，我们对某些业务场景做了内存的限制，很好的避免集群负载高时占用过多内存导致 OOM 的问题。</p><h1>总结</h1><p>自从 Apache Doris 从 2019 年上线第一个业务至今，<strong>目前 Apache Doris 已经在小米内部服务了数十个业务、集群数量达到数十个、节点规模达到数百台、每天完成数万次用户在线分析查询，承担了包括增长分析和报表查询等场景绝大多数在线分析的需求。</strong></p><p>与此同时，以上所列小米对于 Apache Doris 的优化实践，已经有部分功能已经在 Apache Doris 1.0 或 1.1 版本中发布，有部分 PR 已经合入社区 Master，在不久后发布的 1.2 新版本中应该就会与大家见面。随着社区的快速发展，有越来越多小伙伴参与到社区建设中，社区活跃度有了极大的提升。Apache Doris 已经变得越来越成熟，并开始从单一计算存储一体的分析型 MPP 数据库走向湖仓一体的道路，相信在未来还有更多的数据分析场景等待去探索和实现。</p><p><strong>相关链接：</strong></p><p>SelectDB 官方网站：</p><p><a href="https://selectdb.com" target="_blank" rel="noopener noreferrer">https://selectdb.com</a></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[SelectDB 加入阿里云数据存储生态计划，携手共赢数字时代]]></title>
            <link>https://selectdb.com/blog/SelectDB 加入阿里云数据存储生态计划，携手共赢数字时代</link>
            <guid>/SelectDB 加入阿里云数据存储生态计划，携手共赢数字时代</guid>
            <pubDate>Thu, 04 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[近日，“‘数智创新、聚力同行’2022 阿里云数据存储生态发布会”在京召开，阿里云在本次会上宣布发布全新数据存储生态计划，面向行业 ISV 伙伴，通过产品集成和认证方式共建联合解决方案。]]></description>
            <content:encoded><![CDATA[<p>近日，“‘数智创新、聚力同行’2022 阿里云数据存储生态发布会”在京召开，阿里云在本次会上宣布发布全新数据存储生态计划，面向行业 ISV 伙伴，通过产品集成和认证方式共建联合解决方案。</p><p>在近期的合作伙伴大会上，阿里云确定了“坚持伙伴优先”的生态策略，并发布了“云合行动”。据阿里云透露，本次“数据存储生态计划”将是云合行动在数据存储业务领域的践行方案之一。</p><p><img loading="lazy" src="https://img.rwimg.top/53134_f589e643-bfff-4fef-b135-234a59e67bdf.jpg" class="img_ev3q"></p><p>作为一家云数仓领域的创新厂商，SelectDB 受邀出席本次发布会，并作为首批合作伙伴加入到该计划中，后续 SelectDB 将与阿里云一起携手，在联合方案、联合营销、联合销售方面进行全面合作，帮助更多企业实现数据应用创新。</p><p>SelectDB 创始人兼 CEO 连林江作为本次计划的合作伙伴代表，受邀出席了发布会现场。连林江表示，SelectDB 作为一家专注于开源技术创新和云数仓服务的科技公司，从成立伊始就开始大力投入到 Apache Doris 社区的建设中，旨在打造世界领先的分析型数据仓库项目，致力于实现技术普惠。与此同时，为了更好满足商业客户需求，基于 Apache Doris 构建的全新一代云端数据仓库 SelectDB 可以运行于多云之上，为客户提供实时、统一、极致性价比的数据仓库分析服务。</p><p>“ 自 2022 年 5 月以来，我们与阿里云存储、大数据等多个团队建立了全面的合作关系，基于阿里云以及多家云的 SelectDB Cloud 1.0 已于 7 月初发布，并已经正式对外开放申请使用。”连林江补充道，“SelectDB Cloud 1.0 采用了完全的云原生架构设计，和云计算基础设施进行了深度适配。在阿里云上，我们基于高性能云磁盘、对象存储等产品设计了冷热分离、存算分离、数据共享、备份恢复等一系列的技术，实现了资源弹性伸缩、并提供了 10 倍以上的性价比提升和众多企业特性。”</p><p><img loading="lazy" src="https://img.rwimg.top/53134_16d11fc1-8baa-4a5f-8882-d072ffa4dd1e.jpg" class="img_ev3q"></p><p>未来 SelectDB 将与阿里云持续深度合作，并基于阿里云计算、存储等强大的基础设施和技术力量，持续推动技术创新，为客户提供更好的云原生实时数据仓库服务，持续为客户创造价值。</p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[如何将 Pulsar 数据快速且无缝接入 Apache Doris]]></title>
            <link>https://selectdb.com/blog/如何将Pulsar数据快速且无缝接入Doris</link>
            <guid>/如何将Pulsar数据快速且无缝接入Doris</guid>
            <pubDate>Wed, 03 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：Apache Doris Routine Load 支持了将 Kafka 数据接入 Apache Doris，并保障了数据接入过程中的事务性操作。Apache Pulsar 定位为一个云原生时代企业级的消息发布和订阅系统。那么 Apache Pulsar 用户如何将数据接入 Apache Doris 呢？本次分享将介绍利用 KoP 如何将 Pulsar 数据快速且无缝接入 Apache Doris。]]></description>
            <content:encoded><![CDATA[<p>导读：Apache Doris Routine Load 支持了将 Kafka 数据接入 Apache Doris，并保障了数据接入过程中的事务性操作。Apache Pulsar 定位为一个云原生时代企业级的消息发布和订阅系统。那么 Apache Pulsar 用户如何将数据接入 Apache Doris 呢？本次分享将介绍利用 KoP 如何将 Pulsar 数据快速且无缝接入 Apache Doris。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="kop-架构介绍">KoP 架构介绍：<a class="hash-link" href="#kop-架构介绍" title="标题的直接链接">​</a></h2><p>KoP 是 Kafka on Pulsar 的简写，顾名思义就是如何在 Pulsar 上实现对 Kafka 数据的读写。KoP 将 Kafka 协议处理插件引入 Pulsar Broker 来实现 Apache Pulsar 对 Apache Kafka 协议的支持。将 KoP 协议处理插件添加到现有 Pulsar 集群后，用户不用修改代码就可以将现有的 Kafka 应用程序和服务迁移到 Pulsar。</p><p><strong>Apache Pulsar 主要特点如下：</strong></p><ul><li>利用企业级多租户特性简化运营。</li><li>避免数据搬迁，简化操作。</li><li>利用 Apache BookKeeper 和分层存储持久保留事件流。</li><li>利用 Pulsar Functions 进行无服务器化事件处理。</li></ul><p>KoP 架构如下图，通过图可以看到 KoP 引入一个新的协议处理插件，该协议处理插件利用 Pulsar 的现有组件（例如 Topic 发现、分布式日志库-ManagedLedger、cursor 等）来实现 Kafka 传输协议。</p><p><img loading="lazy" src="https://github.com/streamnative/kop/raw/master/docs/kop-architecture.png" alt="kop架构" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="routine-load-订阅-pulsar-数据思路">Routine Load 订阅 Pulsar 数据思路<a class="hash-link" href="#routine-load-订阅-pulsar-数据思路" title="标题的直接链接">​</a></h2><p>Apache Doris Routine Load 支持了将 Kafka 数据接入 Apache Doris，并保障了数据接入过程中的事务性操作。Apache Pulsar 定位为一个云原生时代企业级的消息发布和订阅系统，已经在很多线上服务使用。那么 Apache Pulsar 用户如何将数据数据接入 Apache Doris 呢，答案是通过 KoP 实现。</p><p>由于 Kop 直接在 Pulsar 侧提供了对 Kafka 的兼容，那么对于 Apache Doris 来说可以像使用 Kafka 一样使用 Plusar。整个过程对于 Apache Doris 来说无需任务改变，就能将 Pulsar 数据接入 Apache Doris，并且可以获得 Routine Load 的事务性保障。</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">--------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     Apache Doris       </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     ---------------    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> Routine Load </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     ---------------    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--------------------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">Kafka Protocol</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">librdkafka</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">------------v--------------</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     ---------------    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     KoP      </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">     ---------------    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">       Apache Pulsar    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--------------------------</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="操作实战">操作实战<a class="hash-link" href="#操作实战" title="标题的直接链接">​</a></h2><p><strong>一. Pulsar Standalone 安装环境准备:</strong></p><ol><li>JDK 安装：略</li><li>下载 Pulsar 二进制包，并解压：</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">#下载</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://archive.apache.org/dist/pulsar/pulsar-2.10.0/apache-pulsar-2.10.0-bin.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#解压并进入安装目录</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">tar</span><span class="token plain"> xvfz apache-pulsar-2.10.0-bin.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> apache-pulsar-2.10.0</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>二.  KoP 组件编译和安装:</strong></p><ol><li>下载 KoP 源码</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/streamnative/kop.git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> kop</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>编译 KoP 项目：</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mvn clean </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> -DskipTests</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="3"><li>protocols 配置：在解压后的 apache-pulsar 目录下创建 protocols文 件夹，并把编译好的 nar 包复制到 protocols 文件夹中。</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> apache-pulsar-2.10.0/protocols</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># mv kop/kafka-impl/target/pulsar-protocol-handler-kafka-{{protocol:version}}.nar apache-pulsar-2.10.0/protocols</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">cp</span><span class="token plain"> kop/kafka-impl/target/pulsar-protocol-handler-kafka-2.11.0-SNAPSHOT.nar apache-pulsar-2.10.0/protocols</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="4"><li>添加后的结果查看：</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">root@17a5da45700b apache-pulsar-2.10.0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token comment" style="color:#999988;font-style:italic"># ls protocols/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pulsar-protocol-handler-kafka-2.11.0-SNAPSHOT.nar</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>三. KoP 配置添加：</strong></p><ol><li>在 standalone.conf 或者 broker.conf 添加如下配置</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">#kop适配的协议</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">messagingProtocols</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">kafka</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#kop 的NAR文件路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">protocolHandlerDirectory</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">./protocols</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#是否允许自动创建topic</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">allowAutoTopicCreationType</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">partitioned</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>添加如下服务监听配置</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Use `kafkaListeners` here for KoP 2.8.0 because `listeners` is marked as deprecated from KoP 2.8.0 </span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">kafkaListeners</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">PLAINTEXT://127.0.0.1:9092</span><span class="token comment" style="color:#999988;font-style:italic"># This config is not required unless you want to expose another address to the Kafka client.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># If it’s not configured, it will be the same with `kafkaListeners` config by default</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">kafkaAdvertisedListeners</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">PLAINTEXT://127.0.0.1:9092</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">brokerEntryMetadataInterceptors</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">org.apache.pulsar.common.intercept.AppendIndexMetadataInterceptor</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">brokerDeleteInactiveTopicsEnabled</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">false</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>​	当出现如下错误：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">java.lang.IllegalArgumentException: Broker has disabled transaction coordinator, please enable it before using transaction.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>​	添加如下配置，开启 transactionCoordinatorEnabled</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">kafkaTransactionCoordinatorEnabled</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">transactionCoordinatorEnabled</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">true</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><blockquote><p>这个错误一定要修复，不然看到的现象就是使用 Kafka 自带的工具：bin/kafka-console-producer.sh和bin/kafka-console-consumer.sh在Pulsar 上进行数据的生产和消费正常，但是在 Apache Doris 中数据无法同步过来。</p></blockquote><p><strong>四. Pulsar 启动</strong></p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">#前台启动</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#bin/pulsar standalone</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#后台启动</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pulsar-daemon start standalone</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>五. 创建 Doris 数据库和建表</strong></p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">#进入Doris</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">mysql -u root  -h </span><span class="token number" style="color:#36acaa">127.0</span><span class="token plain">.0.1 -P </span><span class="token number" style="color:#36acaa">9030</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># 创建数据库</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">create database pulsar_doris</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#切换数据库</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">use pulsar_doris</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">#创建clicklog表</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE IF NOT EXISTS pulsar_doris.clicklog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">clickTime</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"> DATETIME NOT NULL COMMENT </span><span class="token string" style="color:#e3116c">"点击时间"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable builtin class-name" style="color:#36acaa">type</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"> String NOT NULL COMMENT </span><span class="token string" style="color:#e3116c">"点击类型"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable function" style="color:#d73a49">id</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain">  VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> COMMENT </span><span class="token string" style="color:#e3116c">"唯一id"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">user</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"> VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> COMMENT </span><span class="token string" style="color:#e3116c">"用户名称"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">city</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"> VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> COMMENT </span><span class="token string" style="color:#e3116c">"所在城市"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DUPLICATE KEY</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">clickTime</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain">, </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable builtin class-name" style="color:#36acaa">type</span><span class="token variable" style="color:#36acaa">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DISTRIBUTED BY HASH</span><span class="token punctuation" style="color:#393A34">(</span><span class="token variable" style="color:#36acaa">`</span><span class="token variable builtin class-name" style="color:#36acaa">type</span><span class="token variable" style="color:#36acaa">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> BUCKETS </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PROPERTIES </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">"replication_allocation"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"tag.location.default: 1"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p><strong>六. 创建 Routine Load 任务</strong></p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CREATE ROUTINE LOAD pulsar_doris.load_from_pulsar_test ON clicklog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token environment constant" style="color:#36acaa">COLUMNS</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">clickTime,id,type,user</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PROPERTIES</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"desired_concurrent_number"</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"3"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"max_batch_interval"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"20"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"max_batch_rows"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"300000"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"max_batch_size"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"209715200"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"strict_mode"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"false"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"format"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"json"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">FROM KAFKA</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"kafka_broker_list"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"127.0.0.1:9092"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"kafka_topic"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"test"</span><span class="token plain">,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"property.group.id"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"doris"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>上述命令中的参数解释如下：</p><ul><li>pulsar_doris ：Routine Load 任务所在的数据库</li><li>load_from_pulsar_test：Routine Load 任务名称</li><li>clicklog：Routine Load 任务的目标表，也就是配置 Routine Load 任务将数据导入到 Doris 哪个表中。</li><li>strict_mode：导入是否为严格模式，这里设置为 False。</li><li>format：导入数据的类型，这里配置为 Json。</li><li>kafka_broker_list：Kafka Broker 服务的地址</li><li>kafka_broker_list：Kafka Topic 名称，也就是同步哪个 Topic 上的数据。</li><li>property.group.id：消费组 ID</li></ul><p><strong>七. 数据导入和测试</strong></p><ol><li>数据导入</li></ol><p>​	    构造一个 ClickLog 的数据结构，并调用 Kafka 的 Producer 发送 5000 万条数据到 Pulsar。</p><p>​	ClickLog 数据结构如下：</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public class ClickLog </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    private String </span><span class="token function" style="color:#d73a49">id</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    private String user</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    private String city</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    private String clickTime</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    private String </span><span class="token builtin class-name">type</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain">. //省略getter和setter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>​	消息构造和发送的核心代码逻辑如下：</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">       String strDateFormat </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"yyyy-MM-dd HH:mm:ss"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       @Autowired</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       private Producer producer</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        try </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            for</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">int j </span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> j</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token number" style="color:#36acaa">50000</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">j++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              int batchSize </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                for</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">int i </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> i</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">batchSize </span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">i++</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    ClickLog clickLog  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> new ClickLog</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    clickLog.setId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">UUID.randomUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">.toString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">))</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    SimpleDateFormat simpleDateFormat </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> new SimpleDateFormat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">strDateFormat</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    clickLog.setClickTime</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">simpleDateFormat.format</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">new Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">))</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    clickLog.setType</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"webset"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    clickLog.setUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"user"</span><span class="token plain">+ new Random</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">.nextInt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> +i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    producer.sendMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Constant.topicName, JSONObject.toJSONString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">clickLog</span><span class="token punctuation" style="color:#393A34">))</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> catch </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Exception e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            e.printStackTrace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol start="2"><li>ROUTINE LOAD 任务查看
执行 SHOW ALL ROUTINE LOAD FOR load_from_pulsar_test \G;命令，查看导入任务的状态。</li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mysql</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">  SHOW ALL ROUTINE LOAD FOR load_from_pulsar_test </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain">G</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">*************************** </span><span class="token number" style="color:#36acaa">1</span><span class="token plain">. row ***************************</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                  Id: </span><span class="token number" style="color:#36acaa">87873</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                Name: load_from_pulsar_test</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          CreateTime: </span><span class="token number" style="color:#36acaa">2022</span><span class="token plain">-05-31 </span><span class="token number" style="color:#36acaa">12</span><span class="token plain">:03:34</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">           PauseTime: NULL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">             EndTime: NULL</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">              DbName: default_cluster:pulsar_doris</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">           TableName: clicklog1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">               State: RUNNING</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      DataSourceType: KAFKA</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      CurrentTaskNum: </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       JobProperties: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"partitions"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"columnToColumnExpr"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"clickTime,id,type,user"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"maxBatchIntervalS"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"20"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"whereExpr"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"dataFormat"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"json"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"timezone"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"Europe/London"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"send_batch_parallelism"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"1"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"precedingFilter"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"mergeType"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"APPEND"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"format"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"json"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"json_root"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">""</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"maxBatchSizeBytes"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"209715200"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"exec_mem_limit"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"2147483648"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"strict_mode"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"false"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"jsonpaths"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">""</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"deleteCondition"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"desireTaskConcurrentNum"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"3"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"maxErrorNum"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"0"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"strip_outer_array"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"false"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"currentTaskConcurrentNum"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"1"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"execMemLimit"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"2147483648"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"num_as_string"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"false"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"fuzzy_parse"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"false"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"maxBatchRows"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"300000"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">DataSourceProperties: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"topic"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"test"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"currentKafkaPartitions"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"0"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"brokerList"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"127.0.0.1:9092"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    CustomProperties: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"group.id"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"doris"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"kafka_default_offsets"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"OFFSET_END"</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"client.id"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"doris.client"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">           Statistic: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"receivedBytes"</span><span class="token plain">:5739001913,</span><span class="token string" style="color:#e3116c">"runningTxns"</span><span class="token plain">:</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">,</span><span class="token string" style="color:#e3116c">"errorRows"</span><span class="token plain">:0,</span><span class="token string" style="color:#e3116c">"committedTaskNum"</span><span class="token plain">:168,</span><span class="token string" style="color:#e3116c">"loadedRows"</span><span class="token plain">:50000000,</span><span class="token string" style="color:#e3116c">"loadRowsRate"</span><span class="token plain">:23000,</span><span class="token string" style="color:#e3116c">"abortedTaskNum"</span><span class="token plain">:1,</span><span class="token string" style="color:#e3116c">"errorRowsAfterResumed"</span><span class="token plain">:0,</span><span class="token string" style="color:#e3116c">"totalRows"</span><span class="token plain">:50000000,</span><span class="token string" style="color:#e3116c">"unselectedRows"</span><span class="token plain">:0,</span><span class="token string" style="color:#e3116c">"receivedBytesRate"</span><span class="token plain">:2675000,</span><span class="token string" style="color:#e3116c">"taskExecuteTimeMs"</span><span class="token plain">:2144799</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            Progress: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"0"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"51139566"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                 Lag: </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"0"</span><span class="token plain">:0</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ReasonOfStateChanged: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        ErrorLogUrls: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            OtherMsg: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> row </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.00</span><span class="token plain"> sec</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ERROR: </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">No query specified</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>从上面结果可以看到 totalRows 为 50000000，errorRows 为 0。说明数据不丢不重的导入 Apache Doris 了。</p><ol start="3"><li><p>数据统计验证</p><p>  执行如下命令统计表中的数据，发现统计的结果也是 50000000，符合预期。</p></li></ol><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mysql</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> from clicklog</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> count</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">*</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50000000</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> row </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3.73</span><span class="token plain"> sec</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">mysql</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>通过 KoP 我们实现了将 Apache Pulsar 数据无缝接入 Apache Doris ，无需对 Routine Load 任务进行任何修改，并保障了数据导入过程中的事务性。与此同时，<strong>Apache Doris 社区已经启动了 Apache Pulsar 原生导入支持的设计，相信在不久后就可以直接订阅 Pulsar 中的消息数据，并保证数据导入过程中的 Exactly-Once 语义。</strong></p><p><strong>相关链接：</strong></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org/" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>技术分享</category>
        </item>
        <item>
            <title><![CDATA[Apache Doris 1.1 特性揭秘：Flink 实时写入如何兼顾高吞吐和低延时]]></title>
            <link>https://selectdb.com/blog/Apache Doris 1.1 特性揭秘：Flink 实时写入如何兼顾高吞吐和低延时</link>
            <guid>/Apache Doris 1.1 特性揭秘：Flink 实时写入如何兼顾高吞吐和低延时</guid>
            <pubDate>Fri, 29 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：随着数据实时化需求的日益增多，数据的时效性对企业的精细化运营越来越重要，使得实时数仓在这一过程中起到了不可替代的作用。本文将基于用户遇到的问题与挑战，揭秘 Apache Doris 1.1 特性，对 Flink 实时写入 Apache Doris 的优化实现与未来规划进行详细的介绍。]]></description>
            <content:encoded><![CDATA[<blockquote><p>导读：随着数据实时化需求的日益增多，数据的时效性对企业的精细化运营越来越重要，使得实时数仓在这一过程中起到了不可替代的作用。本文将基于用户遇到的问题与挑战，揭秘 Apache Doris 1.1 特性，对 Flink 实时写入 Apache Doris 的优化实现与未来规划进行详细的介绍。</p></blockquote><h1>背景</h1><p>随着数据实时化需求的日益增多，数据的时效性对企业的精细化运营越来越重要，在海量数据中，如何能实时有效的挖掘出有价值的信息，快速的获取数据反馈，协助公司更快的做出决策，更好的进行产品迭代，<strong>实时数仓在这一过程中起到了不可替代的作用</strong>。</p><p>在这种形势下，<strong>Apache Doris 作为一款实时 MPP 分析型数据库脱颖而出</strong>，同时具备高性能、简单易用等特性，具有丰富的数据接入方式，结合 Flink 流式计算，可以让用户快速将 Kafka 中的非结构化数据以及 MySQL 等上游业务库中的变更数据，快速同步到 Doris 实时数仓中，同时 Doris 提供亚秒级分析查询的能力，可以有效地满足实时 OLAP、实时数据看板以及实时数据服务等场景的需求。</p><h1>挑战</h1><p>通常实时数仓要保证端到端高并发以及低延迟，往往面临诸多挑战，比如：</p><ul><li>如何保证端到端的<strong>秒级别数据同步</strong>？</li><li>如何快速保证<strong>数据可见性</strong>？</li><li>在高并发大压力下，如何解决<strong>大量小文件写入</strong>的问题？</li><li>如何确保端到端的 <strong>Exactly Once</strong> 语义？</li></ul><p>结合这些挑战，同时对用户使用 Flink+Doris 构建实时数仓的业务场景进行深入调研，在掌握了用户使用的痛点之后，<strong>我们在 Doris 1.1 版本中进行了针对性的优化，大幅提升实时数仓构建的用户体验，同时提升系统的稳定性，系统资源消耗也得到了大幅的优化。</strong></p><h1>优化</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="流式写入">流式写入<a class="hash-link" href="#流式写入" title="标题的直接链接">​</a></h2><p>Flink Doris Connector 最初的做法是在接收到数据后，缓存到内存 Batch 中，通过攒批的方式进行写入，同时使用 batch.size、batch.interval 等参数来控制 Stream Load 写入的时机。这种方式通常在参数合理的情况下可以稳定运行，一旦参数不合理导致频繁的 Stream Load，便会引发 Compaction 不及时，从而导致 version 过多的错误(-235)；其次，当数据过多时，为了减少 Stream Load 的写入时机，batch.size 过大的设置还可能会引发 Flink 任务的 OOM。为了解决这个问题，<strong>我们引入了流式写入</strong> <strong>：</strong> <img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7b4b7364deb34a1398c496d10890a249~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol><li><p>Flink 任务启动后，会异步发起一个 Stream Load 的 Http 请求。</p></li><li><p>接收到实时数据后，通过 Http 的分块传输编码(Chunked transfer encoding)机制持续向 Doris 传输数据。</p></li><li><p>在 Checkpoint 时结束 Http 请求，完成本次 Stream Load 写入，同时异步发起下一次 Stream Load 的请求。</p></li><li><p>继续接收实时数据，后续流程同上。</p></li></ol><p><strong>由于采用 Chunked 机制传输数据，就避免了攒批对内存的压力，同时将写入的时机和 Checkpoint 绑定起来，使得 Stream Load 的时机可控，并且为下面的 Exactly-Once 语义提供了基础。</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="exactly-once">Exactly-Once<a class="hash-link" href="#exactly-once" title="标题的直接链接">​</a></h2><p>Exactly-Once 语义是指即使在机器或应用出现故障的情况下，也不会重复处理数据或者丢失数据。Flink 很早就支持 End-to-End 的 Exactly-Once 场景，主要是通过两阶段提交协议来实现 Sink 算子的 Exactly-Once 语义。在 Flink 两阶段提交的基础上，同时借助 Doris 1.0 的 Stream Load 两阶段提交，<strong>Flink Doris Connector 实现了 Exactly Once 语义，具体原理如下：</strong></p><ol><li>Flink 任务在启动的时候，会发起一个 Stream Load 的 PreCommit 请求，此时会先开启一个事务，同时会通过 Http 的 Chunked 机制将数据持续发送到 Doris。</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9b2f143faf784500a3a8ba34063d6c2e~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol start="2"><li>在 Checkpoint 时，结束数据写入，同时完成 Http 请求，并且将事务状态设置为预提交(PreCommitted)，此时数据已经写入 BE，对用户不可见。</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e7e1d4f76a824c9a8f473e2e266defc4~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol start="3"><li>Checkpoint 完成后，发起 Commit 请求，并且将事务状态设置为提交(Committed)，完成后数据对用户可见。</li></ol><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1215aaa4dc3e44de86cdd4680ac30b00~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><ol start="4"><li>Flink 应用意外挂掉后，从 Checkpoint 重启时，若上次事务为预提交(PreCommitted)状态，则会发起回滚请求，并且将事务状态设置为 Aborted。</li></ol><p><strong>基于此，可以借助 Flink Doris Connector 实现数据实时入库时数据不丢不重。</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="秒级别数据同步">秒级别数据同步<a class="hash-link" href="#秒级别数据同步" title="标题的直接链接">​</a></h2><p>高并发写入场景下的端到端秒级别数据同步以及数据的实时可见能力，<strong>需要 Doris 具备如下几方面的能力：</strong></p><p><strong>事务处理能力</strong></p><p>Flink 实时写入以 Stream Load 2PC 的方式与 Doris 进行交互，需要 Doris 具备对应的事务处理能力，保障事务基本的 ACID 特性，在高并发场景下支撑 Flink 秒级别的数据同步。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据版本的快速聚合能力">数据版本的快速聚合能力<a class="hash-link" href="#数据版本的快速聚合能力" title="标题的直接链接">​</a></h2><p>Doris 里面一次导入会产生一个数据版本，在高并发写入场景下必然带来的一个影响是数据版本过多，且单次导入的数据量不会太大。持续的高并发小文件写入场景对 Doris 并不友好，极其考验 Doris 数据合并的实时性以及性能，进而会影响到查询的性能。<strong>Doris 在 1.1 中大幅增强了数据 Compaction 能力，对于新增数据能够快速完成聚合，避免分片数据中的版本过多导致的 -235 错误以及带来的查询效率问题。</strong></p><p><strong>首先</strong>，在 Doris 1.1 版本中，引入了 QuickCompaction，增加了主动触发式的 Compaction 检查，在数据版本增加的时候主动触发 Compaction。同时通过提升分片元信息扫描的能力，快速的发现数据版本多的分片，触发 Compaction。通过主动式触发加被动式扫描的方式，彻底解决数据合并的实时性问题。</p><p><strong>同时</strong>，针对高频的小文件 Cumulative Compaction，实现了 Compaction 任务的调度隔离，防止重量级的 Base Compaction 对新增数据的合并造成影响。</p><p><strong>最后</strong>，针对小文件合并，优化了小文件合并的策略，采用梯度合并的方式，每次参与合并的文件都属于同一个数据量级，防止大小差别很大的版本进行合并，逐渐有层次的合并，减少单个文件参与合并的次数，能够大幅的节省系统的 CPU 消耗。<img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fee6ce47ed6d4c21a34ca35c3a3ad4df~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>Doris 1.1 对高并发导入、秒级别数据同步、数据实时可见等场景都做了针对性优化，大大增加了 Flink + Doris 系统的易用性以及稳定性，节省了集群整体资源。</strong></p><h1>效果</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="通用-flink-高并发场景">通用 Flink 高并发场景<a class="hash-link" href="#通用-flink-高并发场景" title="标题的直接链接">​</a></h2><p>在调研的通用场景中，使用 Flink 同步上游 Kafka 中的非结构化数据，经过 ETL 后使用 Flink Doris Connector 将数据实时写入 Doris 中。这里客户场景极其严苛，上游维持以每秒 10w 的超高频率写入，需要数据能够在 5s 内完成上下游同步，实现秒级别的数据可见。这里 Flink 配置为 20 并发，Checkpoint 间隔 5s，Doris 1.1 的表现相当优异。<strong>具体体现在如下几个方面：</strong></p><p><strong>Compaction 实时性</strong></p><p>数据能快速合并，Tablet 数据版本个数维持在 50 以下， Compaction Score 稳定。相比于之前高并发导入频出的 -235 问题，<strong>Compaction 合并效率有 10+ 倍提升</strong>。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f622b2f235ae4bad8b2b38fd9d1f0c57~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>CPU 资源消耗</strong></p><p>Doris 1.1 针对小文件的 Compaction 进行了策略优化，在上述高并发导入场景，<strong>CPU 资源消耗下降 25%。</strong></p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec2732a96bf047e283465b04452c063a~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p><strong>QPS 查询延迟稳定</strong></p><p>通过降低 CPU 使用率，减少数据版本的个数，提升了数据整体有序性，从而减少了 SQL 查询的延迟。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9f49f45e950045c0b7913dd167c8d220~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="秒级别数据同步场景极限大压力">秒级别数据同步场景（极限大压力）<a class="hash-link" href="#秒级别数据同步场景极限大压力" title="标题的直接链接">​</a></h2><p>单 BE 单 Tablet，客户端 30 并发极限 Stream Load 压测，数据在实时性&lt;1s，Compaction Score 优化前后对比</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/209083a2f22846688f02454e306e0053~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h1>使用建议</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="数据实时可见场景">数据实时可见场景<a class="hash-link" href="#数据实时可见场景" title="标题的直接链接">​</a></h2><p>对延迟要求特别严格的场景，比如秒级别数据同步，通常意味着单次导入文件较小，此时建议调小 cumulative_size_based_promotion_min_size_mbytes，单位是 MB，默认 64，可以设置成 8，能够很大程度提升 Compaction 的实时性。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="高并发场景">高并发场景<a class="hash-link" href="#高并发场景" title="标题的直接链接">​</a></h2><p>对于高并发的写入场景，可以通过增加 Checkpoint 的间隔来减少 Stream Load 的频率，比如 Checkpoint 可以设置为 5-10s，不仅可以增加 Flink 任务的吞吐，也可以减少小文件的产生，避免给 Compaction 造成更多压力。</p><p>此外，对数据实时性要求不高的场景，比如分钟级别的数据同步，可以增加 Checkpoint 的间隔，比如 5-10 分钟，此时 Flink Doris Connector 依然能够通过两阶段提交 +checkpoint 机制来保证数据的完整性。</p><h1>未来规划</h1><p><strong>实时 Schema Change</strong></p><p>目前通过 Flink CDC 实时接入数据时，当上游业务表进行 Schema Change 操作时，必须先手动修改 Doris 中的 Schema 和 Flink 任务中的 Schema，最后再重启任务，新的 Schema 的数据才可以同步过来。这样使用方式需要人为的介入，会给用户带来极大的运维负担。<strong>后续会针对 CDC 场景做到支持 Schema 实时变更，上游的 Schema Change 实时同步到下游，全面提升 Schema Change 的效率。</strong></p><p><strong>Doris 多表写入</strong></p><p>目前 Doris Sink 算子仅支持同步单张表，所以对于整库同步的操作，需要手动在 Flink 层面进行分流，写到多个 Doris Sink 中，这无疑增加了开发者的难度，<strong>在后续版本中我们也将支持单个 Doris Sink 同步多张表，这样就大大的简化了用户的操作。</strong></p><p><strong>自适应的 Compaction 参数调优</strong></p><p>目前 Compaction 策略参数较多，在大部分通用场景能发挥较好的效果，但是在一些特殊场景下并不能高效的发挥作用。<strong>我们将在后续版本中持续优化，针对不同的场景，进行自适应的 Compaction 调优，在各类场景下提高数据合并效率，提升实时性。</strong></p><p><strong>单副本 Compaction</strong></p><p>目前的 Compaction 策略是各 BE 单独进行，<strong>在后续版本中我们将实现单副本 Compaction，通过克隆快照的方式实现 Compaction 任务，减少集群 2/3 的 Compaction 任务，降低系统的负载，把更多的系统资源留给用户侧。</strong></p><p><strong>相关链接：</strong></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>技术分享</category>
        </item>
        <item>
            <title><![CDATA[ 版本通告｜Apache Doris 1.1 Release 版本正式发布]]></title>
            <link>https://selectdb.com/blog/ 版本通告｜Apache Doris 1.1 Release 版本正式发布</link>
            <guid>/ 版本通告｜Apache Doris 1.1 Release 版本正式发布</guid>
            <pubDate>Thu, 14 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[亲爱的社区小伙伴们，我们很高兴地宣布，Apache Doris 在 2022 年 7 月 14 日迎来 1.1 Release 版本的正式发布！这是 Apache Doris 正式从 Apache 孵化器毕业后并成为 Apache 顶级项目后发布的第一个 Release 版本。在 1.1 版本中，有 90 位 Contributor 为 Apache Doris 提交了超过 450 项优化和修复，感谢每一个让 Apache Doris 变得更好的你！]]></description>
            <content:encoded><![CDATA[<p>亲爱的社区小伙伴们，我们很高兴地宣布，Apache Doris 在 2022 年 7 月 14 日迎来 1.1 Release 版本的正式发布！这是 Apache Doris 正式从 Apache 孵化器毕业后并成为 Apache 顶级项目后发布的第一个 Release 版本。在 1.1 版本中，有 90 位 Contributor 为 Apache Doris 提交了超过 450 项优化和修复，感谢每一个让 Apache Doris 变得更好的你！</p><p>在 1.1 版本中，<strong>我们实现了计算层和存储层的全面向量化、正式将向量化执行引擎作为稳定功能进行全面启用</strong>，所有查询默认通过向量化执行引擎来执行，<strong>性能较之前版本有 3-5 倍的巨大提升</strong>；增加了直接访问 Apache Iceberg 外部表的能力，支持对 Doris 和 Iceberg 中的数据进行联邦查询，<strong>扩展了 Apache Doris 在数据湖上的分析能力</strong>；在原有的 LZ4 基础上增加了 ZSTD 压缩算法，进一步提升了数据压缩率；<strong>修复了诸多之前版本存在的性能与稳定性问题</strong>，使系统稳定性得到大幅提升。欢迎大家下载使用。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="下载链接">下载链接<a class="hash-link" href="#下载链接" title="标题的直接链接">​</a></h3><p>GitHub 地址：<a href="https://github.com/apache/incubator-doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-doris</a></p><p>下载地址：<a href="https://doris.apache.org/downloads/downloads.html" target="_blank" rel="noopener noreferrer">https://doris.apache.org/downloads/downloads.html</a></p><p>源码地址：<a href="https://github.com/apache/doris/releases/tag/1.1.0-rc05" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/releases/tag/1.1.0-rc05</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="升级说明">升级说明<a class="hash-link" href="#升级说明" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="向量化执行引擎默认开启">向量化执行引擎默认开启<a class="hash-link" href="#向量化执行引擎默认开启" title="标题的直接链接">​</a></h3><p>在 Apache Doris 1.0 版本中，我们引入了向量化执行引擎作为实验性功能。用户需要在执行 SQL 查询手工开启，通过 <code>set batch_size = 4096</code> 和 <code>set enable_vectorized_engine = true </code>配置 session 变量来开启向量化执行引擎。</p><p>在 1.1 版本中，我们正式将向量化执行引擎作为稳定功能进行了全面启用，session 变量<code>enable_vectorized_engine</code> 默认设置为 true，无需用户手工开启，所有查询默认通过向量化执行引擎来执行。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="be-二进制文件更名">BE 二进制文件更名<a class="hash-link" href="#be-二进制文件更名" title="标题的直接链接">​</a></h3><p>BE 二进制文件从原有的 palo_be 更名为 doris_be ，如果您以前依赖进程名称进行集群管理和其他操作，请注意修改相关脚本。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="segment-存储格式升级">Segment 存储格式升级<a class="hash-link" href="#segment-存储格式升级" title="标题的直接链接">​</a></h3><p>Apache Doris 早期版本的存储格式为 Segment V1，在 0.12 版本中我们实现了新的存储格式 Segment V2 ，引入了 Bitmap 索引、内存表、Page Cache、字典压缩以及延迟物化等诸多特性。从 0.13 版本开始，新建表的默认存储格式为 Segment V2，与此同时也保留了对 Segment V1 格式的兼容。</p><p>为了保证代码结构的可维护性、降低冗余历史代码带来的额外学习及开发成本，我们决定从下一个版本起不再支持 Segment v1 存储格式，预计在 Apache Doris 1.2 版本中将删除这部分代码，还请所有仍在使用 Segment V1 存储格式的用户务必在 1.1 版本中完成数据格式的转换，操作手册请参考以下链接：</p><p><a href="https://doris.apache.org/zh-CN/1.0/administrator-guide/segment-v2-usage.html" target="_blank" rel="noopener noreferrer">https://doris.apache.org/zh-CN/1.0/administrator-guide/segment-v2-usage.html
</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="正常升级">正常升级<a class="hash-link" href="#正常升级" title="标题的直接链接">​</a></h3><p>正常升级操作请按照官网上的集群升级文档进行滚动升级即可。</p><p><a href="https://doris.apache.org/zh-CN/docs/admin-manual/cluster-management/upgrade.html" target="_blank" rel="noopener noreferrer">https://doris.apache.org/zh-CN/docs/admin-manual/cluster-management/upgrade.html</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="重要功能">重要功能<a class="hash-link" href="#重要功能" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="支持数据随机分布-实验性功能httpsgithubcomapachedorispull8259-8041">支持数据随机分布 <!-- -->[实验性功能][#8259]<!-- -->(<a href="https://github.com/apache/doris/pull/8259" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/8259</a>) <a href="https://github.com/apache/doris/pull/8041" target="_blank" rel="noopener noreferrer">#8041</a><a class="hash-link" href="#支持数据随机分布-实验性功能httpsgithubcomapachedorispull8259-8041" title="标题的直接链接">​</a></h3><p>在某些场景中（例如日志分析类场景），用户可能无法找到一个合适的分桶键来避免数据倾斜，因此需要由系统提供额外的分布方式来解决数据倾斜的问题。</p><p>因此通过在建表时可以不指定具体分桶键，选择使用随机分布对数据进行分桶<code>DISTRIBUTED BY random BUCKET number</code>，数据导入时将会随机写入单个 Tablet ，以减少加载过程中的数据扇出，并减少资源开销、提升系统稳定性。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="支持创建-iceberg-外部表-实验性功能httpsgithubcomapachedorispull7391-7981-8179">支持创建 Iceberg 外部表 <!-- -->[实验性功能][#7391]<!-- -->(<a href="https://github.com/apache/doris/pull/7391" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris/pull/7391</a>) <a href="https://github.com/apache/doris/pull/7981" target="_blank" rel="noopener noreferrer">#7981</a> <a href="https://github.com/apache/doris/pull/8179" target="_blank" rel="noopener noreferrer">#8179</a><a class="hash-link" href="#支持创建-iceberg-外部表-实验性功能httpsgithubcomapachedorispull7391-7981-8179" title="标题的直接链接">​</a></h3><p>Iceberg 外部表为 Apache Doris 提供了直接访问存储在 Iceberg 数据的能力。通过 Iceberg 外部表可以实现对本地存储和 Iceberg 存储的数据进行联邦查询，省去繁琐的数据加载工作、简化数据分析的系统架构，并进行更复杂的分析操作。</p><p>在 1.1 版本中，Apache Doris 支持了创建 Iceberg 外部表并查询数据，并支持通过 REFRESH 命令实现 Iceberg 数据库中所有表 Schema 的自动同步。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="增加-zstd-压缩算法-8923-9747">增加 ZSTD 压缩算法 <a href="https://github.com/apache/doris/pull/8923" target="_blank" rel="noopener noreferrer">#8923</a> <a href="https://github.com/apache/doris/pull/9747" target="_blank" rel="noopener noreferrer">#9747</a><a class="hash-link" href="#增加-zstd-压缩算法-8923-9747" title="标题的直接链接">​</a></h3><p>目前 Apache Doris 中数据压缩方法是系统统一指定的，默认为 LZ4。针对部分对数据存储成本敏感的场景，例如日志类场景，原有的数据压缩率需求无法得到满足。</p><p>在 1.1 版本中，用户建表时可以在表属性中设置<code>"compression"="zstd"</code> 将压缩方法指定为 ZSTD。在 25GB 1.1 亿行的文本日志测试数据中，<strong>最高获得了近 10 倍的压缩率、较原有压缩率提升了 53%，从磁盘读取数据并进行解压缩的速度提升了 30%</strong> 。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="功能优化">功能优化<a class="hash-link" href="#功能优化" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="更全面的向量化支持"><strong>更全面的向量化支持</strong><a class="hash-link" href="#更全面的向量化支持" title="标题的直接链接">​</a></h3><p>在 1.1 版本中，我们实现了计算层和存储层的全面向量化，包括：</p><ul><li><p>实现了所有内置函数的向量化</p></li><li><p>存储层实现向量化，并支持了低基数字符串列的字典优化</p></li><li><p>优化并解决了向量化引擎的大量性能和稳定性问题。</p></li></ul><p>我们对 Apache Doris 1.1 版本与 0.15 版本分别在 SSB 和 TPC-H 标准测试数据集上进行了性能测试：</p><ul><li>在 SSB 测试数据集的全部 13 个 SQL 上，1.1 版本均优于 0.15 版本，整体性能约提升了 3 倍，解决了 1.0 版本中存在的部分场景性能劣化问题；</li></ul><ul><li>在 TPC-H 测试数据集的全部 22 个 SQL 上，1.1 版本均优于 0.15 版本，整体性能约提升了 4.5 倍，部分场景性能达到了十余倍的提升；</li></ul><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/edb59781b0f74ff08821467f23a63bad~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p align="center">SSB 测试数据集</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e34377054f4448b3b367789a391f2122~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p align="center">TPC-H 测试数据集</p><p><strong>性能测试报告：</strong></p><p><a href="https://doris.apache.org/zh-CN/docs/benchmark/ssb.html" target="_blank" rel="noopener noreferrer">https://doris.apache.org/zh-CN/docs/benchmark/ssb.html</a></p><p><a href="https://doris.apache.org/zh-CN/docs/benchmark/tpch.html" target="_blank" rel="noopener noreferrer">https://doris.apache.org/zh-CN/docs/benchmark/tpch.html</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="compaction-逻辑优化与实时性保证-10153">Compaction 逻辑优化与实时性保证 <a href="https://github.com/apache/doris/pull/10153" target="_blank" rel="noopener noreferrer">#10153</a><a class="hash-link" href="#compaction-逻辑优化与实时性保证-10153" title="标题的直接链接">​</a></h3><p>在 Apache Doris 中每次 Commit 都会产生一个数据版本，在高并发写入场景下，容易出现因数据版本过多且 Compaction 不及时而导致的 -235 错误，同时查询性能也会随之下降。</p><p>在 1.1 版本中我们引入了 QuickCompaction，增加了主动触发式的 Compaction 检查，在数据版本增加的时候主动触发 Compaction，同时通过提升分片元信息扫描的能力，快速发现数据版本过多的分片并触发 Compaction。通过主动式触发加被动式扫描的方式，彻底解决数据合并的实时性问题。</p><p>同时，针对高频的小文件 Cumulative Compaction，实现了 Compaction 任务的调度隔离，防止重量级的 Base Compaction 对新增数据的合并造成影响。</p><p>最后，针对小文件合并，优化了小文件合并的策略，采用梯度合并的方式，每次参与合并的文件都属于同一个数据量级，防止大小差别很大的版本进行合并，逐渐有层次的合并，减少单个文件参与合并的次数，能够大幅地节省系统的 CPU 消耗。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a6d5c50f16a048f3ab27357bc97b7461~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><p>在数据上游维持每秒 10w 的写入频率时（20 个并发写入任务、每个作业 5000 行、 Checkpoint 间隔 1s），1.1 版本表现如下：</p><ul><li>数据快速合并：Tablet 数据版本维持在 50 以下，Compaction Score 稳定。相较于之前版本高并发写入时频繁出现的 -235 问题，<strong>Compaction 合并效率有 10 倍以上的提升</strong>。</li></ul><ul><li>CPU 资源消耗显著降低：针对小文件 Compaction 进行了策略优化，在上述高并发写入场景下，<strong>CPU 资源消耗降低 25%</strong> ；</li></ul><ul><li>查询耗时稳定：提升了数据整体有序性，大幅降低查询耗时的波动性，<strong>高并发写入时的查询耗时与仅查询时持平</strong>，查询性能较之前版本<strong>有 3-4 倍提升</strong>。</li></ul><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1c79ee9efba0416d81cc7bed1a349fdf~tplv-k3u1fbpfcp-zoom-1.image" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="parquet-和-orc-文件的读取效率优化-9472">Parquet 和 ORC 文件的读取效率优化 <a href="https://github.com/apache/doris/pull/9472" target="_blank" rel="noopener noreferrer">#9472</a><a class="hash-link" href="#parquet-和-orc-文件的读取效率优化-9472" title="标题的直接链接">​</a></h3><p>通过调整 Arrow 参数，利用 Arrow 的多线程读取能力来加速 Arrow 对每个 row_group 的读取，并修改成 SPSC 模型，通过预取来降低等待网络的代价。优化前后对 Parquet 文件导入的性能有 4 ～ 5 倍的提升。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="更安全的元数据-checkpoint-9180-9192">更安全的元数据 Checkpoint <a href="https://github.com/apache/doris/pull/9180" target="_blank" rel="noopener noreferrer">#9180</a> <a href="https://github.com/apache/doris/pull/9192" target="_blank" rel="noopener noreferrer">#9192</a><a class="hash-link" href="#更安全的元数据-checkpoint-9180-9192" title="标题的直接链接">​</a></h3><p>通过对元数据检查点后生成的 image 文件进行双重检查和保留历史 image 文件的功能，解决了 image 文件错误导致的元数据损坏问题。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="bug-修复">Bug 修复<a class="hash-link" href="#bug-修复" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="修复由于缺少数据版本而无法查询数据的问题严重9267-9266">修复由于缺少数据版本而无法查询数据的问题。（严重）<a href="https://github.com/apache/doris/pull/9267" target="_blank" rel="noopener noreferrer">#9267</a> <a href="https://github.com/apache/doris/pull/9266" target="_blank" rel="noopener noreferrer">#9266</a><a class="hash-link" href="#修复由于缺少数据版本而无法查询数据的问题严重9267-9266" title="标题的直接链接">​</a></h3><p>问题描述：<code>failed to initialize storage reader. tablet=924991.xxxx, res=-214, backend=xxxx</code></p><p>该问题是在版本 1.0 中引入的，可能会导致多个副本的数据版本丢失。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="解决了资源隔离对加载任务的资源使用限制无效的问题中等9492">解决了资源隔离对加载任务的资源使用限制无效的问题（中等）<a href="https://github.com/apache/doris/pull/9492" target="_blank" rel="noopener noreferrer">#9492</a><a class="hash-link" href="#解决了资源隔离对加载任务的资源使用限制无效的问题中等9492" title="标题的直接链接">​</a></h3><p>在 1.1 版本中， Broker Load 和 Routine Load 将使用具有指定资源标记的 BE 节点进行加载。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="修复使用-http-brpc-超过-2gb-传输网络数据包导致数据传输错误的问题中等9770">修复使用 HTTP BRPC 超过 2GB 传输网络数据包导致数据传输错误的问题（中等）<a href="https://github.com/apache/doris/pull/9770" target="_blank" rel="noopener noreferrer">#9770</a><a class="hash-link" href="#修复使用-http-brpc-超过-2gb-传输网络数据包导致数据传输错误的问题中等9770" title="标题的直接链接">​</a></h3><p>在以前的版本中，当通过 BRPC 在后端之间传输的数据超过 2GB 时，可能会导致数据传输错误。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="其他">其他<a class="hash-link" href="#其他" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="禁用-mini-load">禁用 Mini Load<a class="hash-link" href="#禁用-mini-load" title="标题的直接链接">​</a></h3><p>Mini Load 与 Stream Load 的导入实现方式完全一致，都是通过 HTTP 协议提交和传输数据，在导入功能支持上 Stream Load 更加完备。</p><p>在 1.1 版本中，默认情况下 Mini Load 接口 <code>/_load</code> 将处于禁用状态，请统一使用 Stream Load 来替换 Mini Load。您也可以通过关闭 FE 配置项 <code>disable_mini_load</code> 来重新启用 Mini Load 接口。在版本 1.2 中，将彻底删除 Mini Load 。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="完全禁用-segmentv1-存储格式">完全禁用 SegmentV1 存储格式<a class="hash-link" href="#完全禁用-segmentv1-存储格式" title="标题的直接链接">​</a></h3><p>在 1.1 版本中将不再允许新创建 SegmentV1 存储格式的数据，现有数据仍可以继续正常访问。</p><p>您可以使用 ADMIN SHOW TABLET STORAGE FORMAT 语句检查集群中是否仍然存在 SegmentV1 格式的数据，如果存在请务必通过数据转换命令转换为 SegmentV2。</p><p>在 Apache Doris 1.2 版本中不再支持对 Segment V1 数据的访问，同时 Segment V1 代码将被彻底删除。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="限制-string-类型的最大长度-8567">限制 String 类型的最大长度 <a href="https://github.com/apache/doris/pull/8567" target="_blank" rel="noopener noreferrer">#8567</a><a class="hash-link" href="#限制-string-类型的最大长度-8567" title="标题的直接链接">​</a></h3><p>String 类型是 Apache Doris 在 0.15 版本中引入的新数据类型，在过去 String 类型的最大长度允许为 2GB。</p><p>在 1.1 版本中，我们将 String 类型的最大长度限制为 1 MB，超过此长度的字符串无法再写入，同时不再支持将 String 类型用作表的 Key 列、分区列以及分桶列。</p><p>已写入的字符串类型可以正常访问。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="修复-fastjson-相关漏洞-9763">修复 fastjson 相关漏洞 <a href="https://github.com/apache/doris/pull/9763" target="_blank" rel="noopener noreferrer">#9763</a><a class="hash-link" href="#修复-fastjson-相关漏洞-9763" title="标题的直接链接">​</a></h3><p>对 Canal 版本进行更新以修复 fastjson 安全漏洞</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="添加了-admin-diagnose-tablet-命令-8839">添加了 ADMIN DIAGNOSE TABLET 命令 <a href="https://github.com/apache/doris/pull/8839" target="_blank" rel="noopener noreferrer">#8839</a><a class="hash-link" href="#添加了-admin-diagnose-tablet-命令-8839" title="标题的直接链接">​</a></h3><p>通过 ADMIN DIAGNOSE TABLET tablet_id 命令可以快速诊断指定 Tablet 的问题。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="下载使用">下载使用<a class="hash-link" href="#下载使用" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="下载链接-1">下载链接<a class="hash-link" href="#下载链接-1" title="标题的直接链接">​</a></h3><p><a href="http://doris.apache.org/zh-CN/downloads/downloads.html" target="_blank" rel="noopener noreferrer">http://doris.apache.org/zh-CN/downloads/downloads.html</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="升级说明-1">升级说明<a class="hash-link" href="#升级说明-1" title="标题的直接链接">​</a></h3><p>您可以从 Apache Doris 1.0 Release 版本和 1.0.x 发行版本升级到 1.1 Release 版本，升级过程请官网参考文档。如果您当前是 0.15 Release 版本或 0.15.x 发行版本，可跳过 1.0 版本直接升级至 1.1。</p><p><a href="http://doris.apache.org/zh-CN/installing/upgrade.html" target="_blank" rel="noopener noreferrer">http://doris.apache.org/zh-CN/installing/upgrade.html</a></p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="意见反馈">意见反馈<a class="hash-link" href="#意见反馈" title="标题的直接链接">​</a></h4><p>如果您遇到任何使用上的问题，欢迎随时通过 GitHub Discussion 论坛或者 Dev 邮件组与我们取得联系。</p><p>GitHub 论坛：<a href="https://github.com/apache/incubator-doris/discussions" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-doris/discussions</a></p><p>Dev 邮件组：<a href="/blog/dev@doris.apache.org">dev@doris.apache.org</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="致谢">致谢<a class="hash-link" href="#致谢" title="标题的直接链接">​</a></h2><p>Apache Doris 1.1 Release 版本的发布离不开所有社区用户的支持，在此向所有参与版本设计、开发、测试、讨论的社区贡献者们表示感谢，他们分别是：</p><p>@adonis0147</p><p>@airborne12</p><p>@amosbird</p><p>@aopangzi</p><p>@arthuryangcs</p><p>@awakeljw</p><p>@BePPPower</p><p>@BiteTheDDDDt</p><p>@bridgeDream</p><p>@caiconghui</p><p>@cambyzju</p><p>@ccoffline</p><p>@chenlinzhong</p><p>@daikon12</p><p>@DarvenDuan</p><p>@dataalive</p><p>@dataroaring</p><p>@deardeng</p><p>@Doris-Extras</p><p>@emerkfu</p><p>@EmmyMiao87</p><p>@englefly</p><p>@Gabriel39</p><p>@GoGoWen</p><p>@gtchaos</p><p>@HappenLee</p><p>@hello-stephen</p><p>@Henry2SS</p><p>@hewei-nju</p><p>@hf200012</p><p>@jacktengg</p><p>@jackwener</p><p>@Jibing-Li</p><p>@JNSimba</p><p>@kangshisen</p><p>@Kikyou1997</p><p>@kylinmac</p><p>@Lchangliang</p><p>@leo65535</p><p>@liaoxin01</p><p>@liutang123</p><p>@lovingfeel</p><p>@luozenglin</p><p>@luwei16</p><p>@luzhijing</p><p>@mklzl</p><p>@morningman</p><p>@morrySnow</p><p>@nextdreamblue</p><p>@Nivane</p><p>@pengxiangyu</p><p>@qidaye</p><p>@qzsee</p><p>@SaintBacchus</p><p>@SleepyBear96</p><p>@smallhibiscus</p><p>@spaces-X</p><p>@stalary</p><p>@starocean999</p><p>@steadyBoy</p><p>@SWJTU-ZhangLei</p><p>@Tanya-W</p><p>@tarepanda1024</p><p>@tianhui5</p><p>@Userwhite</p><p>@wangbo</p><p>@wangyf0555</p><p>@weizuo93</p><p>@whutpencil</p><p>@wsjz</p><p>@wunan1210</p><p>@xiaokang</p><p>@xinyiZzz</p><p>@xlwh</p><p>@xy720</p><p>@yangzhg</p><p>@Yankee24</p><p>@yiguolei</p><p>@yinzhijian</p><p>@yixiutt</p><p>@zbtzbtzbt</p><p>@zenoyang</p><p>@zhangstar333</p><p>@zhangyifan27</p><p>@zhannngchen</p><p>@zhengshengjun</p><p>@zhengshiJ</p><p>@zingdle</p><p>@zuochunwei</p><p>@zy-kkk</p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[知乎基于 Apache Doris 的 DMP 平台架构建设实践]]></title>
            <link>https://selectdb.com/blog/知乎基于 Apache Doris 的 DMP 平台架构建设实践</link>
            <guid>/知乎基于 Apache Doris 的 DMP 平台架构建设实践</guid>
            <pubDate>Tue, 12 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：知乎基于业务需求搭建了 DMP 平台，本文详细的介绍了 DMP 的工作原理及架构演进过程，同时介绍了 Apache Doris 在 DMP 平台的应用实践，本文对大家了解 DMP 工作方式很有帮助，欢迎阅读。]]></description>
            <content:encoded><![CDATA[<blockquote><p>导读：知乎基于业务需求搭建了 DMP 平台，本文详细的介绍了 DMP 的工作原理及架构演进过程，同时介绍了 Apache Doris 在 DMP 平台的应用实践，本文对大家了解 DMP 工作方式很有帮助，欢迎阅读。</p></blockquote><h1>DMP 业务背景</h1><p>DMP 平台是大家老生常谈的话题。在早期广告系统出现之后就拥有了类似的 DMP 平台，比如：腾讯的广点通、阿里巴巴的达摩盘等都是业界做的比较好的 DMP 平台典型。而知乎搭建属于自己的 DMP 平台，一方面是因为知乎有相关的站内运营业务；另一方面也是因为我们可以通过搭建 DMP 平台支持内部系统对接、同时还可以协助完成相关业务发展以及定制化需求建设的目的。</p><p>DMP 业务包含：业务模式、业务场景以及业务需求。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/70566a52ea2b4e66adb4e8c034d86785~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 1.1 DMP 业务</p><p>DMP 平台设计的方向：为了找到我们的核心客户，并在后续对我们的核心客户完成如广告投放等营销操作，让核心客户跟我们的内容之间能够有更好的匹配。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务模式">业务模式<a class="hash-link" href="#业务模式" title="标题的直接链接">​</a></h2><p>DMP 平台业务模式</p><ul><li><strong>从站外转站内</strong>。典型场景是广告主在进行广告投放的过程中，如何通过 Mapping 将可能出现的站外人群转到站内，并在站内的系统上承接这些用户包。</li><li><strong>从站内转站外。</strong> 在知乎内先找到定向用户后再去用这些用户在三方投广告。</li><li><strong>站内运营。</strong> 包括内容运营，用户运营以及活动运营。一方面可以增加知乎相关内容的宣传，另一方面进行客户定位并精准解决某些客户的问题与需求。与此同时，我们也可以通过活动设计来提升业务效果。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务场景">业务场景<a class="hash-link" href="#业务场景" title="标题的直接链接">​</a></h2><p>基于这三种业务模式，<strong>主要应用的业务场景：</strong></p><ul><li><strong>信息流方面。</strong> 拿推荐场景举例：推荐场景中会有定向推荐以及定向提权两种诉求。定向推荐是我们把推送内容定向推送给某些用户，而定向提权是我们把推送内容在被推送的用户身上完成提权并重新打分。</li><li><strong>广告侧实时竞价。</strong> 得知该用户身上挂了哪些广告之后可以进行实时竞价，通过排序选出最适合该用户的广告。</li><li><strong>详情页。</strong> 详情页中会有弹窗提示：比如说某个用户点击某个详情之后，若该用户没有达到目标条件，会弹窗引导来该用户达到条件。</li><li><strong>活动平台。</strong> 设置活动的目标用户。针对不一样的目标群体，展示不同的活动信息。</li><li><strong>触达系统。</strong> 比如在推送消息、弹窗和短信时，可以拿到一类具体的用户，之后向该类用户进行发布相应的 Push 和站内信等。</li><li><strong>站外投放。</strong> 找到合适的用户群并在站外为其投放相应的广告。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务需求">业务需求<a class="hash-link" href="#业务需求" title="标题的直接链接">​</a></h2><p>基于业务模式场景，在人群方面能做的事情可以分为三类：</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="对接系统">对接系统<a class="hash-link" href="#对接系统" title="标题的直接链接">​</a></h3><p>一般分为以下 3 种情况：</p><ul><li>该用户命中了哪些人群包。拿广告系统为例，该人群包 ID 可以 Mapping 成一个广告，也就是该用户命中了哪些广告。</li><li>内部人群包。人群包对内部而言就是把内容推荐给谁，或者给谁发布内容的 Push。</li><li>对外部的广告。当我们筛选出一类用户需要投放在站外时，这时候就是在使用对外部的人群包。对于这两个人群包之间的区别而言，人群 ID 会有不同：一种是站内的通用 ID，另外一种是基于不同投放平台上对应的对外 ID。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="人群定向"><strong>人群定向</strong><a class="hash-link" href="#人群定向" title="标题的直接链接">​</a></h3><p>人群定向包括导入/导出、基于某些特征进行标签圈选、人群泛化、用户量预估等。</p><ul><li>人群泛化，拿到比较小的种子人群包后，基于规则寻找相似特征，再通过对相似特征的置信度进行调整，扩展更多的人群。</li><li>用户量预估，选中一批用户后需要立即了解这批用户的数量有多少。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="人群洞察"><strong>人群洞察</strong><a class="hash-link" href="#人群洞察" title="标题的直接链接">​</a></h3><p>包括画像洞察，用户的内部画像以及两个不同人群包之间的对比分析。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务流程">业务流程<a class="hash-link" href="#业务流程" title="标题的直接链接">​</a></h2><p>由于当前 DMP 业务的三种场景面向人群不同，会提供向站内与站外不同系统来完成这批人群相关的运营动作。</p><p>据此情况，我们组织人群定向功能、获取到目标用户之后进行 Mapping ，拿到用户在站内或站外投放的效果回收之后，获取目标用户进行构成分析与对比分析，进行用户洞察。若目标达成，那么本次投放顺利达成；若目标未达成，运营侧会做相关假设：是否可以再加一个特征或特殊操作进一步提升业务？提出假设之后，设计 AB 实验，经过 AB 试验后，我们又会对目标人群进行一些调整。以上就是我们的运营流程。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/073ccb98b8d44895b7537b1edf2790f6~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 1.2 DMP 业务流程</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="站内运营自闭环"><strong>站内运营自闭环</strong><a class="hash-link" href="#站内运营自闭环" title="标题的直接链接">​</a></h3><p><strong>人群定向。</strong> 通过标签圈选，选择历史上有活动效果或导入喜欢此活动的人群，进行泛化完成基础人群包选择，以此来确定目标人群。</p><p><strong>进行投放。</strong> 由于很多业务在推荐侧的信息流、触达系统、详情系统以及广告引擎等系统中进行对接，可以利用以上系统和业务来完成对目标用户在站内不同流量场景投放。</p><p><strong>投放之后。</strong> 获取本次投放的效果并进行分析。比如我们做的操作是发 Push，谁点击了 Push、阅读时间等行为，可以分析有哪些用户更喜欢我们此次发布的 Push，从而获得目标用户的典型特征。</p><p>若此次 Push 的点击量达成推送目标，那么目标完成；若点击量没有达成目标，我们会进行一个假设，比如最初预测点击 Push 的男性＞女性，但最后得出的结果相反时，我们会通过 TGI 算法进行排序，找出这两次差别的典型特征，完成设计并产出 AB 实验。</p><p>通过 AB 实验我们可以对前后的人群包再做一次对比并发布相关的 Push。如果点击量有所提升，我们在后续过程中就会不断的完成循环，最终找到基于我们运营场景的领域的精准用户。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="站内向站外投放"><strong>站内向站外投放</strong><a class="hash-link" href="#站内向站外投放" title="标题的直接链接">​</a></h3><p>基于已经积累的用户特征数据，找出在知乎内部有几率产生站外效果的人群，并划出该类人群的范围。再通过 Mapping，可以把站内的 ID 转换成在三方投放平台上产出的 ID 并进行投放。</p><p>由于这个过程我们的站内系统不同，并不能直接拿到相应的埋点数据供我们进行数据链路建设，所以就必须要通过三方投放平台上下载相应的埋点数据，通过类似的场景完成数据导入后再进行后续流程的建设。这也就导致了整个过程的效果回收会比较长。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="站外转站内"><strong>站外转站内</strong><a class="hash-link" href="#站外转站内" title="标题的直接链接">​</a></h3><p>假如我是一名知乎站外的广告主，我要投放一个牙膏类的产品，但是我对知乎的用户并不是特别了解。通过前期所做的运营调研，可以发现历史购买牙膏的人群包是什么样子。那么就可以把前期调研所得到的人群包通过 ID Mapping 转换为知乎 ID 并导入生成目标人群。但是广告主拿到购买牙膏的人群可能存在与知乎用户重合度较低的情况。这时候启用第二个功能，也就是人群泛化功能。</p><p>人群泛化会把导入人数较少的种子人群连接到知乎，这个过程可以对用户达成的所有特征在 AI 模型中完成训练。可以训练出种子人群在知乎所有用户特征下的模型是什么，之后再把所有用户的全部特征灌入得到的模型之中进行推理。这样得到带有置信度的目标用户。</p><p>若广告主认为基于之前的调研结果来看，相关目标人群在知乎中为 1,000 万左右，此时我们就可以选择对于目标用户的置信度。比如说当置信度为 0.7 时，得到结果为 2,000 万；之后我们把置信度调整为 0.8 时，得到结果为 1,000 万，此时我们就可以选择 0.8 的置信度完成广告引擎的对接并进行投放后分析效果。</p><p>基于上述运营流程，我们可以抽象出 DMP 平台最核心的功能包括洞察、定向以及 ID mapping。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="画像特征">画像特征<a class="hash-link" href="#画像特征" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7a7fc176ddbf40d7beb4f9de3f753b69~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 1.3 DMP 画像特征</p><p>我们根据上述的用户画像，构建出了画像特征。其中标签是最重要的部分，也是离散部分。连续部分包括了用户的停留时长以及相关的用户行为，比如：某人在某地做了什么事等，这些都属于连续特征。特征方面，在该特征还没有打上标签之前，我们会统称为普通特征。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="功能梳理">功能梳理<a class="hash-link" href="#功能梳理" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/821bab1e3afc45fea7410227ac2cd4ce~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 1.4 DMP 功能梳理</p><p>基于 DMP 平台的功能，向右侧拓展为业务功能。业务功能会服务于运营、销售或站内的应用系统，包括人群定向、人群洞察以及相关的 ID Mapping。向左侧拓展是信息量巨大且十分重要的特征接入部分。</p><p>当前 DMP 平台由于单从标签方面就有 250 万的标签量级，在用户 X 标签也有 1100 亿相关用户数据，同时业务方面对部分标签具有实时性要求。这也就导致在特征接入过程中需要做很多事情。</p><p>接下来将为大家介绍具体功能。</p><ul><li><strong>人群定向。</strong> 人群定向方面整体上分为导入与导出、特征圈选以及人群泛化这三个功能。</li><li><strong>人群洞察。</strong> 包括构成分析和对比分析两种功能。构成分析部分我们可以简单理解为一个饼图或柱状图。对比分析是多个人群对比分析。</li><li><strong>ID Mapping。</strong> 整体上将无论是 oai、idfa、手机号，全部硬生成知乎的连续统一 ID，而且这个连续 ID 基本是严格自增的。</li><li>特征接入</li><li><ul><li>建设方式分为实时特征及离线特征</li><li>标签组方面有离线和实时两种接入方式。其中树状标签主要用来应对复杂场景，如用户对某话题在阅读和互动方面的是多选的树形结构。</li></ul></li></ul><h1>DMP 架构与实现</h1><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0de0c26f15a747c6ba708f02dd9859c8~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 2.1 DMP 业务架构</p><p>我认为架构对于实现最终目标是很重要的阶段，但并不是必要阶段。只要我们把所有功能都进行完善就可以完成我们所有的业务实践，但是这样会导致在系统经过不断膨胀后，所对应的维护成本也会不断变高，稳定性变差，最后导致没有人可以维护的窘迫情况发生。架构主要可以为我们解决在多个复杂业务功能场景下，如何以低成本的方式进行维护迭代并有目标的去针对某个模块进行优化，但并不能解决实际的业务功能问题。</p><p><strong>基于以上我对架构的认知，对业务以及整体 DMP 架构进行拆解：</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="dmp-使用用户">DMP 使用用户<a class="hash-link" href="#dmp-使用用户" title="标题的直接链接">​</a></h2><p>DMP 系统对接的是 3 类用户：</p><ul><li>平台方面，包括广告平台、信息流、广告引擎以及触达系统。</li><li>操作人员，包括运营、投放以及销售等业务相关操作人员。</li><li>诸如特征开发的产品及相关内部产品。</li></ul><p>而这三部分人所对接的最前台的系统也是不一样的。</p><p>首先我们认为，平台或系统方面会与 DMP 的接口层对接。<strong>接口主要分为三种：</strong></p><p><strong>第一种接口是诸如广告引擎和信息流经常请求用户命中了哪些人群包列表。</strong> 在广告引擎内，完成请求之后就可以直接把人群包列表变成某个广告 ID 并完成竞价。信息流与广告引擎类似：当前用户若命中了我们要提权某内容或领域标签时，我们就会进行提权。该接口的设计就是典型的高稳定性、高并发、高吞吐。我们可以通过线上数据来进行该接口与其他接口的承载差别对比：该接口当前承载了 10 万 qps，由于接口对接了公司的核心系统，因此不能有任何抖动与故障，对它的稳定性要求达到 S 级，所以该接口也有多机缓存和高并发方面的相关设计，需要能够达到高稳定性、高并发、高吞吐的目标。</p><p><strong>第二部分是站内与站外的人群包</strong>，该部分和上述内容也比较类似，都会对接到我们最核心的系统。一旦人群包无法圈选人群，后面整体的营销与定向投放也都会受到影响。对于 DMP 前台部分，该部分和接口层存在着明显区别：DMP 前台主要对接的是我们的内部运营同学与销售同学。DMP 前台若产生异常情况，只是会不能进行新的洞察以及人群定向的，不会影响正常使用历史人群。由于该部分会对接众多的销售和人群而不是对接重请求的接口，使用的复杂性也就必须要降到最低，减少在运营方面的培训成本，所以 DMP 前台就需要具备操作简单且使用成本低的特点。</p><p><strong>第三方面是对接我们的内部系统</strong>，这部分主要会降低我们日常开发的成本。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="dmp-核心功能">DMP 核心功能<a class="hash-link" href="#dmp-核心功能" title="标题的直接链接">​</a></h3><p>DMP 能够支持人群圈选、泛化、人群洞察的核心业务模块；支持标签生产， ID Mapping 还有计算任务运维和存储方面的功能。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="dmp-业务模块">DMP 业务模块<a class="hash-link" href="#dmp-业务模块" title="标题的直接链接">​</a></h3><p>DMP 业务模块分为上下两层，向上的业务层实现新增功能的低成本化，重点在于可扩展性；向下的业务层随着人群与业务功能的增长，整体的开发或技术投入成本不会有太大的产出，也就是资源上的可扩展性。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="dmp-基础设施">DMP 基础设施<a class="hash-link" href="#dmp-基础设施" title="标题的直接链接">​</a></h3><p>最下方是基础设施，需要保证基础设施相关的稳定性。</p><p>我们判断接口的依据是请求的接口主要承载是 Redis；<strong>Doris 主要承载了 DMP 前台和整体业务功能</strong>；后台部分主要承载是 MySQL 与 TiDB。以上是我们当前具体底层数据库的相关承载方面。</p><p>有人会问 Redis 成本是否会太高？不会的。因为<strong>核心的圈选人群逻辑都是在 Doris 上实现的</strong>，存放的大量相关标签都是通过 Doris 进行存放，只有在某个广告要指定某目标人群的某几个特征进行排列组合并且完成泛化时，我们会圈选出某个人群包 ID 对应的结果，最后才导出存放到 Redis 中。因此 Redis 的主要目的是用来扛高并发，实际的存放量很少。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="dmp-平台功能盘点">DMP 平台功能盘点<a class="hash-link" href="#dmp-平台功能盘点" title="标题的直接链接">​</a></h3><p>功能盘点主要分为业务向与基础向两部分。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a72bee73ea1c497fb2f83ff093233e86~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 2.2 DMP 平台功能盘点-业务向</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="业务向"><strong>业务向</strong><a class="hash-link" href="#业务向" title="标题的直接链接">​</a></h4><p>业务向我们能够支持人群定向以及人群洞察两部分能力。</p><p><strong>人群定向：</strong></p><ul><li>人群预估：比如说对性别、年龄、感兴趣的话题、该用户手机品牌是等多个条件进行排列组合，要求能够在 1 秒内完成精确结构的人群特征量级预估。</li><li>人群圈选：经过精确结构的人群数量预估后，可以在分钟级别内将预估结果转化为要进行投放和使用的相关人群包。</li><li>人群包泛化：泛化的能力要求尽可能简单，比如说我选择有历史的人群包后，就可以进行人群泛化并有具体的执行度选择。</li></ul><p><strong>人群洞察：</strong></p><ul><li>可以探索当前活动入口画像，并完成流量回收。比如说我向 100 万人发布了推送，其中有 3 万人点击，那么可以对这 3 万人进行流量回收，与已推送的 100 万人进行对比，就可以这 3 万人明显的用户特征，方便我们后续提取出更精准的用户群体。</li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="基础向"><strong>基础向</strong><a class="hash-link" href="#基础向" title="标题的直接链接">​</a></h4><p>另外 DMP 架构还有一些基础功能，包括了主要特征建设、ID mapping 以及计算任务运维。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d7989dfe5824b76abb19d3c863c4fab~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 2.3 DMP 平台功能盘点-基础向</p><p>这三个基础功能不仅可以让我们快速完成实时和批量计算，还能够帮助我们解决新老版本滚动上线的问题。因为我们当前无论是通过 AI、数据采买、特征筛选，找到一个用户，即使是性别这种最基础的特征，也是在不断优化的过程，但每次优化是没有办法快速进行运营影响的评估，因此就需要做到多版本灰度上线，并进行滚动上线。</p><p><strong>特征建设</strong></p><p>特征整体有两部分，一种是原子特征，一种是派生特征。</p><ul><li>在建设原子特征时，我们就需要从离线或实时数据中生产大量相同基准的特征。</li><li>对于派生特征，会基于已生产的特征再生产一个特征。举例：假如我们认为某群体是高消费能力群体，放在一个简单的场景中，我们可能会圈选出一位在 18-25 岁之间并在一二线城市的女性，并认为这样的特征可能是对化妆品消费能力比较高的群体特征。之后我们就会把该特征作为派生特征进行存储并去加快后续计算速度并降低运营筛选的成本。</li></ul><p>特征建设可以做到能力隔离，以此来提升我们特征建设和上线效率。</p><p><strong>Mapping 能力</strong></p><p>包括设备 ID Mapping、用户特征 ID Mapping、泛化特征 ID mapping。该部分整体场景主要是统一 ID,并将 ID 从差别较大、类型不同的不连续 ID 变成连续统一的 int 型自增 ID。</p><p><strong>计算任务运维</strong></p><p>任务运维主要是完成 DAG 的调度与计算资源管理。如果大家用过 Doris 的话，就会知道 Doris 会使用最快的速度完成每一个 SQL 的执行。因此在进行人群预估时就需要做好排队的速度，否则突然有一波运营动作或热点事件时，可能会出现预估出多个人群包的状况并把所有资源都占满，这样都会互相受到影响，所以就需要通过任务运维进行资源的优先级排队，逐一执行相关人群包的圈选工作。</p><p><strong>总结</strong></p><ul><li>特征建设可以做到能力隔离，以此来提升我们特征建设和上线效率。</li><li>ID Mapping 屏蔽了我们 ID Mapping 的困难成本。我们会分为完成原子特征建设、完成派生特征建设以及进行基础设施的建设这三部分。当基础设施建设同学完成屏蔽或在架构上隔离之后，特征建设的同学就不需要管 ID Mapping 方面的问题，只需要管专注于建设特征即可。</li><li>计算任务运维部分，对于业务开发同学并不需要知道底层到底发生了什么，为此我们要有一个同学完成对底层的封装后向上层提供一个接口，业务侧可以直接使用底层的功能的同时屏蔽了底层的复杂性。通过抽象与屏蔽，可以明显的提升最终上线与建设的效率，并能让其他某些工作从研发侧转移到运营侧。</li></ul><p><strong>举例：</strong> 我们当前有两种特征，第一种是原子特征。在形成原子特征的过程中，写一个 SQL 就可以形成一个特征。分析师与业务产品均可以参与特征的建设过程。第二种是派生特征。我们在运营后台上具备派生特征的交并差的能力，一些业务上的运营动作可以直接在管理后台进行操作并完成派生特征的建设。这样主要的工作量从研发侧逐渐转移到了产品侧与业务侧，明显的提升了各种能力和特征上线的效率。</p><h1>DMP 核心介绍</h1><p>DMP 核心部分有两方面：数据的写入/导入以及快查/快读。写入和导入是链路及存储的一部分，快查和快读我会在后续进行介绍。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="特征数据链路及存储">特征数据链路及存储<a class="hash-link" href="#特征数据链路及存储" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6615d3f89216463c9caf5d1be4cbef92~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.1 特征数据链路及存储</p><p>写入部分流程首先是离线链路：离线链路会从各个业务的 Hive 存储上跑相关的 SQL 并生成一个 Tag 表。我们会在 Hive 上落一份 Tag 表后完成离线 Mapping。这个离线 Mapping 过程会请求通过用户设备核心自动生成统一连续的用户 ID，同时在离线 Mapping 的过程会把 imei、idfa、oaid 等数据进行转换和唯一绑定，若过程结束后发现新用户，则生成新 ID，若是老用户则获取用户 ID。通过这个过程，生成 ID mapping 的表，再进行大小写等复杂流程就可以得到用户唯一 ID 与映射 ID 的 Mapping 表，这就是我们得到的第一个表。</p><p>接着我们会在 ID Mapping 后进行枚举采集：当前标签组是 125 个，由 120 个离线特征和 5 个实施特征组成。当我们完成这 125 个相关数据的开发之后，数据相应的原子特征就可以通过 Mapping 直接拿出来。之所以要进行枚举采集是因为用户在使用过程中需要标签的搜索功能，当用户搜索标签时，250 万人工录入的成本过高，因此我们在离线和实时处理的过程中会将枚举采集出来，并且通过 Bulk Load 的写到 ElasticSearch 中。在这个过程也会生成连续的自增 ID 去映射用户标签的倒排表，也就是 tag_map 表，这是我们得到的第二个表。另外还存在第三个表用户行为表，这张表是我们在实时数仓方面构建的，因此没有单独强调那一部分。</p><p><strong>基于上述三张表的部分，我们形成了三套存储：</strong></p><ul><li>第一套是在 ElasticSearch 上的搜索标签存储。</li><li>第二套是在 Doris 上，也是最核心的存储。</li><li>第三套是整体 ID Mapping 的存储。</li></ul><p>获取到这 3 个存储后，可以进行多种 Join 和查询，为后续的洞察及人群定向提供了基础。</p><p>接下来为大家公布几个量级：用户 X 标签量级，为 1,100 亿；ID Mapping 是一个宽表，量级是 8.5 亿；ElastichSearch，量级是 250 万。<strong>这三个量级也是我们为什么选择 ElasticSearch 和 Doris 的原因。</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="人群定向流程">人群定向流程<a class="hash-link" href="#人群定向流程" title="标题的直接链接">​</a></h2><p>上述的数据导入后形成 3 张表，这里是利用这 3 张表产生人群相关定向和人群包。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab04bc440c7e4785aef88ebb59cb205d~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.2 人群定向流程</p><p><strong>人群定向流程分为两种：</strong></p><ul><li>第一个是通过购物车筛选人群标签后进行人群预估，最后完成人群圈选回写到 Redis 的流程。</li><li>第二个是人群泛化，通过 AI 平台完成 AI 模型的整体训练及人群的推理，再回写到 Doris 中，通过置信度进行选择并打上标签。</li></ul><p><strong>简单介绍一下这两个流程的过程：</strong></p><p><strong>整体的标签搜索。</strong> 用户的前台在产出标签搜索的事件之后就会去完成标签的搜索：通过思考各种名字组合寻找想要的标签后，我们会把这个标签放在标签购物车中并立。这个过程就是不停的向人群购物车中加各种标签和组合条件后，查看人群数量的过程。</p><p>这个过程存在的原因是在日常运营使用中，我们会对每次推广或目标群体进行量级预估。如果这个事件原本只涉及 200 万到 300 万左右的人群，经过人群圈选预估出来是 5,000 万，那么肯定是我们圈选条件不够精准，这个情况下我们就需要逐渐添加各种精准的条件，并把圈选控制在合适范围的量级后再形成人群包，所以这个过程会不断进行循环并获取到合适的标签/特征的组合。在获取到合适的组合之后，我们需要确定这个标签的目标和人群是，这个过程就会生成人群包。生成人群包的过程会进行连表操作并关联原数据，同时也会关联 ID Mapping 的表。若出现导出到站外的情况，则会做 ID Mapping 的表并完成站外的 ID 转换。之后再把导出的人群包 ID 与人群 ID 写入 Reids 中，写入之后进行通知。</p><p>如果只需要提供人群包来发布推送和短信等的业务就不需要写到 Redis 之中，这样可以大量释放存储并写到离线存储上。比如说一方面是 HDFS，另外一方面是我们对接的对象存储就会写到这些存储之中。由这些存储直接传给推动系统后，信息系统就可以直接拿到人群包并批量的给相关人群发布相关 Push 或推送。</p><p><strong>人群泛化。</strong> 人群泛化流程最开始可能会有上传人群包的过程，也有可能没有。这个过程主要解决有些业务中，我们拥有某些历史活动的人群并需要进行人群泛化的问题。如果说它的人群包之前点击过我们的 Push，可以直接筛选，筛选完成之后关联所有的用户特征进行用户训练，模型训练完成后再对全站用户进行推理，推理出一批带有置信度的人群 ID 的结果并返回写到 Doris 之中。在这个过程中会同时发起另外一个流程，此流程会对用户侧的泛化的结果进行筛选，可以根据合适的置信度选择合适的数量。</p><p><strong>接下来为大家介绍几个常用流程：</strong> 在开发完成之后，最核心的流程就是加标签和购物车并完成圈选后，传统的人群进行泛化的流程。但是经过和运营侧沟通后，我们发现日常工作中，运营侧实际上会将我们这几个流程反复进行叠加使用，实际的使用有这么几种：拿到带有历史效果的人群并进行泛化，但是完成泛化之后效果他的用户特征也会被相应被扩大，之后再叠加本次运营特点的标签后完成圈选并进行使用。</p><p><strong>第二种是获取到历史效果后进行洞察和分析。</strong> 包括查看用户的画像后再重新根据标签关系圈选，之后又叠加了一次历史正向人群包后再去进行泛化。泛化之后再实现分发条件，最后再进行圈选，将该人群包给广告与相关的投放业务。运营侧会做很多基于原子能力以外更复杂的一些组合后再进行使用。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="人群定向性能优化">人群定向性能优化<a class="hash-link" href="#人群定向性能优化" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="背景"><strong>背景</strong><a class="hash-link" href="#背景" title="标题的直接链接">​</a></h3><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ad1343e041c14ca49a911ed84c853cb1~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.3 人群定向性能优化 背景与难点</p><p>当前 DMP 系统中有两大功能，第一大功能是人群定向，另外一大功能是人群洞察。基于这两大功能会有一个底层的功能是建设各种用户方面的画像特征。当我们完成拆解之后，我们就会发现人群定向的这部分功能是运营侧或业务侧的痛点。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="场景要求"><strong>场景要求</strong><a class="hash-link" href="#场景要求" title="标题的直接链接">​</a></h3><ul><li>人群预估，针对投放和营销场景，运营侧会有人数预期，那么会构建相应规模的购物车，持续在购物车中加入新的特征，需要立即看到新的特征加入之后会圈选出多少人，而不是每次加入新的特征后都需要很长时间的等待。</li><li>人群圈选，针对热点运营。运营侧在日常工作中会持续跟进发生的各种热点事件，当发生了某些热点事件后，要快速的圈选出人群包发布 Psuh 和 推荐。如果圈选过程需要好几分钟，就会错过热点事件。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="难点"><strong>难点</strong><a class="hash-link" href="#难点" title="标题的直接链接">​</a></h3><ul><li>第一个数据量极大，如上图标注。</li><li>第二个期望时间很短，人群预估与人群筛选分别能够在一秒钟内和一分钟内完成。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="性能优化1"><strong>性能优化（1）</strong><a class="hash-link" href="#性能优化1" title="标题的直接链接">​</a></h3><p>第一阶段优化我们通过了以下几点来解决这两个问题：</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a2a6d71f43514518a5973c2be34848ff~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.3 人群定向性能优化 第一阶段</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="倒排索引和按条件查询"><strong>倒排索引和按条件查询</strong><a class="hash-link" href="#倒排索引和按条件查询" title="标题的直接链接">​</a></h4><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/26dafc79390b43bb9875c400004d2a8d~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.4 人群定向性能优化 倒排索引及 ID Mapping</p><ul><li>首先，倒排索引方面，我们将查询条件由原先的 and or not 改成了 bitmap 函数的交并差；同时我们把连续数值打散成为了离散标签。举例：用户的年龄是大于 0，小于 100 的 int 型，如果按照数字顺序进行筛选，运营侧是不好把控的，圈选的过程中也会导致使用效果不理想。因此我们把按照顺序排列的年龄打上另外的标签，称为年龄段，比如 18-25，0-18 等。</li><li>接着，把原先的 and or not 的查询转换为了倒排索引的相关查询，原先建立的表就会变成按照 tag_group 、tag_value_id 、置信区间的标识、bitmap 的顺序排序。同时基于这部分我们也需要进行 ID Mapping，ID Mapping 在导入的过程中的核心就是要把用户 ID 变成连续自增的。</li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="查询逻辑变更"><strong>查询逻辑变更</strong><a class="hash-link" href="#查询逻辑变更" title="标题的直接链接">​</a></h4><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1a92a41f994a4892b2a367287702751a~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.5 人群定向性能优化 查询逻辑变更</p><p>原先的查询条件是 where 条件中的 and、or、not，现在经过复杂的手段，把原先的查询条件修改成 bitmap_and，bitmap_or，bitmap_not，我们通过业务代码，将外部运营通过可视化后台配置的 and、or、not 的逻辑全部改为函数式的逻辑，相当于把 where 条件放到了函数和聚合逻辑之中。</p><p><strong>但经过优化之后还会存在 2 个问题：</strong></p><p>第一个问题是单一的 bitmap 过大，第二个问题是 bitmap 的空间分散。这两个问题集中导致每次进行交并差聚合时网络 IO 特别高。</p><p>底层 Doris 中用的是 brpc。在数据交换的过程中，因为每一个单一的 bitmap 都很大，就会导致 brpc 传输拥堵，有时甚至会出现上百兆的 bitmap 进行交换的情况。上百兆的 bitmap 进行交并差计算时性能很低，基本上我们想要达它达到 1 分钟圈选人群，1 秒钟进行人群预估是不可能的。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="性能优化2"><strong>性能优化（2）</strong><a class="hash-link" href="#性能优化2" title="标题的直接链接">​</a></h3><p>基于仍存在的问题，我们进行了第二阶段的优化。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/547cc334199844a58de71b93e72a0eb6~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.6 人群定向性能优化 第二阶段</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="分而治之"><strong>分而治之</strong><a class="hash-link" href="#分而治之" title="标题的直接链接">​</a></h4><p>第二阶段的核心的思路是分治。当我们进行了第一波上线后，发现人群预估能力是分钟级别，圈选基本上要到 10 分钟开外了。分治的思路是将全站的用户全部打成连续自增 ID 后，按照某个程度进行分组。比如说 0-100 万是一组，100 万-200 万是一组...逐渐分为几个组别。全站用户的交并差，可以等价于分组之后的交并差结果之和。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8adfa109ef1a45c4a81e543e717542e3~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 3.7 人群定向性能优化 分治</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="数据预置"><strong>数据预置</strong><a class="hash-link" href="#数据预置" title="标题的直接链接">​</a></h4><p>当我们发现这个规律之后，通过分而治之可以做相关的数据预置。利用 Doris 中 Colocate group 特性，把每个分组内 100 万人全部放到某一台物理机上，避免网络的开销。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="算子优化"><strong>算子优化</strong><a class="hash-link" href="#算子优化" title="标题的直接链接">​</a></h4><p>全部放到某一个物理机上之后，就可以把聚合的算子由原先 bitmap_and_not 的 bitmap not 和 bitmap count 替换成一个函数来实现。此时基于 Doris 团队的新版本，增加了类似 bitmap_and_not_count 的组合函数后，性能相对于原先的嵌套函数有了比较明显的优化。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="解决方案"><strong>解决方案</strong><a class="hash-link" href="#解决方案" title="标题的直接链接">​</a></h3><p><strong>基于上述解决思路，我们设计了新的解决方案。</strong></p><p>新的解决方案以上 3 个思路进行拆分，包括查询逻辑的变更，预估变成子逻辑的求和、人群圈选变成子逻辑的合并。</p><ul><li>由于把原先几个 bitmap 的计算变成了多个小组 bitmap 计算，能进一步的提升多线程的并行度，使计算速度提升；同时也对代码进行了优化，将可复合的 bitmap_and_or_not 函数在提交时合并成同一个函数；在写入过程中把分组 ID 和相应的百万分组进行写入调整。</li><li>离线和实时之中都会写相应的 tag 表。在完成 tag 表的写入之后可以把每一个 tag 之中不同的 user tag 写到不同的物理机上：比如可以将 300 万拆开分别写在三台不同的物理机上，完成物理机方面的区隔。这里借助了 Colocate group 以及 Group key 进行设置。完成写入之后，计算过程从原先的整体计算变成独立按照每一个 Group 进行计算。由于整体的 bitmap 很大，每一个独立的 Group 又都在一台物理机上面进行计算，速度有非常明显的提升。</li><li>在每一个 Group 计算之后进行合并，合并之后，人群预估变成了不同物理机上面的数字简单加和，结果基本达到秒出。人群圈选也就变成了不同物理机上面的 bitmap，再 Shuffle 出去做最后的合并，这个过程量级很小，可以做到 1 分钟之内输出结果。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="优化结果"><strong>优化结果</strong><a class="hash-link" href="#优化结果" title="标题的直接链接">​</a></h3><p>下面两张截图分别是还没有进行合并之前以及合并之后的查询计划。</p><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7352a2279c574657b3f1a8ffd59ab6ec~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q">图 2.7 人群定向性能优化 数据预置</p><p><strong>优化前：</strong> 在查询的过程中，首先我们需要针对某一个 tag 做一个 bitmap_and 和 bitmap_not 或者 bitmap_or，在这之后另外几个 tag 也会做相同的聚合，在聚合完之后再做一次 Shuffle，最后进行 Join。同时另外的部分也会进行聚合，经过聚合之后再进行 Shuffle 和 Join。</p><p>这几次聚合过程中，每一个 tag 都有非常高的成本，都需要经过聚合—网络传输—再聚合—再网络传输的过程后再做 Join。</p><p><strong>优化后：</strong> 查询计划有了非常明显的改变。只需要通过一个函数在合并的过程中进行查询，合并完成之后就可以完成最终的结果合并。无论是 int 类型的相加还是 bitmap 的合并都只有最后一层，速度有显著提升。原先人均预估可需要分钟级别完成，优化后，只需要几百毫秒便可完成，即使是复杂到上千个条件也只需要一秒就可以完成。</p><p>人群圈选也和上述过程类似：在条件复杂的情况下，可以做到一分多钟到两分多种之间完成。如果只有几十到 一百个的条件的话，人群圈选都可以在一分钟左右完成。</p><p>整个过程主要对数据进行了拆分，由 Doris 的 Colocate 原理把拆分后的数据提前预置在某一台物理机上面，通过优化，可以满足大部分场景的运营要求。</p><h1>未来及展望</h1><h2 class="anchor anchorWithStickyNavbar_LWe7" id="业务向-1"><strong>业务向</strong><a class="hash-link" href="#业务向-1" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4d93797724b34c74841dc46640c25022~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 4.1 未来与展望 业务向</p><p>如红色框选所见，当前的系统流程是人群定向之后进行 Mapping，在用户洞察上是围绕人群进行建设的，同时与各个业务侧在 Mapping、洞察以及人群等环节进行对接。但是在这个流程中，如何通过运营达成目标、如何设计 AB 方案，两个部分是松耦合的。</p><p>未来我们希望 DMP 运营平台不光是松耦合的模式，而且能够在在业务上执行强耦合、强绑定的模式。这样的运营模式在使用过程中会更舒服，可以完全在 DMP 平台上完成了整体运营流程，并可以根据运营效果设计相关的 AB 实验，不断优化。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="技术向"><strong>技术向</strong><a class="hash-link" href="#技术向" title="标题的直接链接">​</a></h2><p><img loading="lazy" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7668b5f379bd47d3b27429b08d0f4898~tplv-k3u1fbpfcp-zoom-1.image" alt="img" class="img_ev3q"></p><p>图 4.2 未来与展望 技术向</p><p>技术建设过程中，最主要的就是圈选人群。运营侧甚至会选几百个条件进行人群圈选。而这些运营人员可能分属在不同业务，这会导致他们的基础条件写得很相似。对于这种相似的基础条件我们会人工建立相应的 bitmap 进行预合并，再去基于此特征圈选，由于预合并的缘故会明显提升我们后续的执行速度。</p><p><strong>第一个是查询效率。</strong> 对所有运营的人群圈选进行定期扫描及 SQL Parser。经过解析自动设计 SQL 的聚合条件进行预聚合，合成相应的 bitmap 的同时注册到相关的特征。在人群圈选时我们也会通过相同的 SQL Parser 自动将原先圈选的 SQL 改写，在改写之前可能会有好几十个特征，而他们又正好等于某一个派生特征的结果，此时就可以直接替换成派生特征。这个举动能进一步的提升我们查询的圈选速率。</p><p><strong>第二个是导入速度。</strong> 我们经过五天的时间，每天需要导入大概 2TB 的数据量，存储了 11TB 的数据，数据量比较大，我们希望在导入的过程中可以进一步的提速。当前我们了解到业界有做 Spark 直接撰写具体 OLAP 引擎文件，我们也在思考是否可以通过 Spark 直接撰写 Doris Tablet 文件并挂载到 FE 上面，让我们能够快速完成导入或写入。</p><h1>Q&amp;A 环节</h1><p>Q：知乎的标签体系有多少标签？记录量是多少？后台是一张还是多张的大宽表？在人群圈选的时候进行表链接，业务人员能否实时显示圈选出的人群特征和人群数量？</p><p>A：知乎的标签体系很大，包含了用户、内容、商业以及业务方面治理与安全等很多方面的标签，DMP 系统方面主要会与用户方面的标签进行对接。就单论通过认证且正在使用的标签组而言就有将近 700 多个，如果在加上业务方面在提未认证标签可以达到上千个。对于我们正在使用的用户方面的标签有 120 个标签组以及 5 个实时标签，总共 125 个标签。</p><p>记录量方面有 1100 亿的记录量。</p><p>后台不是一张宽表。在子标签完成生成后，会生成出独立的 tag1、tag2、tag3 的数据源表。经过我们将这些表写入 DMP 之后最终才会变成一个大宽表，在 DMP 中是问题中的一个大宽表，在业务中则是每个独立的标签表。多张大宽表在进行人群标签圈选时会进行连接，我们在经过数据处理后，会将数据写入到一张表中而不再是一张大宽表。</p><p>由于我们的优化，在这一张表中的存储的文件已经不会再按照 Tag ID 这种查询进度缓慢的方式进行分散。我们会按照存储的 Key，比如说 0~100 万的 ID 都会分在相同的地方进行存储。我们在计算的过程会在同一台物理机扫描出来，在经过聚合逻辑后就可以拿到结果。所以也就能够做到实时圈选相关数量的结果。</p><p>Q：人群圈选是基于经验进行标签组合圈选吗？投入后的效果如何分析？是独立的分析平台工具吗？如何知道投放人群包的转化率？转化是否回到打标签中利用另外的分析平台进行分析？</p><p>A：人群圈选可以分为两部分。第一部分是我们基于运营的经验进行圈选，这个部分中又分为已知人群圈选与未知人群圈选两个分支。</p><p>已知人群圈选，意味着运营已经对这个场景非常明确。能够熟知我们在运营的用户群体就是某个性别以及用户年龄段等，这时候我们就会基于历史经验进行圈选。对于完全未知的用户特征，我们会直接圈大盘。</p><p>这两种运营流程的区别就在于已知用户群体圈选的准确率会更高。基于已知的结果，我们几乎不再需要不用进行 AB 实验就可以完成本次投放。对于完全未知的用户特征而言，如果直接圈大盘的话，我们就一定需要进行小流量的 AB 实验发现点击 Push 的用户都满足某一个兴趣后，就可以基于这部分兴趣积累经验，之后再设计一个 AB 实验并调整人群特征至合适场景，直到效果逐渐的达到期望目标后，就会从未知的人群变为已知人群。</p><p>还存在另外一种经验。比如说广告主的经验，广告主可能在知乎中并没有历史投放经验，但是广告主知道购买过我的产品人群有哪些，比如说他们手机号的加密 MD5 或手机 idfa 的加密 MD5 等，这样就可以将其他站投放过的效果完成导入，形成基本的人群。通过人群泛化，和站内所有的特征进行 Join 后去训练模型，通过 AI 的能力自动寻找到我的历史购买人群有怎么样的显著特征，之后就可以完成这部分泛化的全选。基于泛化的全选后，还是会经过相同的链路并完成这部分的数次循环循，之后就可以知道我这个场景下应该投放给哪些用户。</p><p>转化率我们在单独的地方进行查看，这也是我后期想要集成在 DMP 平台内做到的功能。我们在单独的页面上可以看不同 Push 的转化率。DMP 平台上面只能通过效果回收进行查看。</p><p>Q：后台都是基于 Doris 吗？多少节点是一个集群呢？</p><p>A：后台主要的计算方面都是基于 Doris。在高吞吐方面我们也依赖于 Redis。TPP 方面我们用了 TiDB。当前 Doris 集群是 6 节点，64 核心 256g 的 BE；3 个 FE 是 6 节点，16 核心 32g 的集群配置。</p><p>Q：人群放大靠谱吗？所有的人群圈选占比有多大，用的是什么算法？</p><p>A：人群放大是比较靠谱的。从运营侧的反馈可以得知：如果只通过广告主或只通过基于列入历史运营效果拿到的数据基本上无法支持完成本次运营，但是如果把我们所有的特征都加入并进行训练的话，基本每次都会有比较明显的提升，在 CTR 方面，能够达到 80%-90%。置信度调整为了 80%。</p><p>人群圈选业务使用占比会比一般圈选要少一些。对于一般圈选而言，我们当前历史上已有的特征也带有置信度。我们基于这些已有特征基本就可以完成绝大部分的运营工作。而人群泛化主要是用来解决的是当我对这部分客户完全没有认知，同时又想将站内全部随机大盘用户导入，进行用户群体特征探测的情况。这个过程其实对运营侧而言工作量比较大，只有在这种特定情况下才会选用泛化，所以泛化的占比按照比例来讲是不多的。比如说每天有 300 个基于特征和标签的定向，而每天基于算法方面的泛化是 1~2 次。</p><p>用的是什么算法我还没有细看过。当前我们会通过数据来调用 AI 同学的相关的算法。我们当前提供的就是将用户的所有特征都准备好后灌入到 AI 的自动训练的模型之中。在完成训练之后，我们再调用这个模型并把所有特征都灌入进行推理。</p><p>Q：AB 如果要用 Reids 查标签该如何设计？要如何保持实时性呢？</p><p>A：对于问题中 A 表和 B 表要查标签，数据量会爆炸，这个情况是的确存在的。所以我建议做标签，最好所有的标签都在这一个表里。通过我们当前经历的探索得出的结论，我们对于该问题的解决方案就是每一台物理机可能会存多个 100 万，但是要确保每一个 100 万的分段都在同一台物理机上，它就可以变成这台物理机的 Scan 以及聚合之后进行直接运算，所以它就不存在双表的 Join 问题，可以直接在表内进行聚合。我们这边有好几个类似于 bitmap and or not 的标签的计算，但是在算子方面，算子已经是被合并在聚合算子里面并完成聚合，聚合后再做一个最终的数据合并，这样的话性能会好很多，而且也能避免 A 表和 B 表做 Join 的结果。</p><p>对于第二个问题，我们完成人群的 ID 聚合都会通过这个函数。当这个函数走完之后，它会生成当前投放特征下的人群列表，我才完成 Join。在这个时候，普通的 Join 就不会有非常爆炸的数量，也不会涉及到上千亿的快速的查询计算。</p><p>Q：可以解读一下关于 250 万个标签的相关内容吗？</p><p>A：大家可以在图 1.3 中看到,出现像 250 万个标签主要是因为一个性别在标签组内算作 1，而在标签方面则会拥有男、女、其他 3 个标签。在手机品牌中，一个标签组下我们当前也是收录了将近 20 多个手机品牌的标签。之后还有话题兴趣的标签组中相当多的话题兴趣的标签数量。比如说知乎站内其实有很多话题，某些用户可能对影视内容感兴趣，也可能对母婴内容感兴趣，同时也可能对教育或学生内容感兴趣，以上的话题兴趣有具有连续的共性点。连续标签方面我们会在后续的文章中继续为大家介绍。当前用户画像的内容方面，如果从标签进行分组，都是属于离散标签。连续标签更多的是用户行为或者是操作数值等。</p><p>Q：标签和特征的关系是什么？标签又是怎样建立的？</p><p>A：我们定义特征是要比较比标签大的，可以理解为我们当前的特征中 90% 是标签，剩下 10% 是用户行为的比例。</p><p><strong>相关链接：</strong></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[数仓体系效率全面提升！同程数科基于 Apache Doris 的数据仓库建设]]></title>
            <link>https://selectdb.com/blog/数仓体系效率全面提升！同程数科基于 Apache Doris 的数据仓库建设</link>
            <guid>/数仓体系效率全面提升！同程数科基于 Apache Doris 的数据仓库建设</guid>
            <pubDate>Fri, 08 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：同程数科成立于 2015 年，是同程集团旗下的旅游产业金融服务平台。2020 年，同程数科基于 Apache Doris 丰富的数据接入方式、优异的并行运算能力、极简运维等特性，引入 Apache Doris 进行数仓架构 2.0 的搭建。本文详细讲述了架构 1.0 到 2.0 的演进过程及 Doris 的应用实践，希望对大家有所帮助。]]></description>
            <content:encoded><![CDATA[<div class="pay-blog-content"><p class="pay-blog-summary">同程数科成立于 2015 年，是同程集团旗下的旅游产业金融服务平台。2020 年，同程数科基于 Apache Doris 丰富的数据接入方式、优异的并行运算能力、极简运维等特性，引入 Apache Doris 进行数仓架构2.0 的搭建。本文详细讲述了架构1.0 到 2.0 的演进过程及 Doris 的应用实践，希望对大家有所帮助。</p><div class="pay-blog-btn"><span>点击付费阅读全文</span><svg width="24" height="24" viewBox="0 0 24 24" class="Zi Zi--ArrowDown ContentItem-arrowIcon" fill="currentColor"><path fill-rule="evenodd" d="M12 13.248 8.22 9.223a.684.684 0 0 0-1.01 0 .796.796 0 0 0 0 1.075l4.15 4.42a.867.867 0 0 0 1.28 0l4.15-4.42a.796.796 0 0 0 0-1.075.684.684 0 0 0-1.01 0L12 13.248Z" clip-rule="evenodd"></path></svg></div></div>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[SelectDB Cloud 1.0 正式上线]]></title>
            <link>https://selectdb.com/blog/SelectDB Cloud 1.0 正式上线</link>
            <guid>/SelectDB Cloud 1.0 正式上线</guid>
            <pubDate>Tue, 05 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[飞轮科技是一家专注于开源数据仓库技术创新和云端数据仓库商业服务的公司。在开源方面，秉持开源开放的核心理念，大力投入研发力量，加强 Apache Doris 在数据分析技术上的持续创新力，使其成为世界领先的开源分析数据库。在云数仓方面，基于 Apache Doris ，构建运行于多云之上的新一代云原生实时数仓 SelectDB ，为客户提供极简运维和极致性价比的数仓服务。]]></description>
            <content:encoded><![CDATA[<p>飞轮科技是一家专注于开源数据仓库技术创新和云端数据仓库商业服务的公司。在开源方面，秉持开源开放的核心理念，大力投入研发力量，加强 Apache Doris 在数据分析技术上的持续创新力，使其成为世界领先的开源分析数据库。在云数仓方面，基于 Apache Doris ，构建运行于多云之上的新一代云原生实时数仓 SelectDB ，为客户提供极简运维和极致性价比的数仓服务。</p><p>今天，我们正式上线了 SelectDB Cloud 1.0，开放对外申请使用。SelectDB Cloud是一个运行于多云之上，提供多云使用一致性的云原生数仓，当前支持运行在阿里云上、预计7月份完成对AWS、华为云的支持，对其他云的支持也会陆续完成支持。SelectDB Cloud支持Apache Doris集群运行在客户VPC和SelectDB VPC，提供对集群的升级、变配、扩缩容等可视化管理，提供对集群状态的监控，提供易用的SQL查询用户界面。</p><p><img loading="lazy" alt="img" src="/assets/images/selectdb_cloud_1-542f93f3face3128592c69acb0c41b45.jpeg" width="2554" height="1356" class="img_ev3q"></p><p><img loading="lazy" alt="img" src="/assets/images/selectdb_cloud_2-d13840c12a9f9c660210944d9ab77642.jpeg" width="2554" height="1356" class="img_ev3q"></p><p>同时，我们也发布了技术支持服务 SelectDB Support。无论你是使用开源的Apache Doris，还是SelectDB Cloud的云数仓，都可以免费使用和付费订阅 SelectDB Support。我们创新推出了付费技术支持的按季度订阅服务，让你可以以最低的成本来体验我们的专业技术支持服务。</p><p><strong>相关链接：</strong></p><p>SelectDB 官方网站：</p><p><a href="https://selectdb.com" target="_blank" rel="noopener noreferrer">https://selectdb.com</a></p><p>SelectDB Cloud：</p><p><a href="https://cloud.selectdb.com" target="_blank" rel="noopener noreferrer">https://cloud.selectdb.com</a></p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[蜀海供应链基于 Apache Doris 的数据中台建设]]></title>
            <link>https://selectdb.com/blog/蜀海供应链基于 Apache Doris 的数据中台建设</link>
            <guid>/蜀海供应链基于 Apache Doris 的数据中台建设</guid>
            <pubDate>Sun, 03 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[蜀海供应链是集销售、研发、采购、生产、品保、仓储、运输、信息、金融为一体的餐饮供应链服务企业，为广大餐饮连锁企业及零售客户提供整体食材供应链解决方案服务。因其业务比较复杂，2020 年底完成了以 Apache Doris 为核心的架构升级，并在 2021 年开始建设以 Apache Doris 为核心的数据中台。]]></description>
            <content:encoded><![CDATA[<div class="pay-blog-content"><p class="pay-blog-summary">蜀海供应链是集销售、研发、采购、生产、品保、仓储、运输、信息、金融为一体的餐饮供应链服务企业，因其业务比较复杂，2020 年底完成了以 Apache Doris 为核心的架构升级，并在 2021 年开始建设以 Apache Doris 为核心的数据中台。本文将从数据接入，数据服务编排，数据安全，Doris 应用等方面进行介绍。</p><div class="pay-blog-btn"><span>点击付费阅读全文</span><svg width="24" height="24" viewBox="0 0 24 24" class="Zi Zi--ArrowDown ContentItem-arrowIcon" fill="currentColor"><path fill-rule="evenodd" d="M12 13.248 8.22 9.223a.684.684 0 0 0-1.01 0 .796.796 0 0 0 0 1.075l4.15 4.42a.867.867 0 0 0 1.28 0l4.15-4.42a.796.796 0 0 0 0-1.075.684.684 0 0 0-1.01 0L12 13.248Z" clip-rule="evenodd"></path></svg></div></div>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[10 亿数据秒级关联，货拉拉基于 Apache Doris 的 OLAP 体系演进]]></title>
            <link>https://selectdb.com/blog/10 亿数据秒级关联，货拉拉基于 Apache Doris 的 OLAP 体系演进</link>
            <guid>/10 亿数据秒级关联，货拉拉基于 Apache Doris 的 OLAP 体系演进</guid>
            <pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：本文是货拉拉大数据引擎负责人杨秋吉在 DataFunSummit 2022 多维分析架构峰会上的演讲分享，分享的主题是《货拉拉基于 Apache Doris 的 OLAP 体系演进及建设方法》，详细讲解了货拉拉从 OLAP1.0 到 3.0 的演进过程，其中不乏有值得借鉴的方法论以及深刻的技术思考，希望能对大家有所帮助。]]></description>
            <content:encoded><![CDATA[<div class="pay-blog-content"><p class="pay-blog-summary">本文是货拉拉大数据引擎负责人杨秋吉在 DataFunSummit 2022 多维分析架构峰会上的演讲分享，分享的主题是《货拉拉基于 Apache Doris 的 OLAP 体系演进及建设方法》，详细讲解了货拉拉从 OLAP1.0 到 3.0 的演进过程，其中不乏有值得借鉴的方法论以及深刻的技术思考，希望能对大家有所帮助。</p><div class="pay-blog-btn"><span>点击付费阅读全文</span><svg width="24" height="24" viewBox="0 0 24 24" class="Zi Zi--ArrowDown ContentItem-arrowIcon" fill="currentColor"><path fill-rule="evenodd" d="M12 13.248 8.22 9.223a.684.684 0 0 0-1.01 0 .796.796 0 0 0 0 1.075l4.15 4.42a.867.867 0 0 0 1.28 0l4.15-4.42a.796.796 0 0 0 0-1.075.684.684 0 0 0-1.01 0L12 13.248Z" clip-rule="evenodd"></path></svg></div></div>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[Apache Doris 整合 Apache Flink CDC + Iceberg 构建实时湖仓一体的联邦查询]]></title>
            <link>https://selectdb.com/blog/ApacheDoris 整合 FLINK CDC + Iceberg 构建实时湖仓一体的联邦从查询</link>
            <guid>/ApacheDoris 整合 FLINK CDC + Iceberg 构建实时湖仓一体的联邦从查询</guid>
            <pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[1.概览]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_LWe7" id="1概览">1.概览<a class="hash-link" href="#1概览" title="标题的直接链接">​</a></h2><p>这篇教程将展示如何使用 Flink CDC + Iceberg + Doris 构建实时湖仓一体的联邦查询分析，Doris 1.1 版本提供了 Iceberg 的支持，本文主要展示 Doris 和 Iceberg 怎么使用，同时本教程整个环境是都基于伪分布式环境搭建，大家按照步骤可以一步步完成。完整体验整个搭建操作的过程。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="11-软件环境">1.1 软件环境<a class="hash-link" href="#11-软件环境" title="标题的直接链接">​</a></h3><p>本教程的演示环境如下：</p><ol><li>Centos7</li><li>Apache doris 1.1</li><li>Hadoop 3.3.3</li><li>hive 3.1.3</li><li>Fink 1.14.4</li><li>flink-sql-connector-mysql-cdc-2.2.1</li><li>Apache Iceberg 0.13.2</li><li>JDK 1.8.0_311</li><li>MySQL 8.0.29</li></ol><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://archive.apache.org/dist/hadoop/core/hadoop-3.3.3/hadoop-3.3.3.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://archive.apache.org/dist/hive/hive-3.1.3/apache-hive-3.1.3-bin.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://dlcdn.apache.org/flink/flink-1.14.4/flink-1.14.4-bin-scala_2.12.tgz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://search.maven.org/remotecontent?filepath</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">org/apache/iceberg/iceberg-flink-runtime-1.14/0.13.2/iceberg-flink-runtime-1.14-0.13.2.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://repository.cloudera.com/artifactory/cloudera-repos/org/apache/flink/flink-shaded-hadoop-3-uber/3.1.1.7.2.9.0-173-9.0/flink-shaded-hadoop-3-uber-3.1.1.7.2.9.0-173-9.0.jar</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="12-系统架构">1.2 系统架构<a class="hash-link" href="#12-系统架构" title="标题的直接链接">​</a></h3><ol><li>首先我们从 Mysql 数据中使用 Flink 通过 Binlog 完成数据的实时采集</li><li>然后再 Flink 中创建 Iceberg 表，Iceberg 的元数据保存在 hive 里</li><li>最后我们在 Doris 中创建 Iceberg 外表</li><li>在通过 Doris 统一查询入口完成对 Iceberg 里的数据进行查询分析，供前端应用调用，这里 iceberg 外表的数据可以和 Doris 内部数据或者 Doris 其他外部数据源的数据进行关联查询分析</li></ol><p>Doris 湖仓一体的联邦查询：</p><ol><li>Doris 通过 ODBC 方式支持：MySQL，Postgresql，Oracle ，SQLServer</li><li>同时支持 Elasticsearch 外表</li><li>1.0 版本支持 Hive 外表</li><li>1.1 版本支持 Iceberg 外表</li><li>1.2 版本支持 Hudi 外表</li></ol><h2 class="anchor anchorWithStickyNavbar_LWe7" id="2环境安装部署">2.环境安装部署<a class="hash-link" href="#2环境安装部署" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="21-安装-hadoophive">2.1 安装 Hadoop、Hive<a class="hash-link" href="#21-安装-hadoophive" title="标题的直接链接">​</a></h3><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">tar zxvf hadoop-3.3.3.tar.gz</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">tar zxvf apache-hive-3.1.3-bin.tar.gz</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>配置系统环境变量</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">export HADOOP_HOME=/data/hadoop-3.3.3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export HADOOP_HDFS_HOME=$HADOOP_HOME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export HIVE_HOME=/data/hive-3.1.3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">export PATH=$PATH:$HADOOP_HOME/bin:$HIVE_HOME/bin:$HIVE_HOME/conf</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="22-配置-hdfs">2.2 配置 hdfs<a class="hash-link" href="#22-配置-hdfs" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="221-core-sitexml">2.2.1 core-site.xml<a class="hash-link" href="#221-core-sitexml" title="标题的直接链接">​</a></h4><p>vi etc/hadoop/core-site.xml</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">fs.defaultFS</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">hdfs://localhost:9000</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="222-hdfs-sitexml">2.2.2 hdfs-site.xml<a class="hash-link" href="#222-hdfs-sitexml" title="标题的直接链接">​</a></h4><p>vi etc/hadoop/hdfs-site.xml</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">dfs.replication</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">1</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">dfs.namenode.name.dir</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">/data/hdfs/namenode</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">dfs.datanode.data.dir</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">/data/hdfs/datanode</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="223-修改-hadoop-启动脚本">2.2.3 修改 Hadoop 启动脚本<a class="hash-link" href="#223-修改-hadoop-启动脚本" title="标题的直接链接">​</a></h4><p>sbin/start-dfs.sh</p><p>sbin/stop-dfs.sh</p><p>在文件开始加上下面的内容</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">HDFS_DATANODE_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">HADOOP_SECURE_DN_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">hdfs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">HDFS_NAMENODE_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">HDFS_SECONDARYNAMENODE_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">root</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>sbin/start-yarn.sh</p><p>sbin/stop-yarn.sh</p><p>在文件开始加上下面的内容</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">YARN_RESOURCEMANAGER_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">root</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">HADOOP_SECURE_DN_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">yarn</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token assign-left variable" style="color:#36acaa">YARN_NODEMANAGER_USER</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">root</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="23-配置-yarn">2.3 配置 yarn<a class="hash-link" href="#23-配置-yarn" title="标题的直接链接">​</a></h3><p>这里我改变了 Yarn 的一些端口，因为我是单机环境和 Doris 的一些端口冲突。你可以不启动 yarn</p><p>vi etc/hadoop/yarn-site.xml</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.resourcemanager.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jiafeng-test:50056</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.resourcemanager.scheduler.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jiafeng-test:50057</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.resourcemanager.resource-tracker.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jiafeng-test:50058</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.resourcemanager.admin.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jiafeng-test:50059</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.resourcemanager.webapp.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jiafeng-test:9090</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.nodemanager.localizer.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">0.0.0.0:50060</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">yarn.nodemanager.webapp.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">0.0.0.0:50062</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>vi etc/hadoop/mapred-site.xm</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">mapreduce.jobhistory.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">0.0.0.0:10020</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">mapreduce.jobhistory.webapp.address</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">0.0.0.0:19888</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">mapreduce.shuffle.port</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">50061</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="224-启动-hadoop">2.2.4 启动 hadoop<a class="hash-link" href="#224-启动-hadoop" title="标题的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">sbin/start-all.sh</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="24-配置-hive">2.4 配置 Hive<a class="hash-link" href="#24-配置-hive" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="241-创建-hdfs-目录">2.4.1 创建 hdfs 目录<a class="hash-link" href="#241-创建-hdfs-目录" title="标题的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">hdfs dfs -mkdir -p /user/hive/warehouse</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hdfs dfs -mkdir /tmp</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hdfs dfs -chmod g+w /user/hive/warehouse</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hdfs dfs -chmod g+w /tmp</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="242-配置-hive-sitexml">2.4.2 配置 hive-site.xml<a class="hash-link" href="#242-配置-hive-sitexml" title="标题的直接链接">​</a></h4><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token prolog" style="color:#999988;font-style:italic">&lt;?xml version="1.0"?&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token prolog" style="color:#999988;font-style:italic">&lt;?xml-stylesheet type="text/xsl" href="configuration.xsl"?&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">javax.jdo.option.ConnectionURL</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">javax.jdo.option.ConnectionDriverName</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">com.mysql.jdbc.Driver</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">javax.jdo.option.ConnectionUserName</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">root</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">javax.jdo.option.ConnectionPassword</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">MyNewPass4!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">hive.metastore.warehouse.dir</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">/user/hive/warehouse</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">description</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">location of default database for the warehouse</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">description</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">hive.metastore.uris</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">description</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Thrift URI for the remote metastore. Used by metastore client to connect to remote metastore.</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">description</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">javax.jdo.PersistenceManagerFactoryClass</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">org.datanucleus.api.jdo.JDOPersistenceManagerFactory</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">hive.metastore.schema.verification</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">false</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">datanucleus.schema.autoCreateAll</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">name</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">true</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">value</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">property</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="243-配置-hive-envsh">2.4.3 配置 hive-env.sh<a class="hash-link" href="#243-配置-hive-envsh" title="标题的直接链接">​</a></h4><p>加入一下内容</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">HADOOP_HOME=/data/hadoop-3.3.3</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="244-hive-元数据初始化">2.4.4 hive 元数据初始化<a class="hash-link" href="#244-hive-元数据初始化" title="标题的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">schematool -initSchema -dbType mysql</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="245-启动-hive-metaservice">2.4.5 启动 hive metaservice<a class="hash-link" href="#245-启动-hive-metaservice" title="标题的直接链接">​</a></h4><p>后台运行</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">nohup bin/hive --service metaservice 1&gt;/dev/null 2&gt;&amp;1 &amp;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>验证</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">lsof -i:9083</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">java    20700 root  567u  IPv6 54605348      0t0  TCP *:emc-pp-mgmtsvc (LISTEN)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="25-安装-mysql">2.5 安装 MySQL<a class="hash-link" href="#25-安装-mysql" title="标题的直接链接">​</a></h3><p>具体请参照这里：</p><p><a href="https://doris.apache.org/zh-CN/blogs/PracticalCases/flink-cdc-to-doris.html#_4-3-%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE-mysql" target="_blank" rel="noopener noreferrer">使用 Flink CDC 实现 MySQL 数据实时入 Apache Doris</a></p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="251-创建-mysql-数据库表并初始化数据">2.5.1 创建 MySQL 数据库表并初始化数据<a class="hash-link" href="#251-创建-mysql-数据库表并初始化数据" title="标题的直接链接">​</a></h4><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DATABASE</span><span class="token plain"> demo</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">USE</span><span class="token plain"> demo</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> userinfo </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  id </span><span class="token keyword" style="color:#00009f">int</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">AUTO_INCREMENT</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  name </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DEFAULT</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'flink'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  address </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  phone_number </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">512</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  email </span><span class="token keyword" style="color:#00009f">VARCHAR</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">255</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token keyword" style="color:#00009f">ENGINE</span><span class="token operator" style="color:#393A34">=</span><span class="token keyword" style="color:#00009f">InnoDB</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10001</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_110'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'Shanghai'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10002</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_111'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'xian'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10003</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_112'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'beijing'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10004</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_113'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'shenzheng'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10005</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_114'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'hangzhou'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10006</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_115'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'guizhou'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10007</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_116'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'chengdu'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10008</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_117'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'guangzhou'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> userinfo </span><span class="token keyword" style="color:#00009f">VALUES</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10009</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'user_118'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'xian'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token string" style="color:#e3116c">'13347420870'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="26-安装-flink">2.6 安装 Flink<a class="hash-link" href="#26-安装-flink" title="标题的直接链接">​</a></h3><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">tar zxvf flink-1.14.4-bin-scala_2.12.tgz</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后需要将相应的依赖拷贝到 Flink 安装目录下的 lib 目录下。</p><p>下面将几个 Hadoop 和 Flink 里没有的依赖下载地址放在下面</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://repo1.maven.org/maven2/com/ververica/flink-sql-connector-mysql-cdc/2.2.1/flink-sql-connector-mysql-cdc-2.2.1.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://repo1.maven.org/maven2/org/apache/thrift/libfb303/0.9.3/libfb303-0.9.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://search.maven.org/remotecontent?filepath</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">org/apache/iceberg/iceberg-flink-runtime-1.14/0.13.2/iceberg-flink-runtime-1.14-0.13.2.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">wget</span><span class="token plain"> https://repository.cloudera.com/artifactory/cloudera-repos/org/apache/flink/flink-shaded-hadoop-3-uber/3.1.1.7.2.9.0-173-9.0/flink-shaded-hadoop-3-uber-3.1.1.7.2.9.0-173-9.0.jar</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>其他的：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/common/lib/commons-configuration2-2.1.1.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/common/lib/commons-logging-1.1.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/tools/lib/hadoop-archive-logs-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/common/lib/hadoop-auth-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/common/lib/hadoop-annotations-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/common/hadoop-common-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">adoop-3.3.3/share/hadoop/hdfs/hadoop-hdfs-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hadoop-3.3.3/share/hadoop/client/hadoop-client-api-3.3.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hive-3.1.3/lib/hive-exec-3.1.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hive-3.1.3/lib/hive-metastore-3.1.3.jar</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">hive-3.1.3/lib/hive-hcatalog-core-3.1.3.jar</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="261-启动-flink">2.6.1 启动 Flink<a class="hash-link" href="#261-启动-flink" title="标题的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">bin/start-cluster.sh</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="262-进入-flink-sql-client">2.6.2 进入 Flink SQL Client<a class="hash-link" href="#262-进入-flink-sql-client" title="标题的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> bin/sql-client.sh embedded</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>开启 checkpoint，每隔 3 秒做一次 checkpoint</p><p>Checkpoint 默认是不开启的，我们需要开启 Checkpoint 来让 Iceberg 可以提交事务。 并且，mysql-cdc 在 binlog 读取阶段开始前，需要等待一个完整的 checkpoint 来避免 binlog 记录乱序的情况。</p><blockquote><p>注意：</p><p>这里是演示环境，checkpoint 的间隔设置比较短，线上使用，建议设置为 3-5 分钟一次 checkpoint。</p></blockquote><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Flink </span><span class="token keyword" style="color:#00009f">SQL</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">SET</span><span class="token plain"> execution</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">checkpointing</span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword" style="color:#00009f">interval</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">INFO</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">Session</span><span class="token plain"> property has been </span><span class="token keyword" style="color:#00009f">set</span><span class="token punctuation" style="color:#393A34">.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="263-创建-iceberg-catalog">2.6.3 创建 Iceberg Catalog<a class="hash-link" href="#263-创建-iceberg-catalog" title="标题的直接链接">​</a></h4><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> CATALOG hive_catalog </span><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'type'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'iceberg'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'catalog-type'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'hive'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'uri'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'thrift://localhost:9083'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'clients'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'5'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'property-version'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'1'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'warehouse'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'hdfs://localhost:8020/user/hive/warehouse'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>查看 catalog</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Flink </span><span class="token keyword" style="color:#00009f">SQL</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">show</span><span class="token plain"> catalogs</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">-----------------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">    catalog name </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">-----------------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> default_catalog </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">    hive_catalog </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">-----------------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">in</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">set</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="264-创建-mysql-cdc-表">2.6.4 创建 Mysql CDC 表<a class="hash-link" href="#264-创建-mysql-cdc-表" title="标题的直接链接">​</a></h4><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> user_source </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    database_name STRING METADATA VIRTUAL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    table_name STRING METADATA VIRTUAL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DECIMAL</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name STRING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    address STRING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    phone_number STRING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    email STRING</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">KEY</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">NOT</span><span class="token plain"> ENFORCED</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">WITH</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'connector'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'mysql-cdc'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'hostname'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'localhost'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'port'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'3306'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'username'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'root'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'password'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'MyNewPass4!'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'database-name'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'demo'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">'table-name'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'userinfo'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>查询 CDC 表:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> user_source</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="265-创建-iceberg-表">2.6.5 创建 Iceberg 表<a class="hash-link" href="#265-创建-iceberg-表" title="标题的直接链接">​</a></h4><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">---查看catalog</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">show</span><span class="token plain"> catalogs</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">---使用catalog</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> catalog hive_catalog</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--创建数据库</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">DATABASE</span><span class="token plain"> iceberg_hive</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">--使用数据库</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> iceberg_hive</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h5 class="anchor anchorWithStickyNavbar_LWe7" id="2651-创建表">2.6.5.1 创建表<a class="hash-link" href="#2651-创建表" title="标题的直接链接">​</a></h5><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE all_users_info (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    database_name STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    table_name    STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    `id`          DECIMAL(20, 0) NOT NULL,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    name          STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    address       STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    phone_number  STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    email         STRING,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    PRIMARY KEY (database_name, table_name, `id`) NOT ENFORCED</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  ) WITH (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    'catalog-type'='hive'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  );</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>从 CDC 表里插入数据到 Iceberg 表里</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">use</span><span class="token plain"> catalog default_catalog</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">insert</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">into</span><span class="token plain"> hive_catalog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">all_users_info </span><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> user_source</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后停掉任务，我们去查询 iceberg 表</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> hive_catalog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">all_users_info</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>我们也可以通过 Hive 建好 Iceberg 表，然后通过 Flink 将数据插入到表里</p><p>下载 Iceberg Hive 运行依赖</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"> wget https://repo1.maven.org/maven2/org/apache/iceberg/iceberg-hive-runtime/0.13.2/iceberg-hive-runtime-0.13.2.jar</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在 hive shell 下执行：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">SET engine.hive.enabled=true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SET iceberg.engine.hive.enabled=true;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">SET iceberg.mr.catalog=hive;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> add jar /path/to/iiceberg-hive-runtime-0.13.2.jar;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>创建表</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> EXTERNAL </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> iceberg_hive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">id</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"> string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">STORED </span><span class="token keyword" style="color:#00009f">BY</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">LOCATION </span><span class="token string" style="color:#e3116c">'hdfs://localhost:8020/user/hive/warehouse/iceber_db/iceberg_hive'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">TBLPROPERTIES </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token string" style="color:#e3116c">'iceberg.mr.catalog'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'hadoop'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token string" style="color:#e3116c">'iceberg.mr.catalog.hadoop.warehouse.location'</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">'hdfs://localhost:8020/user/hive/warehouse/iceber_db/iceberg_hive'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后再 Flink SQL Client 下执行下面语句将数据插入到 Iceberg 表里</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> hive_catalog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive </span><span class="token keyword" style="color:#00009f">values</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'c'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">INTO</span><span class="token plain"> hive_catalog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive </span><span class="token keyword" style="color:#00009f">values</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'zhangfeng'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>查询这个表</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> hive_catalog</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">iceberg_hive</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="3-doris-查询-iceberg">3. Doris 查询 Iceberg<a class="hash-link" href="#3-doris-查询-iceberg" title="标题的直接链接">​</a></h2><p>Apache Doris 提供了 Doris 直接访问 Iceberg 外部表的能力，外部表省去了繁琐的数据导入工作，并借助 Doris 本身的 OLAP 的能力来解决 Iceberg 表的数据分析问题：</p><ol><li>支持 Iceberg 数据源接入 Doris</li><li>支持 Doris 与 Iceberg 数据源中的表联合查询，进行更加复杂的分析操作</li></ol><h3 class="anchor anchorWithStickyNavbar_LWe7" id="31-安装-doris">3.1 安装 Doris<a class="hash-link" href="#31-安装-doris" title="标题的直接链接">​</a></h3><p>这里我们不在详细讲解 Doris 的安装，如果你不知道怎么安装 Doris 请参照官方文档：<a href="https://doris.apache.org/zh-CN/docs/get-starting/get-starting.html#%E7%8E%AF%E5%A2%83%E5%87%86%E5%A4%87" target="_blank" rel="noopener noreferrer">快速入门</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="32-创建-iceberg-外表">3.2 创建 Iceberg 外表<a class="hash-link" href="#32-创建-iceberg-外表" title="标题的直接链接">​</a></h3><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">CREATE TABLE `all_users_info`</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ENGINE = ICEBERG</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PROPERTIES (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"iceberg.database" = "iceberg_hive",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"iceberg.table" = "all_users_info",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"iceberg.hive.metastore.uris"  =  "thrift://localhost:9083",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">"iceberg.catalog.type"  =  "HIVE_CATALOG"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">);</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="参数说明">参数说明：<a class="hash-link" href="#参数说明" title="标题的直接链接">​</a></h4><ul><li>ENGINE 需要指定为 ICEBERG</li><li>PROPERTIES 属性：<ul><li><code>iceberg.hive.metastore.uris</code>：Hive Metastore 服务地址</li><li><code>iceberg.database</code>：挂载 Iceberg 对应的数据库名</li><li><code>iceberg.table</code>：挂载 Iceberg 对应的表名，挂载 Iceberg database 时无需指定。</li><li><code>iceberg.catalog.type</code>：Iceberg 中使用的 catalog 方式，默认为 <code>HIVE_CATALOG</code>，当前仅支持该方式，后续会支持更多的 Iceberg catalog 接入方式。</li></ul></li></ul><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">mysql</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">TABLE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token identifier">all_users_info</span><span class="token identifier punctuation" style="color:#393A34">`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">ENGINE</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ICEBERG</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> PROPERTIES </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"iceberg.database"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"iceberg_hive"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"iceberg.table"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"all_users_info"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"iceberg.hive.metastore.uris"</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain">  </span><span class="token string" style="color:#e3116c">"thrift://localhost:9083"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"iceberg.catalog.type"</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">=</span><span class="token plain">  </span><span class="token string" style="color:#e3116c">"HIVE_CATALOG"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Query OK</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token plain"> affected </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.23</span><span class="token plain"> sec</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">mysql</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">select</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> all_users_info</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">---------------+------------+-------+----------+-----------+--------------+-------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> database_name </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> table_name </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> id    </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> name     </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> address   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> phone_number </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> email </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">---------------+------------+-------+----------+-----------+--------------+-------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10004</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_113 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> shenzheng </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10005</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_114 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> hangzhou  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10002</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_111 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> xian      </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10003</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_112 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> beijing   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10001</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_110 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> Shanghai  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10008</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_117 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> guangzhou </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10009</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_118 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> xian      </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10006</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_115 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> guizhou   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> demo          </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> userinfo   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10007</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> user_116 </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> chengdu   </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">13347420870</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">NULL</span><span class="token plain">  </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">+</span><span class="token comment" style="color:#999988;font-style:italic">---------------+------------+-------+----------+-----------+--------------+-------+</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">9</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">rows</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">in</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">set</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.18</span><span class="token plain"> sec</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="33-同步挂在">3.3 同步挂在<a class="hash-link" href="#33-同步挂在" title="标题的直接链接">​</a></h3><p>当 Iceberg 表 Schema 发生变更时，可以通过 <code>REFRESH</code> 命令手动同步，该命令会将 Doris 中的 Iceberg 外表删除重建。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">-- 同步 Iceberg 表</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">REFRESH TABLE t_iceberg;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- 同步 Iceberg 数据库</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">REFRESH DATABASE iceberg_test_db;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="34-doris-和-iceberg-数据类型对应关系">3.4 Doris 和 Iceberg 数据类型对应关系<a class="hash-link" href="#34-doris-和-iceberg-数据类型对应关系" title="标题的直接链接">​</a></h3><p>支持的 Iceberg 列类型与 Doris 对应关系如下表：</p><table><thead><tr><th align="center">Iceberg</th><th align="center">Doris</th><th align="center">描述</th></tr></thead><tbody><tr><td align="center">BOOLEAN</td><td align="center">BOOLEAN</td><td align="center"></td></tr><tr><td align="center">INTEGER</td><td align="center">INT</td><td align="center"></td></tr><tr><td align="center">LONG</td><td align="center">BIGINT</td><td align="center"></td></tr><tr><td align="center">FLOAT</td><td align="center">FLOAT</td><td align="center"></td></tr><tr><td align="center">DOUBLE</td><td align="center">DOUBLE</td><td align="center"></td></tr><tr><td align="center">DATE</td><td align="center">DATE</td><td align="center"></td></tr><tr><td align="center">TIMESTAMP</td><td align="center">DATETIME</td><td align="center">Timestamp 转成 Datetime 会损失精度</td></tr><tr><td align="center">STRING</td><td align="center">STRING</td><td align="center"></td></tr><tr><td align="center">UUID</td><td align="center">VARCHAR</td><td align="center">使用 VARCHAR 来代替</td></tr><tr><td align="center">DECIMAL</td><td align="center">DECIMAL</td><td align="center"></td></tr><tr><td align="center">TIME</td><td align="center">-</td><td align="center">不支持</td></tr><tr><td align="center">FIXED</td><td align="center">-</td><td align="center">不支持</td></tr><tr><td align="center">BINARY</td><td align="center">-</td><td align="center">不支持</td></tr><tr><td align="center">STRUCT</td><td align="center">-</td><td align="center">不支持</td></tr><tr><td align="center">LIST</td><td align="center">-</td><td align="center">不支持</td></tr><tr><td align="center">MAP</td><td align="center">-</td><td align="center">不支持</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="35-注意事项">3.5 注意事项<a class="hash-link" href="#35-注意事项" title="标题的直接链接">​</a></h3><ul><li>Iceberg 表 Schema 变更<strong>不会自动同步</strong>，需要在 Doris 中通过 <code>REFRESH</code> 命令同步 Iceberg 外表或数据库。</li><li>当前默认支持的 Iceberg 版本为 0.12.0，0.13.x，未在其他版本进行测试。后续后支持更多版本。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="36-doris-fe-配置">3.6 Doris FE 配置<a class="hash-link" href="#36-doris-fe-配置" title="标题的直接链接">​</a></h3><p>下面几个配置属于 Iceberg 外表系统级别的配置，可以通过修改 <code>fe.conf</code> 来配置，也可以通过 <code>ADMIN SET CONFIG</code> 来配置。</p><ul><li><p><code>iceberg_table_creation_strict_mode</code></p><p>创建 Iceberg 表默认开启 strict mode。
strict mode 是指对 Iceberg 表的列类型进行严格过滤，如果有 Doris 目前不支持的数据类型，则创建外表失败。</p></li><li><p><code>iceberg_table_creation_interval_second</code></p><p>自动创建 Iceberg 表的后台任务执行间隔，默认为 10s。</p></li><li><p><code>max_iceberg_table_creation_record_size</code></p><p>Iceberg 表创建记录保留的最大值，默认为 2000. 仅针对创建 Iceberg 数据库记录。</p></li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="4-总结">4. 总结<a class="hash-link" href="#4-总结" title="标题的直接链接">​</a></h2><p>这里 Doris On Iceberg 我们只演示了 Iceberg 单表的查询，你还可以联合 Doris 的表，或者其他的 ODBC 外表，Hive 外表，ES 外表等进行联合查询分析，通过 Doris 对外提供统一的查询分析入口。</p><p>自此我们完整从搭建 Hadoop，hive、flink 、Mysql、Doris 及 Doris On Iceberg 的使用全部介绍完了，Doris 朝着数据仓库和数据融合的架构演进，支持湖仓一体的联邦查询，给我们的开发带来更多的便利，更高效的开发，省去了很多数据同步的繁琐工作，快快来体验吧。</p><p><strong>相关链接：</strong></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p>]]></content:encoded>
            <category>技术分享</category>
        </item>
        <item>
            <title><![CDATA[Lifewit 数据平台基于Apache Doris的建设实践]]></title>
            <link>https://selectdb.com/blog/Lifewit 数据平台基于Apache Doris的建设实践</link>
            <guid>/Lifewit 数据平台基于Apache Doris的建设实践</guid>
            <pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：近几年随着跨境电商行业的快速发展，Lifewit 业务达到近十倍的增长，原先的痛点已经严重影响到用户的数据使用体验。技术端需要随着业务的飞速发展不断升级迭代适应业务的增长。Lifewit 规划了从旧数据架构进化成目前基于 Apache Doris 构建的轻量级业财一体化数据平台，来系统化地解决旧架构存在的痛点，打通业务数据和财务数据，构建综合数据平台提供全业务链自助数据分析能力，支撑完善的数据报表体系和高效的数据分析。]]></description>
            <content:encoded><![CDATA[<div class="pay-blog-content"><p class="pay-blog-summary">近几年随着跨境电商行业的快速发展，Lifewit 业务达到近十倍的增长，原先的痛点已经严重影响到用户的数据使用体验。技术端需要随着业务的飞速发展不断升级迭代适应业务的增长。Lifewit 规划了从旧数据架构进化成目前基于 Apache Doris 构建的轻量级业财一体化数据平台，来系统化地解决旧架构存在的痛点，打通业务数据和财务数据，构建综合数据平台提供全业务链自助数据分析能力，支撑完善的数据报表体系和高效的数据分析。</p><div class="pay-blog-btn"><span>点击付费阅读全文</span><svg width="24" height="24" viewBox="0 0 24 24" class="Zi Zi--ArrowDown ContentItem-arrowIcon" fill="currentColor"><path fill-rule="evenodd" d="M12 13.248 8.22 9.223a.684.684 0 0 0-1.01 0 .796.796 0 0 0 0 1.075l4.15 4.42a.867.867 0 0 0 1.28 0l4.15-4.42a.796.796 0 0 0 0-1.075.684.684 0 0 0-1.01 0L12 13.248Z" clip-rule="evenodd"></path></svg></div></div>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[物易云通基于 Apache Doris 的实时数据仓库建设]]></title>
            <link>https://selectdb.com/blog/物易云通基于 Apache Doris 的实时数据仓库建设</link>
            <guid>/物易云通基于 Apache Doris 的实时数据仓库建设</guid>
            <pubDate>Fri, 01 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读： 物易云通目前已成为国内产融供应链运营服务平台的领军企业之一，平台年交易额超过 200 亿元 ， 随着公司业务的快速发展，对数据计算分析的时效要求也越来越高。经数据团队的调研对比，于 2021 年引入了 Apache Doris 作为实时数据仓库。实战过程中获得一些经验，在此分享给大家。]]></description>
            <content:encoded><![CDATA[<div class="pay-blog-content"><p class="pay-blog-summary">物易云通目前已成为国内产融供应链运营服务平台的领军企业之一，平台年交易额超过 200 亿元，随着公司业务的快速发展，对数据计算分析的时效要求也越来越高。经数据团队的调研对比，于 2021 年引入了 Apache Doris 作为实时数据仓库。实战过程中获得一些经验，在此分享给大家。</p><div class="pay-blog-btn"><span>点击付费阅读全文</span><svg width="24" height="24" viewBox="0 0 24 24" class="Zi Zi--ArrowDown ContentItem-arrowIcon" fill="currentColor"><path fill-rule="evenodd" d="M12 13.248 8.22 9.223a.684.684 0 0 0-1.01 0 .796.796 0 0 0 0 1.075l4.15 4.42a.867.867 0 0 0 1.28 0l4.15-4.42a.796.796 0 0 0 0-1.075.684.684 0 0 0-1.01 0L12 13.248Z" clip-rule="evenodd"></path></svg></div></div>]]></content:encoded>
            <category>用户案例</category>
        </item>
        <item>
            <title><![CDATA[Apache Doris 成功从 Apache 孵化器毕业，正式成为 Apache 顶级项目]]></title>
            <link>https://selectdb.com/blog/Apache Doris毕业官宣文章</link>
            <guid>/Apache Doris毕业官宣文章</guid>
            <pubDate>Thu, 16 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[全球最大的开源软件基金会 Apache 软件基金会（以下简称 Apache）于美国时间 2022 年 6 月 15 日 宣布，]]></description>
            <content:encoded><![CDATA[<p>全球最大的开源软件基金会 Apache 软件基金会（以下简称 Apache）于美国时间 2022 年 <strong>6 月 15 日</strong> 宣布，
<strong>Apache Doris 成功从 Apache 孵化器毕业，正式成为 Apache 顶级项目</strong>（Top-Level Project，TLP）。</p><blockquote><p>以下内容译自 <a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">Apache Doris 官网</a></p></blockquote><p>Apache Doris 是一个基于 MPP 的现代化、高性能、实时的分析型数据库，以极速易用的特点被人们所熟知，仅需亚秒级响应时间即可返回海量数据下的查询结果，不仅可以支持高并发的点查询场景，也能支持高吞吐的复杂分析场景。基于此，Apache Doris 在多维报表、用户画像、即席查询、实时大屏等诸多业务领域都能得到很好应用。</p><p>Apache Doris 最早是诞生于百度内部广告报表业务的 Palo 项目，2017 年正式对外开源，2018 年 7 月由百度捐赠给 Apache 基金会进行孵化，之后在 Apache 导师的指导下由孵化器项目管理委员会成员进行孵化和运营。</p><p>“我们很自豪 Doris 能够顺利从 Apache 孵化器毕业，这是一个重要的里程碑。在整个孵化阶段，依靠 Apache 理念的指导和孵化器导师的帮助，我们学会了如何以 Apache 的方式去发展我们的项目与社区，也在这一进程中获得了巨大的成长。” Apache Doris VP 陈明雨说道。</p><p><strong>目前 Apache Doris 社区已经聚集了来自不同行业近百家企业的 300 余位贡献者，并且每月活跃贡献者人数也接近 100 位。</strong>在孵化期间，Apache Doris 一共发布了 8 个重要版本，完成了包括存储引擎升级、向量化执行引擎等诸多重大功能，并正式发布了 1.0 版本。正是依靠这些来自开源贡献者的力量，才使得 Apache Doris 取得了今天的成绩。</p><p>与此同时，Apache Doris 如今在中国乃至全球范围内都拥有着广泛的用户群体，截止目前， <strong>Apache Doris 已经在全球超过 500 家企业的生产环境中得到应用，在中国市值或估值排行前 50 的互联网公司中，有超过 80% 的公司长期使用 Apache Doris</strong>，包括百度、美团、小米、京东、字节跳动、腾讯、快手、网易、微博、新浪、360 等知名公司。同时在一些传统行业如金融、能源、制造、电信等领域也有着丰富的应用。</p><p>“你可以基于 Apache Doris 快速构建一个简单易用并且性能强大的数据分析平台，非常易于上手，所需要付出的学习成本非常低。并且 Apache Doris 的分布式架构非常简洁，可以极大降低系统运维的工作量，这也是越来越多用户选择 Apache Doris 的关键因素。”</p><p><strong>作为一款成熟的分析型数据库项目，Apache Doris 有以下优势：</strong></p><ul><li><strong>性能优异：</strong>自带高效的列式存储引擎，减少数据扫描量的同时还实现了超高的数据压缩比。同时 Doris 还提供了丰富的索引结构来加速数据读取与过滤，利用分区分桶裁剪功能，Doris 可以支持在线服务业务的超高并发，单节点最高可支持上千 QPS。更进一步，Apache Doris 结合了向量化执行引擎来充分发挥现代化 CPU 并行计算能力，辅以智能物化视图技术实现预聚合加速，并可以通过查询优化器同时进行基于规划和基于代价的查询优化。通过上述多种方式，实现了极致的查询性能。</li><li><strong>简单易用：</strong>支持标准 ANSI SQL 语法，包括单表聚合、排序、过滤和多表 Join、子查询等，还支持窗口函数、Grouping Set 等复杂 SQL 语法，同时用户可以通过 UDF 和 UDAF 等自定义函数来拓展系统功能。除此以外，Apache Doris 还实现了 MySQL 协议兼容，用户可以通过各类客户端工具来访问 Doris，并支持与 BI 工具的无缝对接。</li><li><strong>架构精简：</strong>系统只有两个 Frontend（FE）和 Backend（BE）两个模块，其中 FE 节点负责用户请求的接入、查询计划的解析、元数据存储及集群管理等工作，BE 节点负责数据存储和查询计划的执行，自身就是一个完备的分布式数据库管理系统，用户无需安装任何第三方管控组件即可运行起 Apache Doris 集群，并且部署和升级过程都非常简易。同时，任一模块都可以支持横向拓展，集群最高可以拓展到数百个节点，支持存储超过 10PB 的超大规模数据。</li><li><strong>稳定可靠：</strong>支持数据多副本存储，集群具备自愈功能，自身的分布式管理框架可以自动管理数据副本的分布、修复和均衡，副本损坏时系统可以自动感知并进行修复。节点扩容时，仅需一条 SQL 命令即可完成，数据分片会自动在节点间均衡，无需人工干预或操作。无论是扩容、缩容、单节点故障还是在升级过程中，系统都无需停止运行，可正常提供稳定可靠的在线服务。</li><li><strong>生态丰富：</strong>提供丰富的数据同步方式，支持快速加载来自本地、Hadoop、Flink、Spark、Kafka、SeaTunnel 等系统中的数据，也可以直接访问 MySQL、PostgreSQL、Oracle、S3、Hive、Iceberg、Elasticsearch 等系统中的数据而无需数据复制。同时存储在 Doris 中的数据也可以被 Spark、Flink 读取，并且可以输出给上游数据应用进行展示分析。</li></ul><p><strong>“‍ 毕业不是最终目标，它是新征程的起点。”陈明雨说到。</strong></p><p>“在过去，我们发起 Doris 的目标是为更多人提供体验更佳的数据分析工具、解决他们数据分析的难题。成为 Apache 顶级项目一方面是对 Apache Doris 社区过去所有贡献者一直以来辛勤工作的肯定，另一方面也意味着我们在 Apache Way 的指引下建立了一个强大的、繁荣的、可持续发展的开源社区。未来我们将会继续以 Apache 方式运作社区，相信会吸引到更多优秀的开源贡献者参与社区中来，社区也会在所有贡献者的帮助下得到进一步成长。”</p><p>“Apache Doris 后续将开展更多富有挑战且有意义的工作，包括新的查询优化器、对湖仓一体化的支持，以及面向云上基础设施的架构演进等等。欢迎更多的开源技术爱好者加入 Apache Doris 的社区，携手共成长。”</p><p><strong>“我们再次由衷地感谢所有参与建设 Apache Doris 社区的贡献者们，以及所有使用 Apache Doris 并不断提出改进建议的用户们。同时也感谢一路走来，不断鼓励、支持和帮助过我们的孵化器导师、IPMC 成员以及各个开源项目社区的朋友们。”</strong></p><p><strong>相关链接：</strong></p><p>Apache Doris 官方网站：</p><p><a href="http://doris.apache.org" target="_blank" rel="noopener noreferrer">http://doris.apache.org</a></p><p>Apache Doris Github：</p><p><a href="https://github.com/apache/doris" target="_blank" rel="noopener noreferrer">https://github.com/apache/doris</a></p><p>Apache Doris 开发者邮件组：</p><p><a href="mailto:dev@doris.apache.org" target="_blank" rel="noopener noreferrer">dev@doris.apache.org</a></p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
        <item>
            <title><![CDATA[官宣！「飞轮科技」完成超3亿元天使轮和天使+轮融资]]></title>
            <link>https://selectdb.com/blog/官宣！「飞轮科技」完成超3亿元天使轮和天使+轮融资</link>
            <guid>/官宣！「飞轮科技」完成超3亿元天使轮和天使+轮融资</guid>
            <pubDate>Fri, 29 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[导读：致力解决实时数据分析的行业痛点，飞轮科技将持续与广大开发者共建 Apache Doris 开源社区，并基于此研发云原生实时数仓 SelectDB。]]></description>
            <content:encoded><![CDATA[<blockquote><p><strong>导读：</strong>致力解决实时数据分析的行业痛点，飞轮科技将持续与广大开发者共建 Apache Doris 开源社区，并基于此研发云原生实时数仓 SelectDB。</p></blockquote><p>36 氪获悉，云原生实时数仓厂商 北京飞轮数据科技有限公司（以下简称飞轮科技）于近期完成超 3 亿元的天使轮和天使 + 轮融资，投资方为 IDG 资本、红杉中国等 VC 。</p><p>据了解，飞轮科技成立于 2021 年 12 月，由原百度智能云大数据与视频云总经理连林江创办，团队核心成员来自百度、腾讯、奇安信、阿里、亚马逊、字节跳动、蚂蚁金服、快手等国内外头部互联网和云计算公司。</p><p>谈及创业原因，连林江向 36 氪介绍，公司核心团队过去一直深耕于大数据分析和云计算领域，投身在互联网数字经济的趋势中。在整个社会数字化转型加速的背景下，各行各业对大数据实时处理和应用的需求正快速增长，对分析时效性的要求也越来越高。正由此，飞轮科技团队希望贡献自己的技术经验和工程力量解决行业痛点、构建云原生时代具行业普适能力的实时数据仓库。</p><p>在技术方面，飞轮科技表示，其团队对于大数据开源软件和云计算有着深度理解。在此基础上，公司将投身于由国人自主研发并开源的实时 OLAP 数据库 Apache Doris，将其作为实时数据分析技术的建设起点。对此连林江强调，当今中国还需要更多的开源技术贡献者和生态建设者，飞轮科技的首要定位是一家开源技术公司，所以团队也将秉持开源开放的核心理念，持续投入研发力量共建开源社区，持续服务好社区用户，与广大社区开发者一起持续提升 Apache Doris 作为分析型数据库开源项目在国际范围内的影响力。</p><p>在产品方面，飞轮科技还将研发基于 Doris 内核的云原生发行版 SelectDB。SelectDB 是运行在云上的实时数据仓库，为用户和客户提供开箱即用的能力。据介绍，其主要的特色功能体现在：充分发挥弹性云计算、弹性云存储的优势，实现高性价比；提供可视化、易用的管控平台和用户交互开发平台。另外在场景适配度上，公司表示其产品具备通用性特点，对各个业务场景均具备适用性，可以帮助客户在一套架构中实现对流、批数据以及结构化、半结构化数据的处理和分析，解决繁重架构带来的难以落地及运维难题。</p><p>在市场推广方面，公司表示，将推进开放共赢的生态合作战略，致力于和云厂商及 ISV/SI 开展合作，让客户在各家云上都能享受到一致的产品体验。可以看到，当前不少云厂商都会提供自身的数据仓库产品，但在飞轮科技看来，云厂商和独立数据仓库厂商之间的合作在国内外都很常见，双方可以在不同场景下相辅相成、共同满足客户的多样化和差异化需求。尤其客户在多云场景的选择上，独立数据仓库厂商的中立地位难以被取代。当前，飞轮科技已经同部分业内头部云厂商开展合作，同时与更多公有云、行业云厂商的合作也将逐步落地。在行业客户上，飞轮科技表示除泛互联网以外，汽车、金融、电信、零售快消、交通物流、能源、医疗、生物制药、工业制造等行业也不断有客户和飞轮科技建立联系，对产品表现出浓厚兴趣。</p><p>团队方面，飞轮科技创始人兼 CEO 连林江曾负责百度智能云大数据、云存储、视频云以及企业应用平台等业务，从零到一开拓了大量客户和商业化收入。联合创始人兼 CTO 衣国垒曾担任百度 Doris 团队技术负责人、腾讯云架构平台部 Clickhouse 负责人。联合创始人兼技术 VP 肖康曾担任奇安信 ToB 大数据平台高级总监、360 大数据/云平台技术总监，成功交付过大量客户。联合创始人兼产品 VP 杨勇强曾担任百度智能云存储部总架构师，主导构建了云存储技术产品体系，他也是 Linux 内核社区贡献者。整体来看，创始团队在技术、产品、开源运营、商业化等方面均拥有十余年的深厚经验。</p>]]></content:encoded>
            <category>重大新闻</category>
        </item>
    </channel>
</rss>