<?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>Apache SeaTunnel Blog</title>
        <link>https://seatunnel.apache.org/zh-CN/blog</link>
        <description>Apache SeaTunnel Blog</description>
        <lastBuildDate>Tue, 13 Jan 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <item>
            <title><![CDATA[深度拆解 Apache SeaTunnel 元数据缓存：支撑数万同步任务并行运行]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/seatunnel-metadata-cache</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/seatunnel-metadata-cache</guid>
            <pubDate>Tue, 13 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[在大规模数据集成场景中，吞吐瓶颈往往不在数据通道本身，而在“元数据路径”上：启动时的 Connector/Jar 加载、运行中的状态管理与恢复、以及初始化阶段对外部系统（如数据库、Hive Metastore）的 Schema/分区查询。任务量一旦上到千级、万级，这些“看似轻量”的动作会被放大成集群级别的压力。]]></description>
            <content:encoded><![CDATA[<p>在大规模数据集成场景中，吞吐瓶颈往往不在数据通道本身，而在“元数据路径”上：启动时的 Connector/Jar 加载、运行中的状态管理与恢复、以及初始化阶段对外部系统（如数据库、Hive Metastore）的 Schema/分区查询。任务量一旦上到千级、万级，这些“看似轻量”的动作会被放大成集群级别的压力。</p><p>Apache SeaTunnel Engine（Zeta）把一部分高频、可复用且昂贵的元数据下沉到引擎侧进行缓存，并配合分布式存储与自动清理策略，让海量同步任务可以更稳定地并行运行。</p><p><img loading="lazy" alt="SeaTunnel 分布式架构下的元数据流转" src="/zh-CN/assets/images/metadata-flow-ea94de898f982087ea7895137f65fc6c.jpg" width="2130" height="1560" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="为什么元数据会成为瓶颈">为什么“元数据”会成为瓶颈<a href="#为什么元数据会成为瓶颈" class="hash-link" aria-label="为什么“元数据”会成为瓶颈的直接链接" title="为什么“元数据”会成为瓶颈的直接链接">​</a></h2><p>以“万级小作业”并行启动为例，常见的元数据瓶颈主要来自三类：</p><ul><li><strong>类加载与依赖隔离</strong>：每个作业独立创建 ClassLoader，会反复加载同一批 Connector 依赖，快速抬升 JVM Metaspace 压力。</li><li><strong>状态与恢复信息</strong>：Checkpoint、任务状态、历史作业信息等若缺少分层存储与自动清理，会带来内存与 IO 的双重负担。</li><li><strong>外部目录/Schema 查询</strong>：作业初始化阶段对源端数据库或 Metastore 的频繁请求，容易造成连接拥塞与元数据服务抖动。</li></ul><p>下面从三条主线拆解 SeaTunnel 的“元数据缓存”思路与可落地的配置建议。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="一classloader-缓存降低-metaspace-压力">一：ClassLoader 缓存，降低 Metaspace 压力<a href="#一classloader-缓存降低-metaspace-压力" class="hash-link" aria-label="一：ClassLoader 缓存，降低 Metaspace 压力的直接链接" title="一：ClassLoader 缓存，降低 Metaspace 压力的直接链接">​</a></h2><p>当大量作业复用相同的 Source/Sink Connector 时，持续创建和销毁类加载器会带来明显的 Metaspace 抖动，甚至触发溢出。SeaTunnel Engine 提供 <code>classloader-cache-mode</code>，用于复用作业之间的 ClassLoader，减少重复加载和频繁回收的开销。</p><p>在 <code>seatunnel.yaml</code> 中开启（该配置默认开启，若你曾手动关闭可重新启用）：</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">engine</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">classloader-cache-mode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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><ul><li>作业规模大、启动频繁，且 Connector 类型相对有限（复用率高）。</li><li>JVM Metaspace 频繁增长，或出现与类加载相关的内存告警。</li></ul><p><strong>注意点</strong>：</p><ul><li>如果集群长期运行且 Connector 类型非常分散，缓存会增加常驻的类元数据占用；建议结合监控观察 Metaspace 曲线，再决定是否开启或调整作业结构。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="二分布式状态与持久化保证可恢复与可运营">二：分布式状态与持久化，保证可恢复与可运营<a href="#二分布式状态与持久化保证可恢复与可运营" class="hash-link" aria-label="二：分布式状态与持久化，保证可恢复与可运营的直接链接" title="二：分布式状态与持久化，保证可恢复与可运营的直接链接">​</a></h2><p>SeaTunnel Engine 的容错语义基于 Chandy–Lamport Checkpoint 思想。为了兼顾性能与可靠性，它在引擎内部使用 Hazelcast 的分布式数据结构（如 IMap）承载一部分运行态信息，并通过外部存储（共享/分布式存储）完成故障恢复所需的数据落盘。</p><p>你通常需要关心三组配置：</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-checkpoint-触发参数">1) Checkpoint 触发参数<a href="#1-checkpoint-触发参数" class="hash-link" aria-label="1) Checkpoint 触发参数的直接链接" title="1) Checkpoint 触发参数的直接链接">​</a></h3><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">engine</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">checkpoint</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">interval</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">300000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">timeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">10000</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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>env</code> 中配置了 <code>checkpoint.interval</code>/<code>checkpoint.timeout</code>，会优先以作业配置为准。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-imap-备份与持久化建议用于生产集群">2) IMap 备份与持久化（建议用于生产集群）<a href="#2-imap-备份与持久化建议用于生产集群" class="hash-link" aria-label="2) IMap 备份与持久化（建议用于生产集群）的直接链接" title="2) IMap 备份与持久化（建议用于生产集群）的直接链接">​</a></h3><p>当集群节点数大于 1 时，建议至少配置 <code>backup-count</code>，以降低单点故障导致的内存态信息丢失风险；对于需要“全停全启后自动恢复”的场景，可进一步配置 IMap 外部持久化。</p><p>相关细节可参考文档：</p><ul><li><code>/docs/seatunnel-engine/deployment</code></li><li><code>/docs/seatunnel-engine/checkpoint-storage</code></li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-历史作业信息的自动清理">3) 历史作业信息的自动清理<a href="#3-历史作业信息的自动清理" class="hash-link" aria-label="3) 历史作业信息的自动清理的直接链接" title="3) 历史作业信息的自动清理的直接链接">​</a></h3><p>SeaTunnel 将已完成作业的状态、计数器、错误日志等信息存放在 IMap 中。作业越多，累积越快。建议按需配置 <code>history-job-expire-minutes</code>，让过期信息自动淘汰，避免内存长期膨胀（默认 1440 分钟，即 1 天）。</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key atrule">seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">engine</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">history-job-expire-minutes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1440</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="三catalogschema-元数据缓存减少源端压力">三：Catalog/Schema 元数据缓存，减少源端压力<a href="#三catalogschema-元数据缓存减少源端压力" class="hash-link" aria-label="三：Catalog/Schema 元数据缓存，减少源端压力的直接链接" title="三：Catalog/Schema 元数据缓存，减少源端压力的直接链接">​</a></h2><p>大量作业并行启动时，对外部系统的元数据请求（表结构、分区信息、约束信息等）很容易成为“隐形风暴”。SeaTunnel 在 Connector/Catalog 侧引入缓存与复用思路，尽量把高频查询前置到引擎侧，减少重复的网络往返与服务端解析开销。</p><ul><li><strong>JDBC 场景</strong>：初始化阶段会读取表结构、字段类型、主键等信息，用于校验与分片规划。建议在高并发启动时避免每个作业对同一张表重复拉取全量元数据（可通过作业编排层做批次启动/预热）。</li><li><strong>Hive 场景</strong>：Hive Metastore 往往是共享服务且相对敏感，建议尽量复用 Catalog 实例与已加载的 Database/Table/Partition 信息，并在大规模分区表同步中关注 Metastore 的 QPS 与响应时间。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="与-flink--spark-的差异面向海量小作业的轻量化">与 Flink / Spark 的差异：面向“海量小作业”的轻量化<a href="#与-flink--spark-的差异面向海量小作业的轻量化" class="hash-link" aria-label="与 Flink / Spark 的差异：面向“海量小作业”的轻量化的直接链接" title="与 Flink / Spark 的差异：面向“海量小作业”的轻量化的直接链接">​</a></h2><p>Flink 的设计重心是长生命周期的流作业与复杂算子状态；Spark 更偏向批处理与作业级 Context 管理。在“万级独立小任务并发”这个目标下，SeaTunnel Engine 的策略更强调把可复用的启动与运行元数据沉到引擎层：减少重复加载、减少重复查询、并对历史信息进行可控的生命周期管理，从而提升并发启动与稳定性。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="生产落地建议">生产落地建议<a href="#生产落地建议" class="hash-link" aria-label="生产落地建议的直接链接" title="生产落地建议的直接链接">​</a></h2><ul><li><strong>启用合理备份</strong>：生产集群建议 <code>backup-count &gt;= 1</code>，并评估是否需要 IMap 外部持久化以支持全停全启自动恢复。</li><li><strong>收敛 Connector 类型</strong>：尽量在同一集群里控制 Connector 组合的离散程度，让 <code>classloader-cache-mode</code> 的收益最大化。</li><li><strong>关注“元数据指标”</strong>：除了 JVM 指标，建议关注 Checkpoint 延迟/失败率、Hazelcast 内存使用、IMap 大小与增长速率、历史作业累积速度等。</li><li><strong>配置过期策略</strong>：根据排障与审计需求设置 <code>history-job-expire-minutes</code>，避免“为了可观测性而撑爆内存”。</li></ul><p><img loading="lazy" alt="元数据缓存相关指标示意" src="/zh-CN/assets/images/dashboard-d5922cf6e6f51be8fcd1b518be64d8f8.jpg" width="3456" height="6134" class="img_ev3q"></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[亚马逊云科技架构师的分享：基于SeaTunnel迁移数据到Amazon Aurora DSQL]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/migrate-data-to-amazon-aurora-dsql</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/migrate-data-to-amazon-aurora-dsql</guid>
            <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Amazon Aurora DSQL是亚马逊云科技于2024年12月推出的分布式SQL数据库，专为构建扩展性无限、高可用且免基础设施管理的应用程序设计，具有可用性高、无服务器模式架构、兼容性强、容错能力和安全级别高等特点。]]></description>
            <content:encoded><![CDATA[<p>Amazon Aurora DSQL是亚马逊云科技于2024年12月推出的分布式SQL数据库，专为构建扩展性无限、高可用且免基础设施管理的应用程序设计，具有可用性高、无服务器模式架构、兼容性强、容错能力和安全级别高等特点。 </p><p>由于Aurora DSQL的认证机制与IAM集成， 访问Aurora DSQL数据库需要通过IAM的身份来生成token 进行访问，而token 默认只有15分钟有效期，因此目前一些主流的数据同步工具暂不支持将其他数据库的数据迁移到Aurora DSQL。</p><p>基于这种情况，本文作者<strong>基于数据同步工具Apache SeaTunnel开发了一个专门针对Aurora DSQL的sink Connector</strong>，以满足从其他数据库迁移数据到Aurora DSQL需求。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel-介绍">SeaTunnel 介绍<a href="#seatunnel-介绍" class="hash-link" aria-label="SeaTunnel 介绍的直接链接" title="SeaTunnel 介绍的直接链接">​</a></h2><p>SeaTunnel是一个非常易用、多模态、超高性能的分布式数据集成平台，专注于数据集成和数据同步，主要旨在解决数据集成领域的常见问题。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel-相关特性">SeaTunnel 相关特性<a href="#seatunnel-相关特性" class="hash-link" aria-label="SeaTunnel 相关特性的直接链接" title="SeaTunnel 相关特性的直接链接">​</a></h3><ul><li>丰富且可扩展的Connector： 目前，SeaTunnel 支持超过 190 个Connector且数量还在增加，像主流数据库MySQL 、Oracle、SQLServer、PostgreSQL等都已经提供了Connector支持。插件式设计让用户可以轻松开发自己的Connector并将其集成到SeaTunnel项目中。</li><li>批流集成：基于SeaTunnel Connector API开发的Connector完美兼容离线同步、实时同步、全量同步、增量同步等场景。 它们大大降低了管理数据集成任务的难度。</li><li>分布式快照：支持分布式快照算法，保证数据一致性。</li><li>多引擎支持：SeaTunnel默认使用SeaTunnel引擎（Zeta）进行数据同步。 SeaTunnel还支持使用Flink或Spark作为Connector的执行引擎，以适应企业现有的技术组件。 SeaTunnel 支持 Spark 和 Flink 的多个版本。</li><li>JDBC复用、数据库日志多表解析：SeaTunnel支持多表或全库同步，解决了过度JDBC连接的问题； 支持多表或全库日志读取解析，解决了CDC多表同步场景下需要处理日志重复读取解析的问题。</li><li>高吞吐量、低延迟：SeaTunnel支持并行读写，提供稳定可靠、高吞吐量、低延迟的数据同步能力。</li><li>完善的实时监控：SeaTunnel支持数据同步过程中每一步的详细监控信息，让用户轻松了解同步任务读写的数据数量、数据大小、QPS等信息。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel-工作流程">SeaTunnel 工作流程<a href="#seatunnel-工作流程" class="hash-link" aria-label="SeaTunnel 工作流程的直接链接" title="SeaTunnel 工作流程的直接链接">​</a></h3><p><img loading="lazy" src="https://openwrite-whaleops.oss-cn-zhangjiakou.aliyuncs.com/2025/11/25/17640559894620.jpg" class="img_ev3q">
图一 Seatunnel工作流图</p><p>SeaTunnel的工作流程如上图所示，用户配置作业信息并选择提交作业的执行引擎。Source Connector负责并行读取源端数据并将数据发送到下游Transform或直接发送到Sink，Sink将数据写入目的地。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="从源码构建seatunnel">从源码构建SeaTunnel<a href="#从源码构建seatunnel" class="hash-link" aria-label="从源码构建SeaTunnel的直接链接" title="从源码构建SeaTunnel的直接链接">​</a></h2><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">git clone https://github.com/apache/seatunnel.git</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd seatunnel</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">sh ./mvnw clean install -DskipTests -Dskip.spotless=true</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cp seatunnel-dist/target/apache-seatunnel-${version}-bin.tar.gz /The-Path-You-Want-To-Copy</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd /The-Path-You-Want-To-Copy</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tar -xzvf "apache-seatunnel-${version}-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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>从源码构建成功后，所有的Connector插件和一些必要的依赖（例如：mysql驱动）都包含在二进制包中。您可以直接使用Connector插件，而无需单独安装它们。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用seatunnel同步mysql数据到aurora-dsql-配置示例">使用Seatunnel同步MySQL数据到Aurora DSQL 配置示例<a href="#使用seatunnel同步mysql数据到aurora-dsql-配置示例" class="hash-link" aria-label="使用Seatunnel同步MySQL数据到Aurora DSQL 配置示例的直接链接" title="使用Seatunnel同步MySQL数据到Aurora DSQL 配置示例的直接链接">​</a></h2><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">env {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  parallelism = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  job.mode = "STREAMING"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  checkpoint.interval = 6000</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  checkpoint.timeout = 1200000</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">source {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  MySQL-CDC {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    username = "user name"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    password = "password"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    table-names = ["db.table1"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    url = "jdbc:mysql://dbhost:3306/db?useSSL=false&amp;allowPublicKeyRetrieval=true&amp;serverTimezone=UTC&amp;connectTimeout=120000&amp;socketTimeout=120000&amp;autoReconnect=true&amp;failOverReadOnly=false&amp;maxReconnects=10"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    table-names-config = [</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table = "db.table1"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        primaryKeys = ["id"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    ]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">transform {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">sink {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    Jdbc {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        url="jdbc:postgresql://&lt;dsql_endpoint&gt;:5432/postgres"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        dialect="dsql"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        driver = "org.postgresql.Driver"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        username = "admin"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        access_key_id = "ACCESSKEYIDEXAMPLE"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        secret_access_key = "SECRETACCESSKEYEXAMPLE"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        region = "us-east-1"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database = "postgres"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        generate_sink_sql = true</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        primary_keys = ["id"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        max_retries="3"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        batch_size =1000</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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 href="#运行数据同步任务" class="hash-link" aria-label="运行数据同步任务的直接链接" title="运行数据同步任务的直接链接">​</a></h2><p>将上面的配置保存为mysql-to-dsql.conf 文件(请注意需要将示例中的值替换为真实的参数)，存放在apache-seatunnel-${version} 的config 目录下，执行以下命令:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">cd "apache-seatunnel-${version}"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">./bin/seatunnel.sh --config ./config/mysql-to-dsql.conf -m local</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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://openwrite-whaleops.oss-cn-zhangjiakou.aliyuncs.com/2025/11/25/17640560876306.jpg" class="img_ev3q">
图二 数据同步日志信息</p><p>命令执行成功后，您可以通过新产生的日志观察任务执行情况，如果出现错误，也可以根据异常信息进行定位，比如数据库连接超时、表不存在情况。而正常情况下，数据会成功写入目标 Aurora DSQL，如上图所示。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>Aurora DSQL是一款高度安全、易扩展、无服务器基础设施的分布式数据库，它的认证方式与IAM身份结合，因此目前缺少合适的工具可以将数据同步到Aurora DSQL中，尤其是在实时数据同步方面。SeaTunnel 是一款非常优秀数据集成和数据同步工具，目前支持多种数据源的数据同步，并且基于SeaTunnel 也可以非常灵活地实现自定义的数据同步需求，比如全量同步/增量实时同步。基于这种灵活性，本文作者开发了一种专门针对于Aurora DSQL 的Sink Connector， 以满足对于Aurora DSQL 数据同步需求。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="参考文档">参考文档<a href="#参考文档" class="hash-link" aria-label="参考文档的直接链接" title="参考文档的直接链接">​</a></h2><ul><li><p>SeaTunnel 部署：<a href="https://seatunnel.apache.org/zh-CN/docs/start-v2/locally/deployment" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/zh-CN/docs/start-v2/locally/deployment</a></p></li><li><p>开发新的SeaTunnel Connector：</p></li><li><p><a href="https://github.com/apache/seatunnel/blob/dev/seatunnel-connectors-v2/README.zh.md" target="_blank" rel="noopener noreferrer">https://github.com/apache/seatunnel/blob/dev/seatunnel-connectors-v2/README.zh.md</a></p></li><li><p>在Aurora DSQL 中生成身份验证令牌：<a href="https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_authentication-token.html" target="_blank" rel="noopener noreferrer">https://docs.aws.amazon.com/aurora-dsql/latest/userguide/SECTION_authentication-token.html</a></p></li></ul><p>*前述特定亚马逊云科技生成式人工智能相关的服务目前在亚马逊云科技海外区域可用。亚马逊云科技中国区域相关云服务由西云数据和光环新网运营，具体信息以中国区域官网为准。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="本篇作者">本篇作者<a href="#本篇作者" class="hash-link" aria-label="本篇作者的直接链接" title="本篇作者的直接链接">​</a></h2><p><img loading="lazy" src="https://openwrite-whaleops.oss-cn-zhangjiakou.aliyuncs.com/2025/11/25/17640561555693.jpg" class="img_ev3q"></p><p><strong>谭志强，亚马逊云科技迁移解决方案架构师</strong>，主要负责企业级客户的上云或跨云迁移工作，具有十几年 IT 专业服务经验，历任程序设计师、项目经理、技术顾问、解决方案架构师。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apache SeaTunnel 与计算引擎的解耦之道，重构API我们做了些什么]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/2022/05/31/engine</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/2022/05/31/engine</guid>
            <pubDate>Tue, 31 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Apache SeaTunnel (Incubating) 与 Apache Inlong (Incubating) 的5月联合Meetup中，第二位分享的嘉宾是来自白鲸开源的高级工程师李宗文。在使用Apache SeaTunnel (Incubating) 的过程中，他发现了 Apache SeaTunnel (Incubating) 存在的四大问题：Connector实现次数多、参数不统一、难以支持多个版本的引擎以及引擎升级难的问题。为了解决以上的难题，李宗文将目标放在将Apache SeaTunnel (Incubating)与计算引擎进行解耦，重构其中Source与Sink API，实现改良了开发体验。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="/zh-CN/assets/images/0-1b0b7ccac22c107239f10a37321c7719.jpg" width="1920" height="1316" class="img_ev3q"></p><p>Apache SeaTunnel (Incubating) 与 Apache Inlong (Incubating) 的5月联合Meetup中，第二位分享的嘉宾是来自白鲸开源的高级工程师李宗文。在使用Apache SeaTunnel (Incubating) 的过程中，他发现了 Apache SeaTunnel (Incubating) 存在的四大问题：Connector实现次数多、参数不统一、难以支持多个版本的引擎以及引擎升级难的问题。为了解决以上的难题，李宗文将目标放在将Apache SeaTunnel (Incubating)与计算引擎进行解耦，重构其中Source与Sink API，实现改良了开发体验。</p><p>本次演讲主要包含四个部分：</p><ol><li>Apache SeaTunnel (Incubating)<strong>重构的背景和动机</strong></li><li>Apache SeaTunnel (Incubating)<strong>重构的目标</strong></li><li>Apache SeaTunnel (Incubating)<strong>重构整体的设计</strong></li><li>Apache SeaTunnel (Incubating) <strong>Source API的设计</strong></li><li>Apache SeaTunnel (Incubating) <strong>Sink API的设计</strong></li></ol><p><img loading="lazy" src="/zh-CN/assets/images/1-cbc169e3bc121f1008b807103abe9fd8.jpg" width="1440" height="810" class="img_ev3q"></p><p><strong>李宗文</strong></p><p>白鲸开源 高级工程师</p><p>Apache SeaTunnel(Incubating)</p><p>&amp; Flink Contributor, Flink CDC &amp; Debezium Contributor</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="01-重构的背景与动机"><strong>01</strong> 重构的背景与动机<a href="#01-重构的背景与动机" class="hash-link" aria-label="01-重构的背景与动机的直接链接" title="01-重构的背景与动机的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="01-apache-seatunnelincubating与引擎耦合">01 Apache SeaTunnel(Incubating)与引擎耦合<a href="#01-apache-seatunnelincubating与引擎耦合" class="hash-link" aria-label="01 Apache SeaTunnel(Incubating)与引擎耦合的直接链接" title="01 Apache SeaTunnel(Incubating)与引擎耦合的直接链接">​</a></h3><p>用过Apache SeaTunnel (Incubating) 的小伙伴或者开发者应该知道，目前Apache SeaTunnel (Incubating) 与引擎完全耦合，完全基于Spark、Flink开发，其中的配置文件参数都基于Flink、Spark引擎。从贡献者和用户的角度出发，我们能发现一些问题。</p><p><strong>从贡献者的角度</strong>：反复实现Connector，没有收获感；潜在贡献者由于引擎版本不一致无法贡献社区；</p><p><strong>从用户的角度</strong>：目前很多公司采用Lambda架构，离线作业使用Spark，实时作业使用Flink， 使用中就会发现SeaTunnel 的Connector可能Spark有，但是Flink没有，以及两个引擎对于同一存储引擎的Connector的参数也不统一，有较高的使用成本，脱离了SeaTunnel简单易用的初衷；还有用户提问说目前支不支持Flink的1.14版本，按照目前SeaTunnel的架构，想要支持Flink的1.14就必须抛弃之前的版本，因此这也会对之前版本的用户造成很大的问题。</p><p>因此，我们不管是做引擎升级或者支持更多的版本的用户都很困难。</p><p>另外Spark和Flink都采用了Chandy-lamport算法实现的Checkpoint容错机制，也在内部进行了DataSet与DataStream的统一，以此为前提我们认为解耦是可行的。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="02-apache-seatunnelincubating与引擎解耦"><strong>02</strong> Apache SeaTunnel(Incubating)与引擎解耦<a href="#02-apache-seatunnelincubating与引擎解耦" class="hash-link" aria-label="02-apache-seatunnelincubating与引擎解耦的直接链接" title="02-apache-seatunnelincubating与引擎解耦的直接链接">​</a></h2><p>因此为了解决以上提出的问题，我们有了以下的目标：</p><ol><li><strong>Connector只实现一次</strong>：针对参数不统一、Connector多次实现的问题，我们希望实现一个统一的Source 与Sink API;</li><li><strong>支持多个版本的Spark与Flink引擎</strong>：在Source与Sink API上再加入翻译层去支持多个版本与Spark和Flink引擎，解耦后这个代价会小很多。</li><li><strong>明确Source的分片并行逻辑和Sink的提交逻辑</strong>：我们必须提供一个良好的API去支持Connector开发；</li><li><strong>支持实时场景下的数据库整库同步</strong>：这个是目前很多用户提到<strong>需要CDC</strong>支持衍生的需求。我之前参与过Flink CDC社区，当时有许多用户提出在CDC的场景中，如果直接使用Flink CDC的话会导致每一个表都持有一个链接，当遇到需要整库同步需求时，千张表就有千个链接，该情况无论是对于数据库还是DBA都是不能接受的，如果要解决这个问题，最简单的方式就是引入Canal、Debezium等组件，使用其拉取增量数据到Kafka等MQ做中间存储，再使用Flink SQL进行同步，这实际已经违背了Flink CDC最早减少链路的想法，但是Flink CDC的定位只是一个Connector，无法做全链路的需求，所以该proposal在Flink CDC社区中没有被提出，我们借着本次重构，将proposa提交到了SeaTunnel社区中。</li><li><strong>支持元信息的自动发现与存储</strong>：这一部分用户应该有所体验，如Kafka这类存储引擎，没有记录数据结构的功能，但我们在读取数据时又必须是结构化的，导致每次读取一个topic之前，用户都必须定义topic的结构化数据类型，我们希望做到用户只需要完成一次配置，减少重复的操作。</li></ol><p>可能也有同学有疑惑为什么我们不直接使用Apache Beam，Beam的Source分为BOUNDED与UNBOUNDED，也就是需要实现两遍，并且有些Source与Sink的特性也不支持，具体所需的特性在后面会提到；</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="03-apache-seatunnelincubating重构整体的设计"><strong>03</strong> Apache SeaTunnel(Incubating)重构整体的设计<a href="#03-apache-seatunnelincubating重构整体的设计" class="hash-link" aria-label="03-apache-seatunnelincubating重构整体的设计的直接链接" title="03-apache-seatunnelincubating重构整体的设计的直接链接">​</a></h2><p><img loading="lazy" src="/zh-CN/assets/images/1-cbc169e3bc121f1008b807103abe9fd8.jpg" width="1440" height="810" class="img_ev3q"></p><p>Apache SeaTunnel(Incubating) API总体结构的设计如上图；</p><p><strong>Source &amp; Sink API</strong>：数据集成的核心API之一，明确Source的分片并行逻辑和Sink的提交逻辑，用于实现Connector；</p><p><strong>Engine API</strong>：</p><p>Translation: 翻译层，用于将SeaTunnel的Souce与Sink API翻译成引擎内部可以运行的Connector；</p><p><strong>Execution</strong>：执行逻辑，用于定义Source、Transform、Sink等操作在引擎内部的执行逻辑；</p><p><strong>Table API</strong>：</p><p><strong>Table SPI</strong>：主要用于以SPI的方式暴露Source与Sink接口，并明确Connector的必填与可选参数等；</p><p><strong>DataType</strong>：SeaTunnel的数据结构，用于隔离引擎，声明Table Schema等；</p><p><strong>Catalog</strong>：用于获取Table Scheme、Options等；</p><p><strong>Catalog Storage</strong>: 用于存储用户定义Kafka等非结构化引擎的Table Scheme等；</p><p><img loading="lazy" src="/zh-CN/assets/images/2-507471aef3b2e7dc6ee4bc03188bc784.jpg" width="1440" height="810" class="img_ev3q"></p><p><strong>从上图是我们现在设想的执行流程</strong>：</p><ol><li>从配置文件或UI等方式获取任务参数；</li><li>通过参数从Catalog中解析得到Table Schema、Option等信息；</li><li>以SPI方式拉起SeaTunnel的Connector，并注入Table信息等；</li><li>将SeaTunnel的Connector翻译为引擎内部的Connector；</li><li>执行引擎的作业逻辑，图中的多表分发目前只存在CDC整库同步场景下，其他Connector都是单表，不需要分发逻辑；</li></ol><p>从以上可以看出，最难的部分是<strong>如何将Apache SeaTunnel(Incubating) 的Source和Sink翻译成引擎内部的Source和Sink。</strong></p><p>当下许多用户不仅把Apache SeaTunnel (Incubating) 当做一个数据集成方向的工具，也当做数仓方向的工具，会使用很多Spark和Flink的SQL，我们目前希望能够保留这样的SQL能力，让用户实现无缝升级。</p><p><img loading="lazy" src="/zh-CN/assets/images/3-68113e215aa8e91a5d2469ccb37a3c22.jpg" width="1440" height="810" class="img_ev3q"></p><p>根据我们的调研，如上图，是对Source与Sink的理想执行逻辑，由于SeaTunnel以WaterDrop孵化，所以图上的术语偏向Spark；</p><p>理想情况下，在Driver上可以运行Source和Sink的协调器，然后Worker上运行Source的Reader和Sink的Writer。在Source协调器方面，我们希望它能支持几个能力。</p><p><strong>一、是数据的分片逻辑</strong>，可以将分片动态添加到Reader中。</p><p><strong>二、是可以支持Reader的协调</strong>。SourceReader用于读取数据，然后将数据发送到引擎中流转，最终流转到Source Writer中进行数据写入，同时Writer可以支持二阶段事务提交，并由Sink的协调器支持Iceberg等Connector的聚合提交需求；</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="04-source-api"><strong>04</strong> Source API<a href="#04-source-api" class="hash-link" aria-label="04-source-api的直接链接" title="04-source-api的直接链接">​</a></h2><p>通过我们的调研，发现Source所需要的以下特性：</p><ol><li><strong>统一离线和实时API</strong>：Source只实现一次，同时支持离线和实时；</li><li><strong>能够支持并行读取</strong>：比如Kafka每一个分区都生成一个的读取器，并行的执行；</li><li><strong>支持动态添加分片</strong>：比如Kafka定于一个topic正则，由于业务量的需求，需要新增一个topic，该Source API可以支持我们动态添加到作业中。</li><li><strong>支持协调读取器的工作</strong>：这个目前只发现在CDC这种Connector需要支持。CDC目前都是基于Netfilx的DBlog并行算法去支持，该情况在全量同步和增量同步两个阶段的切换时需要协调读取器的工作。</li><li><strong>支持单个读取器处理多张表</strong>：即由前面提到的支持实时场景下的数据库整库同步需求；</li></ol><p><img loading="lazy" src="/zh-CN/assets/images/4-ac907e3f8e305b9f6585d2171013a973.jpg" width="1440" height="810" class="img_ev3q"></p><p>对应以上需求，我们做出了基础的API，如上图，目前代码以提交到Apache SeaTunnel(Incubating)的社区中api-draft分支，感兴趣的可以查看代码详细了解。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="如何适配spark和flink引擎"><strong>如何适配Spark和Flink引擎</strong><a href="#如何适配spark和flink引擎" class="hash-link" aria-label="如何适配spark和flink引擎的直接链接" title="如何适配spark和flink引擎的直接链接">​</a></h3><p>Flink与Spark都在后面统一了DataSet与DataStream API，即能够支持前两个特性，那么对于剩下的3个特性：</p><ul><li>如何支持动态添加分片？</li><li>如何支持协调读取器？</li><li>如何支持单个读取器处理多张表？</li></ul><p>带着问题，进入目前的设计。</p><p><img loading="lazy" src="/zh-CN/assets/images/5-f743cf882b36bb3c383c66dec4bad95f.jpg" width="1440" height="810" class="img_ev3q"></p><p>我们发现除了<strong>CDC</strong>之外，其他Connector是不需要协调器的，针对不需要协调器的，我们会有一个支持并行的Source，并进行引擎翻译。</p><p>如上图中左边是一个<strong>分片的enumerator</strong>，可以列举source需要哪些分片，有哪些分片，实时进行分片的枚举，随后将每个分片分发到真正的数据读取模块SourceReader中。<strong>对于离线与实时作业的区分使用Boundedness标记</strong>，Connector可以在分片中标记是否有停止的Offset，如Kafka可以支持实时，同时也可以支持离线。ParallelSource可以在引擎设置任意并行度，以支持并行读取。</p><p><img loading="lazy" src="/zh-CN/assets/images/6-ef5d53449db28f4466c14827335d07a6.jpg" width="1440" height="810" class="img_ev3q"></p><p>在需要协调器的场景，如上图，需要在Reader和Enumerator之间进行Event传输，<strong> Enumerator</strong>通过Reader发送的Event进行协调工作。<strong>Coordinated Source</strong>需要在引擎层面保证单并行度，以保证数据的一致性；当然这也不能良好的使用引擎的内存管理机制，但是取舍是必要的；</p><p><strong>对于最后一个问题，我们如何支持单个读取器处理多张表。这会涉及到Table API层</strong>，通过Catalog读取到了所有需要的表后，有些表可能属于一个作业，可以通过一个链接去读取，有些可能需要分开，这个依赖于Source是怎么实现的。基于这是一个特殊需求，我们想要减少普通开发者的难度，在Table API这一层，我们会提供一个SupportMultipleTable接口，用于声明Source支持多表的读取。Source在实现时，要根据多张表实现对应的反序列化器。针对衍生的多表数据如何分离，Flink将采用Side Output机制，Spark预想使用Filter或Partition机制。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="05-sink-api"><strong>05</strong> Sink API<a href="#05-sink-api" class="hash-link" aria-label="05-sink-api的直接链接" title="05-sink-api的直接链接">​</a></h2><p>目前Sink所需的特性并不是很多，<strong>经过调研目前发现有三个需求</strong>：</p><ol><li>幂等写入，这个不需要写代码，主要看存储引擎是否能支持。</li><li>分布式事务，主流是二阶段提交，如Kafka都是可以支持分布式事务的。</li><li>聚合提交，对于Iceberg、hoodie等存储引擎而言，我们不希望有小文件问题，于是期望将这些文件聚合成一个文件，再进行提交。</li></ol><p>基于以上三个需求，我们有对应的<strong>三个API</strong>，分别是<strong>SinkWriter、SinkCommitter、SinkAggregated Committer</strong>。SinkWriter是作为基础写入，可能是幂等写入，也可能不是。SinkCommitter支持二阶段提交。SinkAggregatedCommitter支持聚合提交。</p><p><img loading="lazy" src="/zh-CN/assets/images/7-e9830d63f81f7139a7cd1d4d9b9f5e43.jpg" width="1440" height="810" class="img_ev3q"></p><p>理想状态下，<strong>AggregatedCommitter</strong>单并行的在Driver中运行，Writer与Committer运行在Worker中，可能有多个并行度，每个并行度都有自己的预提交工作，然后把自己提交的信息发送给Aggregated Committer再进行聚合。</p><p><strong>目前Spark和Flink的高版本都支持在Driver</strong>(Job Manager)运行AggregatedCommitter，worker(Job Manager)运行writer和Committer。</p><p><img loading="lazy" src="/zh-CN/assets/images/8-aa0537c76d15543b46623445ff00e490.jpg" width="1440" height="810" class="img_ev3q"></p><p>但是对于<strong>Flink低版本</strong>，无法支持AggregatedCommitter在JM中运行，我们也进行翻译适配的设计。Writer与Committer会作为前置的算子，使用Flink的ProcessFunction进行包裹，支持并发的预提交与写入工作，基于Flink的Checkpoint机制实现二阶段提交，这也是目前Flink的很多Connector的2PC实现方式。这个ProcessFunction会将预提交信息发送到下游的Aggregated Committer中，Aggregated Committer可以采用SinkFunction或Process Function等算子包裹，当然，<strong>我们需要保证AggregatedCommitter只会启动一个，即单并行度</strong>，否则聚合提交的逻辑就会出现问题。</p><p>感谢各位的观看，如果大家对具体实现感兴趣，可以去 Apache SeaTunnel (Incubating) 的社区查看<strong>api-draft</strong>分支代码，谢谢大家。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[百亿级数据同步，如何基于 SeaTunnel 的 ClickHouse 实现？]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/2022/05/10/ClickHouse</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/2022/05/10/ClickHouse</guid>
            <pubDate>Tue, 10 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[作者 | Apache SeaTunnel(Incubating) Contributor 范佳]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="/zh-CN/assets/images/0-c3f068094d4f0308d7100502a6162925.jpg" width="1920" height="1275" class="img_ev3q"></p><p>作者 | Apache SeaTunnel(Incubating) Contributor 范佳</p><p>整理 | 测试工程师 冯秀兰</p><p>对于百亿级批数据的导入，传统的 JDBC 方式在一些海量数据同步场景下的表现并不尽如人意。为了提供更快的写入速度，Apache SeaTunnel(Incubating) 在刚刚发布的 2.1.1 版本中提供了 ClickhouseFile-Connector 的支持，以实现 Bulk load 数据写入。</p><p>Bulk load 指把海量数据同步到目标 DB 中，目前 SeaTunnel 已实现数据同步到 ClickHouse 中。</p><p>在 Apache SeaTunnel(Incubating) 4 月 Meetup 上，Apache SeaTunnel(Incubating) Contributor 范佳分享了《基于 SeaTunnel 的 ClickHouse bulk load 实现》，详细讲解了 ClickHouseFile 高效处理海量数据的具体实现原理和流程。</p><p>感谢本文整理志愿者 测试工程师 冯秀兰 对 Apache SeaTunnel(Incubating) 项目的支持！</p><p>本次演讲主要包含七个部分：</p><ul><li>ClickHouse Sink 现状
</li><li>ClickHouse Sink 弱场景
</li><li>ClickHouseFile 插件介绍
</li><li>ClickHouseFile 核心技术点
</li><li>ClickHouseFile 插件的实现解析
</li><li>插件能力对比
</li><li>后期优化方向
</li></ul><p><img loading="lazy" src="/zh-CN/assets/images/0-1-56defefcc273a6e21b09dd483bf95914.png" width="1171" height="1171" class="img_ev3q"></p><p>​范 佳白鲸开源 高级工程师</p><h1>01 ClickHouse Sink 现状</h1><p>现阶段，SeaTunnel 把数据同步到 ClickHouse 的流程是：只要是 SeaTunnel 支持的数据源，都可以把数据抽取出来，抽取出来之后，经过转换（也可以不转换），直接把源数据写入 ClickHouse sink connector 中，再通过 JDBC 写入到 ClickHouse 的服务器中。</p><p><img loading="lazy" src="/zh-CN/assets/images/1-76284c6612152506e0111e0f0d25d0f5.png" width="1139" height="585" class="img_ev3q"></p><p>但是，通过传统的 JDBC 写入到 ClickHouse 服务器中会存在一些问题。</p><p>首先，现阶段使用的工具是 ClickHouse 提供的驱动，实现方式是通过 HTTP，然而 HTTP 在某些场景下，实现效率不高。其次是海量数据，如果有重复数据或者一次性写入大量数据，使用传统的方式是生成对应的插入语句，通过 HTTP 发送到 ClickHouse 服务器端，在服务器端来进行逐条或分批次解析、执行，无法实现数据压缩。</p><p>最后就是我们通常会遇到的问题，数据量过大可能导致 SeaTunnel 端 OOM，或者服务器端因为写入数据量过大，频率过高，导致服务器端挂掉。</p><p>于是我们思考，是否有比 HTTP 更快的发送方式？如果可以在 SeaTunnel 端做数据预处理或数据压缩，那么网络带宽压力会降低，传输速率也会提高。</p><h1>02 ClickHouse Sink 的弱场景</h1><p>如果使用 HTTP 传输协议，当数据量过大，批处理以微批的形式发送请求，HTTP 可能处理不过来；</p><p>太多的 insert 请求，服务器压力大。假设带宽可以承受大量的请求，但服务器端不一定能承载。线上的服务器不仅需要数据插入，更重要的是查询数据为其他业务团队使用。若因为插入数据过多导致服务器集群宕机，是得不偿失的。</p><h1>03 ClickHouse File 核心技术点</h1><p>针对这些 ClickHouse 的弱场景，我们想，有没有一种方式，既能在 Spark 端就能完成数据压缩，还可以在数据写入时不增加 Server 的资源负载，并且能快速写入海量数据？于是我们开发了 ClickHouseFile 插件来满足这些需求。</p><p>ClickHouseFile 插件的关键技术是 ClickHouse -local。ClickHouse-local 模式可以让用户能够对本地文件执行快速处理，而无需部署和配置 ClickHouse 服务器。ClickHouse-local 使用与 ClickHouse Server 相同的核心，因此它支持大多数功能以及相同的格式和表引擎。</p><p>因为有这 2 个特点,这意味着用户可以直接处理本地文件，而无需在 ClickHouse 服务器端做处理。由于是相同的格式，我们在远端或者 SeaTunnel 端进行的操作所产生的数据和服务器端是无缝兼容的，可以使用 ClickHouse local 来进行数据写入。ClickHouse local 是实现 ClickHouseFile 的核心技术点，因为有了这个插件，现阶段才能够实现 ClickHouse file 连接器。</p><p>ClickHouse local 核心使用方式：</p><p><img loading="lazy" src="/zh-CN/assets/images/2-2367f70ae655c30a94a2ec65e67a6b26.png" width="1112" height="262" class="img_ev3q"></p><p>第一行：将数据通过 Linux 管道传递给 ClickHouse-local 程序的 test_table 表。</p><p>第二至五行：创建一个 result_table 表用于接收数据。</p><p>第六行：将数据从 test<!-- -->_<!-- -->table 到 result<!-- -->_<!-- -->table 表。</p><p>第七行：定义数据处理的磁盘路径。</p><p>通过调用 Clickhouse-local 组件，实现在 Apache SeaTunnel(Incubating) 端完成数据文件的生成，以及数据压缩等一系列操作。再通过和 Server 进行通信，将生成的数据直接发送到 Clickhouse 的不同节点，再将数据文件提供给节点查询。</p><p>原阶段和现阶段实现方式对比：</p><p><img loading="lazy" src="/zh-CN/assets/images/3-6204c709b48243f88914bfd492dc67f2.png" width="1272" height="576" class="img_ev3q"></p><p>原来是 Spark 把数据包括 insert 语句，发送给服务器端，服务器端做 SQL 的解析，表的数据文件生成、压缩，生成对应的文件、建立对应索引。若使用 ClickHouse local 技术，则由 SeaTunnel 端做数据文件的生成、文件压缩，以及索引的创建，最终产出就是给服务器端使用的文件或文件夹，同步给服务器后，服务器就只需对数据查询，不需要做额外的操作。</p><h1>04 核心技术点</h1><p><img loading="lazy" src="/zh-CN/assets/images/4-d47e1da865afa7ea4de50b2d6e4b6ac1.png" width="1164" height="435" class="img_ev3q"></p><p>以上流程可以促使数据同步更加高效，得益于我们对其中的三点优化。</p><p>第一，数据实际上师从管道传输到 ClickHouseFile，在长度和内存上会有限制。为此，我们将 ClickHouse connector，也就是 sink 端收到的数据通过 MMAP 技术写入临时文件，再由 ClickHouse local 读取临时文件的数据，生成我们的目标 local file，以达到增量读取数据的效果，解决 OM 的问题。</p><p><img loading="lazy" src="/zh-CN/assets/images/5-9f00635b1727843f705cd5a28632e2e4.png" width="1206" height="565" class="img_ev3q"></p><p>第二，支持分片。因为如果在集群中使用，如果只生成一个文件或文件夹，实际上文件只分发到一个节点上，会大大降低查询的性能。因此，我们进行了分片支持，用户可以在配置文件夹中设置分片的 key，算法会将数据分为多个 log file，写入到不同的集群节点中，大幅提升读取性能。</p><p><img loading="lazy" src="/zh-CN/assets/images/6-35b30550d6a18fbea49856083aa85094.png" width="1043" height="558" class="img_ev3q"></p><p>第三个重要的优化是文件传输，目前 SeaTunnel 支持两种文件传输方式，一种是 SCP，其特点是安全、通用、无需额外配置；另一种是 RSYNC，其有点事快速高效，支持断点续传，但需要额外配置，用户可以根据需要选择适合自己的方式。</p><h1>05 插件实现解析</h1><p>概括而言，ClickHouseFile 的总体实现流程如下：</p><p><img loading="lazy" src="/zh-CN/assets/images/7-1be978da30a55fe0289c683f2ae61aac.png" width="533" height="635" class="img_ev3q"></p><ul><li>缓存数据，缓存到 ClickHouse sink 端；
</li><li>调用本地的 ClickHouse-local 生成文件；
</li><li>将数据发送到 ClickHouse 服务端；
</li><li>执行 ATTACH 命令
</li></ul><p>通过以上四个步骤，生成的数据达到可查询的状态。</p><h1>06 插件能力对比</h1><p><img loading="lazy" src="/zh-CN/assets/images/8-261e7ba686f3fadf5d7c1445e9be5b66.png" width="1071" height="485" class="img_ev3q"></p><p>从数据传输角度来说，ClickHouseFile 更适用于海量数据，优势在于不需要额外的配置，通用性强，而 ClickHouseFile 配置比较复杂，目前支持的 engine 较少；</p><p>就环境复杂度来说，ClickHouse 更适合环境复杂度高的情况，不需要额外配置就能直接运行；</p><p>在通用性上，ClickHouse 由于是 SeaTunnel 官方支持的 JDBC diver，基本上支持所有的 engine 的数据写入，ClickHouseFile 支持的 engine 相对较少；从服务器压力方面来说，ClickHouseFile 的优势在海量数据传输时就体现出来了，不会对服务器造成太大的压力。</p><p>但这二者并不是竞争关系，需要根据使用场景来选择。</p><h1>07 后续计划</h1><p>目前虽然 SeaTunnel 支持 ClickHouseFile 插件，但是还有很多地方需要优化，主要包括：</p><ul><li>Rsync 支持；
</li><li>Exactly-Once 支持；
</li><li>支持 Zero Copy 传输数据文件；
</li><li>更多 Engine 的支持</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SeaTunnel 在孩子王的选型过程及应用改造实践]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/2022/05/01/_Kidswant</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/2022/05/01/_Kidswant</guid>
            <pubDate>Sun, 01 May 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[在Apache SeaTunnel(Incubating) 4 月Meetup上，孩子王大数据专家、OLAP平台架构师 袁洪军 为我们带来了《Apache SeaTunnel (Incubating)在孩子王的应用实践》。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="/zh-CN/assets/images/0-38f7968af0b7239e9d427a85adee4452.png" width="1920" height="1080" class="img_ev3q"></p><p>在Apache SeaTunnel(Incubating) 4 月Meetup上，孩子王大数据专家、OLAP平台架构师 袁洪军 为我们带来了《Apache SeaTunnel (Incubating)在孩子王的应用实践》。</p><p>本次演讲主要包含五个部分：</p><ul><li><p>孩子王引入Apache SeaTunnel (Incubating)的背景介绍</p></li><li><p>大数据处理主流工具对比分析</p></li><li><p>Apache SeaTunnel (Incubating)的落地实践</p></li><li><p>Apache SeaTunnel (Incubating)改造中的常见问题</p></li><li><p>对孩子王未来发展方向的预测展望</p></li></ul><p><img loading="lazy" src="/zh-CN/assets/images/0-1-4c853aa726b29acc5954ba53240dc2b8.png" width="2578" height="2567" class="img_ev3q"></p><p>袁洪军</p><p>孩子王 大数据专家、OLAP 平台架构师。多年大数据平台研发管理经验，在数据资产、血缘图谱、数据治理、OLAP 等领域有着丰富的研究经验。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="01-背景介绍">01 背景介绍<a href="#01-背景介绍" class="hash-link" aria-label="01 背景介绍的直接链接" title="01 背景介绍的直接链接">​</a></h2><p><img loading="lazy" src="/zh-CN/assets/images/1-b67aae24cc6274ca2d6a4541848b847d.png" width="1583" height="979" class="img_ev3q"></p><p>目前孩子王的OLAP平台主要包含元数据层、任务层、存储层、SQL层、调度层、服务层以及监控层七部分，本次分享主要关注任务层中的离线任务。</p><p>其实孩子王内部有一套完整的采集推送系统，但由于一些历史遗留问题，公司现有的平台无法快速支持OLAP平台上线，因此当时公司只能选择放弃自身的平台，转而着手研发新的系统。</p><p>当时摆在OLAP面前的有三个选择：</p><p>1、给予采集推送系统做二次研发；</p><p>2、完全自研；</p><p>3、参与开源项目。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="02-大数据处理主流工具对比分析">02 大数据处理主流工具对比分析<a href="#02-大数据处理主流工具对比分析" class="hash-link" aria-label="02 大数据处理主流工具对比分析的直接链接" title="02 大数据处理主流工具对比分析的直接链接">​</a></h2><p>而这三项选择却各有优劣。若采基于采集推送做二次研发，其优点是有前人的经验，能够避免重复踩坑。但缺点是代码量大，研读时间、研读周期较长，而且抽象代码较少，与业务绑定的定制化功能较多，这也导致了其二开的难度较大。</p><p>若完全自研，其优点第一是开发过程自主可控，第二是可以通过Spark等一些引擎做贴合我们自身的架构，但缺点是可能会遭遇一些未知的问题。</p><p>最后如果使用开源框架，其优点一是抽象代码较多，二是经过其他大厂或公司的验证，框架在性能和稳定方面能够得到保障。因此孩子王在OLAP数据同步初期，我们主要研究了DATAX、Sqoop和SeaTunnel这三个开源数据同步工具。</p><p><img loading="lazy" src="/zh-CN/assets/images/2-a6b8a591b0c27aed589b35565edf7c02.png" width="876" height="915" class="img_ev3q"></p><p>从脑图我们可以看到，Sqoop的主要功能是针对RDB的数据同步，其实现方式是基于MAP/REDUCE。Sqoop拥有丰富的参数和命令行可以去执行各种操作。Sqoop的优点在于它首先贴合Hadoop生态，并已经支持大部分RDB到HIVE任意源的转换，拥有完整的命令集和API的分布式数据同步工具。</p><p>但其缺点是Sqoop只支持RDB的数据同步，并且对于数据文件有一定的限制，以及还没有数据清洗的概念。</p><p><img loading="lazy" src="/zh-CN/assets/images/3-3ff7bcde6e57af360cccd3d50e713b8e.png" width="2452" height="1485" class="img_ev3q"></p><p>DataX的主要功能是任意源的数据同步，通过配置化文件+多线程的方式实现，主要分为三个流程：Reader、Framework和Writer，其中Framework主要起到通信和留空的作用。</p><p>DataX的优点是它采用了插件式的开发，拥有自己的流控和数据管控，在社区活跃度上，DataX的官网上提供了许多不同源的数据推送。但DataX的缺点在于它基于内存，对数据量可能存在限制。</p><p><img loading="lazy" src="/zh-CN/assets/images/4-96dfdedcf3c28bf69a805f20cc51e960.png" width="2199" height="1860" class="img_ev3q"></p><p>Apache SeaTunnel (Incubating)做的也是任意源的数据同步，实现流程分为source、transform和sink三步，基于配置文件、Spark或Flink实现。其优点是目前官网2.1.0有非常多的插件和源的推送，基于插件式的思想也使其非常容易扩展，拥抱Spark和Flink的同时也做到了分布式的架构。要说Apache SeaTunnel (Incubating)唯一的缺点可能是目前缺少IP的调用，UI界面需要自己做管控。</p><p>综上所述，Sqoop虽然是分布式，但是仅支持RDB和HIVE、Hbase之间的数据同步且扩展能力差，不利于二开。DataX扩展性好，整体性稳定，但由于是单机版，无法分布式集群部署，且数据抽取能力和机器性能有强依赖关系。而SeaTunnel和DataX类似并弥补了DataX非分布式的问题，对于实时流也做了很好的支持，虽然是新产品，但社区活跃度高。基于是否支持分布式、是否需要单独机器部署等诸多因素的考量，最后我们选择了SeaTunnel。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="03-apache-seatunnel-incubating的落地实践">03 Apache SeaTunnel (Incubating)的落地实践<a href="#03-apache-seatunnel-incubating的落地实践" class="hash-link" aria-label="03 Apache SeaTunnel (Incubating)的落地实践的直接链接" title="03 Apache SeaTunnel (Incubating)的落地实践的直接链接">​</a></h2><p>在Apache SeaTunnel (Incubating)的官网我们可以看到Apache SeaTunnel (Incubating)的基础流程包括source、transform和sink三部分。根据官网的指南，Apache SeaTunnel (Incubating)的启动需要配置脚本，但经过我们的研究发现，Apache SeaTunnel (Incubating)的最终执行是依赖config文件的spark-submit提交的一个Application应用。</p><p>这种初始化方式虽然简单，但存在必须依赖Config文件的问题，每次运行任务后都会生成再进行清除，虽然可以在调度脚本中动态生成，但也产生了两个问题。1、频繁的磁盘操作是否有意义；2、是否存在更为高效的方式支持Apache SeaTunnel (Incubating)的运行。</p><p><img loading="lazy" src="/zh-CN/assets/images/5-94809b2d6f1fbbac48a249728119e5af.png" width="1597" height="973" class="img_ev3q"></p><p>基于以上考量，在最终的设计方案中，我们增加了一个统一配置模板平台模块。调度时只需要发起一个提交命令，由Apache SeaTunnel (Incubating)自身去统一配置模板平台中拉取配置信息，再去装载和初始化参数。</p><p><img loading="lazy" src="/zh-CN/assets/images/5-1-05477f37c0f5e72755d1335853ea650e.png" width="1496" height="1022" class="img_ev3q"></p><p>上图展示的便是孩子王OLAP的业务流程，主要分为三块。数据从Parquet，即Hive，通过Parquet表的方式到KYLIN和CK source的整体流程。</p><p><img loading="lazy" src="/zh-CN/assets/images/7-1317712145fdcaee3b0219f09f68a739.png" width="1205" height="766" class="img_ev3q"></p><p>这是我们建模的页面，主要通过拖拉拽的方式生成最终模型，每个表之间通过一些交易操作，右侧是针对Apache SeaTunnel (Incubating)的微处理。</p><p><img loading="lazy" src="/zh-CN/assets/images/8-d476fef7e065db158f63dd1b3291543b.jpg" width="1306" height="609" class="img_ev3q"></p><p>因此我们最终提交的命令如上，其中标红的首先是【-conf customconfig/jars】，指用户可以再统一配置模板平台进行处理，或者建模时单独指定。最后标红的【421 $start_time $end_time $taskType】Unicode，属于唯一编码。</p><p>下方图左就是我们最终调度脚本提交的38个命令，下方图右是针对Apache SeaTunnel (Incubating)做的改造，可以看到一个较为特殊的名为WaterdropContext的工具类。可以首先判断Unicode是否存在，再通过Unicode_code来获取不同模板的配置信息，避免了config文件的操作。</p><p>在最后的reportMeta则是用于在任务执行完成后上报一些信息，这也会在Apache SeaTunnel (Incubating)中完成。</p><p><img loading="lazy" src="/zh-CN/assets/images/9-018ad4a2a0de58d2d3c9c69fffbcd06c.png" width="1136" height="940" class="img_ev3q"></p><p><img loading="lazy" src="/zh-CN/assets/images/10-f0199109244a92f734e89024bef0b51b.png" width="944" height="388" class="img_ev3q"></p><p><img loading="lazy" src="data:image/png;base64,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s7IJECBAgAABAs8QKAuvz5izWRAgQIAAAQIECDQqUBBeu21XR1QbXWZlEyBAgAABAgSeIRANr+cl1y4QO43wjJvJLAgQIECAAAECZwtEw2tXR36s1bFZ87xYfLad/gkQIECAAAECBC4WiIbXbn80fbr6HB64eJEMR4AAAQIECBAgkASi4TW1Tnuux+68WgkCBAgQIECAAAECQYGy8Nrffw0OoBkBAgQIECBAgACBowTKwutRo+Z+bOIeTqpDAgQIECBAgMCDBSqH13yO9sHEpkaAAAECBAgQIHCUQOXwetQ09EOAAAECBAgQIPAGAeH1DatsjgQIECBAgACBhwgIrw9ZSNMgQIAAAQIECLxBQHh9wyqbIwECBAgQIEDgIQLC60MW0jQIECBAgAABAm8QEF7fsMrmSIAAAQIECBB4iIDw+pCFNA0CBAgQIECAwBsETgmv3asHvH3gDXePORIgQIAAAQIELhY4Jbx2rx64eBqGI0CAAAECBAgQeINANLymzdT8eQONORIgQIAAAQIECNxNIBpeu7rTq1y90PVuS6geAgQIECBAgMB7BArC63tQzJQAAQIECBAgQOCeAgXh1ZmBey6hqggQIECAAAEC7xEoCK/5zEDkSQJd40iz90CbKQECBAgQIECAwH6BaHgtfYBAl1xLL9k/GT0QIECAAAECBAg8WyAaXm2jPvs+MDsCBAgQIECAQBMC0fCajgGkjy3VJpZWkQQIECBAgACB5wlEw2s3c8/Jet7ymxEBAgQIECBAoC2BgvAan5gzBnErLQkQIECAAAECBOICp4RXLzKIL4CWBAgQIECAAAECcYFTwmt8eC0JECBAgAABAgQIxAWE17iVlgQIECBAgAABApUFhNfKC2B4AgQIECBAgACBuIDwGrfSkgABAgQIECBAoLKA8Fp5AQxPgAABAgQIECAQFxBe41ZaEiBAgAABAgQIVBYQXisvgOEJECBAgAABAgTiAsJr3EpLAgQIECBAgACBygLCa+UFMDwBAgQIECBAgEBcoCy8du999erXOK6WBAgQIECAAAECxwoUhNcutqb3vsqvx66B3ggQIECAAAECBIIC0fCakmvqNP8QHEMzAgQIECBAgAABAocIRMNrGsyxgUPQdUKAAAECBAgQILBNoCC8OjawjdhVBAgQIECAAAECRwkUhFenBY5C1w8BAgQIECBA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" width="916" height="348" class="img_ev3q"></p><p>在最终完成的config文件如上，值得注意的是在transform方面，孩子王做了一些改造。首先是针对手机或者身份证号等做脱敏处理，如果用户指定字段，就按照字段做，如果不指定字段就扫描所有字段，然后根据模式匹配，进行脱敏加密。</p><p>第二transform还支持自定义处理，如上文说道OLAP建模的时候说到。加入了HideStr，可以保留一串字符的前十个字段，加密后方的所有字符，在数据安全上有所保障。</p><p>然后，在sink端，我们为了支持任务的幂等性，我们加入了pre_sql，这主要完成的任务是数据的删除，或分区的删除，因为任务在生产过程中不可能只运行一次，一旦出现重跑或补数等操作，就需要这一部分为数据的不同和正确性做考量。</p><p>在图右方的一个Clickhouse的Sink端，这里我们加入了一个is_senseless_mode，它组成了一个读写分离的无感模式，用户在查询和补数的时候不感知整体区域，而是用到CK的分区转换，即名为MOVE PARTITION TO TABLE的命令进行操作的。</p><p>此处特别说明KYLIN的Sink端，KYLIN是一个非常特殊的源，拥有自己一整套数据录入的逻辑，而且，他有自己的监控页面，因此我们给予KYLIN的改造只是简单地调用其API操作，在使用KYLIN时也只是简单的API调用和不断轮询的状态，所以KYLIN这块的资源在统一模板配置平台就被限制地很小。</p><p><img loading="lazy" src="/zh-CN/assets/images/12-61af6fe2e1a38126ec03a0291e15c1bf.jpg" width="1307" height="593" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="04-apache-seatunnel-incubating改造中的常见问题">04 Apache SeaTunnel (Incubating)改造中的常见问题<a href="#04-apache-seatunnel-incubating改造中的常见问题" class="hash-link" aria-label="04 Apache SeaTunnel (Incubating)改造中的常见问题的直接链接" title="04 Apache SeaTunnel (Incubating)改造中的常见问题的直接链接">​</a></h2><p>1、OOM&amp;Too many Parts</p><p>问题通常会出现在Hive到Hive的过程中，即使我们通过了自动资源的分配，但也存在数据突然间变大的情况，比如在举办了多次活动之后。这样的问题其实只能通过手动动态地调参，调整数据同步批量时间来避免。未来我们可能尽力去完成对于数据量的掌握，做到精细的控制。</p><p>2、字段、类型不一致问题</p><p>模型上线后，任务依赖的上游表或者字段，用户都会做一些修改，这些修改若无法感知，可能导致任务的失败。目前解决方法是依托血缘+快照的方式进行提前感知来避免错误。</p><p>3、自定义数据源&amp;自定义分隔符</p><p>如财务部门需要单独使用的分割符，或是jar信息，现在用户可以自己在统一配置模板平台指定加载额外jar信息以及分割符信息。</p><p>4、数据倾斜问题</p><p>这可能因为用户自己设置了并行度，但无法做到尽善尽美。这一块我们暂时还没有完成处理，后续的思路可能在Source模块中添加post处理，对数据进行打散，完成倾斜。</p><p>5、KYLIN全局字典锁问题</p><p>随着业务发展，一个cube无法满足用户使用，就能需要建立多个cube，如果多个cube之间用了相同的字段，就会遇到KYLIN全局字典锁的问题。目前解决的思路是把两个或多个任务之间的调度时间进行隔开，如果无法隔开，可以做一个分布式锁的控制。KYLIN的sink端必须要拿到锁才能运行。</p><p>05 对孩子王未来发展方向的预测展望</p><ul><li><p>多源数据同步，未来可能针对RDB源进行处理</p></li><li><p>基于实时Flink的实现</p></li><li><p>接管已有采集调度平台（主要解决分库分表的问题）</p></li><li><p>数据质量校验，像一些空值、整个数据的空置率、主时间的判断等</p></li></ul><p>我的分享就到这里，希望以后可以和社区多多交流，共同进步，谢谢！</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[智能化时代的数据集成技术革新]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/智能化时代的数据集成技术革新</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/智能化时代的数据集成技术革新</guid>
            <pubDate>Fri, 08 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[1]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" alt="1" src="/zh-CN/assets/images/1-3abb262dd4a58a521e31f7249b2058d2.png" width="900" height="383" class="img_ev3q"></p><p>可管理，可调用，可计算，可变现的数据资源才能成为资产，信息系统的互联互通使得多源和多维度的数据集成需求巨大，这就对数据处理和集成的工具提出了严苛的要求。</p><p>智能化时代，在“智慧城市”、“智慧治理”、“产品智能化”等的趋势下，企业大多面临如何实现高效数据推送，提高平台质量，以及保障数据安全的挑战。选对数据集成工具和平台，数据才能发挥出做大的作用。</p><p>Apache SeaTunnel (Incubating) 作为下一代高性能、分布式、海量数据集成框架，致力于让数据同步更简单，更高效，加快分布式数据处理能力在生产环境落地。</p><p>在 Apache SeaTunnel(Incubating) Meetup（2022&nbsp;年&nbsp;4&nbsp;月&nbsp;16日），Apache SeaTunnel(Incubating) 社区将邀请了 Apache SeaTunnel(Incubating)的资深用户，分享 Apache SeaTunnel(Incubating)在智能化生产环境中落地的最佳实践。此外，还会有贡献者现场进行 Apache SeaTunnel(Incubating)的源码解析，让你对 Apache SeaTunnel(Incubating)有一个更加全面而深入的了解。</p><p>无论你是对 Apache SeaTunnel(Incubating)抱有兴趣的初学者，还是在日常的生产实践中遭遇了复杂棘手的部署问题，都可以来到这里，与我们的讲师近距离沟通，得到你想要的答案。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="01-报-名-通-道">01 报 名 通 道<a href="#01-报-名-通-道" class="hash-link" aria-label="01 报 名 通 道的直接链接" title="01 报 名 通 道的直接链接">​</a></h2><p>Apache SeaTunnel (Incubating) Meetup | 4 月线上直播报名通道已开启，赶快预约吧！</p><p>时间：2022-4-16 14:00-17:00</p><p>形式：线上直播</p><p>点击链接或扫码预约报名（免费）：</p><p><img loading="lazy" alt="2" src="/zh-CN/assets/images/2-e9c01734f2c74a12f59e2f3eecd2a2c6.png" width="300" height="300" class="img_ev3q"></p><p>扫码预约报名</p><p><img loading="lazy" alt="3" src="/zh-CN/assets/images/4-b0dd1cb8a1432483e3f3202cbea5c271.png" width="498" height="507" class="img_ev3q"></p><p>扫码进直播群</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="02-活-动-亮-点">02 活 动 亮 点<a href="#02-活-动-亮-点" class="hash-link" aria-label="02 活 动 亮 点的直接链接" title="02 活 动 亮 点的直接链接">​</a></h2><ul><li>行业案例详解</li><li>特色功能分析</li><li>一线企业踩坑心得</li><li>开源社区实战攻略</li><li>行业技术专家面对面 Q&amp;A</li><li>惊喜礼品送不停</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="03-活-动-议-程">03 活 动 议 程<a href="#03-活-动-议-程" class="hash-link" aria-label="03 活 动 议 程的直接链接" title="03 活 动 议 程的直接链接">​</a></h2><p>活动当天，将有来自孩子王、oppo 的工程师现场分享来自厂商的一线前沿实践经验，还有来自白鲸开源的高级工程师对 Apache SeaTunnel(Incubating)的重要功能更新进行“硬核”讲解，干货满满。</p><p><img loading="lazy" alt="4" src="/zh-CN/assets/images/5-f80f0bce07f15e8f83f603d16825dde4.png" width="1080" height="1075" class="img_ev3q"></p><p>袁洪军&nbsp;孩子王 大数据专家、OLAP 平台架构师</p><p>多年大数据平台研发管理经验，在数据资产、血缘图谱、数据治理、OLAP 等领域有着丰富的研究经验</p><p>演讲时间：14:00-14:40</p><p>演讲题目：Apache SeaTunnel(Incubating) 在孩子王的应用实践</p><p>演讲概要： 如何实现高效数据推送？如何提高平台质量？如何保障数据安全？孩子王又对 Apache SeaTunnel(Incubating)做了哪些改造？</p><p><img loading="lazy" alt="6" src="/zh-CN/assets/images/6-8f02c4507ef50d7a109ddf227c45a7a5.png" width="1080" height="1080" class="img_ev3q"></p><p>范佳&nbsp;白鲸开源&nbsp; 高级工程师 Apache SeaTunnel Contributor</p><p>演讲时间： 14:40-15:20</p><p>演讲题目： 基于 Apache SeaTunnel(Incubating)的 Clickhouse Bulk Load 实现</p><p>演讲概要： 通过扩展 Apache SeaTunnel(Incubating)的 Connector实现 Clickhouse的 bulk load 数据同步功能。</p><p><img loading="lazy" alt="7" src="/zh-CN/assets/images/7-8b4c7798dd107093cb31b526eb3d8476.png" width="1080" height="1078" class="img_ev3q"></p><p>王子超&nbsp;oppo&nbsp;高级后端工程师</p><p>演讲时间： 15:50-16:30</p><p>演讲题目： oppo智能推荐样本中心基于 Apache SeaTunnel(Incubating)的技术革新</p><p>演讲概要： 介绍 oppo 智能推荐机器学习样本流程的演进及 Apache &nbsp;SeaTunnel(Incubating)&nbsp;在其中发挥的作用。</p><p>除了精彩的演讲之外，现场还设置了多个抽奖环节，参与抽奖有机会获得&nbsp;Apache&nbsp;SeaTunnel(Incubating)&nbsp;精美定制礼品，敬请期待~</p>]]></content:encoded>
            <category>Meetup</category>
        </item>
        <item>
            <title><![CDATA[Apache SeaTunnel(Incubating) 首个Apache 版本 2.1.0 发布，内核重构，全面支持Flink]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/Apache SeaTunnel(Incubating) 首个Apache 版本 2.1.0 发布，内核重构，全面支持Flink</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/Apache SeaTunnel(Incubating) 首个Apache 版本 2.1.0 发布，内核重构，全面支持Flink</guid>
            <pubDate>Fri, 18 Mar 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[2021 年 12 月 9 日，Apache SeaTunnel(Incubating) 进入 Apache 孵化器，在经过社区各位贡献者近四个月的努力下，我们于2022年3月18日发布了首个Apache版本，并且保证了首个版本一次性通过检查。这意味着 2.1.0 版本，是经过 Apache SeaTunnel(Incubating) 社区和 Apache 孵化器投票检查发布的官方版本，企业和个人用户可以放心安全使用。]]></description>
            <category>2.1.0</category>
            <category>Release</category>
        </item>
        <item>
            <title><![CDATA[如何快速地把 HDFS 中的数据导入 ClickHouse]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/hdfs-to-clickhouse</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/hdfs-to-clickhouse</guid>
            <pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[ClickHouse 是面向 OLAP 的分布式列式 DBMS。我们部门目前已经把所有数据分析相关的日志数据存储至 ClickHouse 这个优秀的数据仓库之中，当前日数据量达到了 300 亿。]]></description>
            <content:encoded><![CDATA[<p>ClickHouse 是面向 OLAP 的分布式列式 DBMS。我们部门目前已经把所有数据分析相关的日志数据存储至 ClickHouse 这个优秀的数据仓库之中，当前日数据量达到了 300 亿。</p><p>之前介绍的有关数据处理入库的经验都是基于实时数据流，数据存储在 Kafka 中，我们使用 Java 或者 Golang 将数据从 Kafka 中读取、解析、清洗之后写入 ClickHouse 中，这样可以实现数据的快速接入。然而在很多同学的使用场景中，数据都不是实时的，可能需要将 HDFS 或者是 Hive 中的数据导入 ClickHouse。有的同学通过编写 Spark 程序来实现数据的导入，那么是否有更简单、高效的方法呢。</p><p>目前开源社区上有一款工具 <strong>Seatunnel</strong>，项目地址 <a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-seatunnel</a>，可以快速地将 HDFS 中的数据导入 ClickHouse。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="hdfs-to-clickhouse">HDFS To ClickHouse<a href="#hdfs-to-clickhouse" class="hash-link" aria-label="HDFS To ClickHouse的直接链接" title="HDFS To ClickHouse的直接链接">​</a></h2><p>假设我们的日志存储在 HDFS 中，我们需要将日志进行解析并筛选出我们关心的字段，将对应的字段写入 ClickHouse 的表中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="log-sample">Log Sample<a href="#log-sample" class="hash-link" aria-label="Log Sample的直接链接" title="Log Sample的直接链接">​</a></h3><p>我们在 HDFS 中存储的日志格式如下， 是很常见的 Nginx 日志</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token number">10.41</span><span class="token plain">.1.28 github.com </span><span class="token number">114.250</span><span class="token plain">.140.241 </span><span class="token number">0</span><span class="token plain">.001s </span><span class="token string" style="color:rgb(255, 121, 198)">"127.0.0.1:80"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">26</span><span class="token plain">/Oct/2018:03:09:32 +0800</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"GET /Apache/Seatunnel HTTP/1.1"</span><span class="token plain"> </span><span class="token number">200</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-"</span><span class="token plain"> - </span><span class="token string" style="color:rgb(255, 121, 198)">"Dalvik/2.1.0 (Linux; U; Android 7.1.1; OPPO R11 Build/NMF26X)"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"196"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"mainpage"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"443"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"172.16.181.129"</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="clickhouse-schema">ClickHouse Schema<a href="#clickhouse-schema" class="hash-link" aria-label="ClickHouse Schema的直接链接" title="ClickHouse Schema的直接链接">​</a></h3><p>我们的 ClickHouse 建表语句如下，我们的表按日进行分区</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">CREATE TABLE cms.cms_msg</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">date</span><span class="token plain"> Date, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    datetime DateTime, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    url String, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    request_time Float32, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    status String, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">hostname</span><span class="token plain"> String, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    domain String, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    remote_addr String, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    data_size Int32, </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    pool String</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> ENGINE </span><span class="token operator">=</span><span class="token plain"> MergeTree PARTITION BY </span><span class="token function" style="color:rgb(80, 250, 123)">date</span><span class="token plain"> ORDER BY </span><span class="token function" style="color:rgb(80, 250, 123)">date</span><span class="token plain"> SETTINGS index_granularity </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">16384</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-with-clickhouse">Seatunnel with ClickHouse<a href="#seatunnel-with-clickhouse" class="hash-link" aria-label="Seatunnel with ClickHouse的直接链接" title="Seatunnel with ClickHouse的直接链接">​</a></h2><p>接下来会给大家详细介绍，我们如何通过 Seatunnel 满足上述需求，将 HDFS 中的数据写入 ClickHouse 中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel">Seatunnel<a href="#seatunnel" class="hash-link" aria-label="Seatunnel的直接链接" title="Seatunnel的直接链接">​</a></h3><p><a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">Seatunnel</a> 是一个非常易用，高性能，能够应对海量数据的实时数据处理产品，它构建在Spark之上。Seatunnel 拥有着非常丰富的插件，支持从 Kafka、HDFS、Kudu 中读取数据，进行各种各样的数据处理，并将结果写入 ClickHouse、Elasticsearch 或者 Kafka 中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="#prerequisites" class="hash-link" aria-label="Prerequisites的直接链接" title="Prerequisites的直接链接">​</a></h3><p>首先我们需要安装 Seatunnel，安装十分简单，无需配置系统环境变量</p><ol><li>准备 Spark 环境</li><li>安装 Seatunnel</li><li>配置 Seatunnel</li></ol><p>以下是简易步骤，具体安装可以参照 <a href="/zh-CN/docs/quick-start">Quick Start</a></p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> /usr/local</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">wget</span><span class="token plain"> https://archive.apache.org/dist/spark/spark-2.2.0/spark-2.2.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">tar</span><span class="token plain"> -xvf https://archive.apache.org/dist/spark/spark-2.2.0/spark-2.2.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">wget</span><span class="token plain"> https://github.com/InterestingLab/seatunnel/releases/download/v1.1.1/seatunnel-1.1.1.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">unzip</span><span class="token plain"> seatunnel-1.1.1.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> seatunnel-1.1.1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">vim</span><span class="token plain"> config/seatunnel-env.sh</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 指定Spark安装路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">SPARK_HOME</span><span class="token operator">=</span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">${SPARK_HOME</span><span class="token variable operator" style="color:rgb(189, 147, 249);font-style:italic">:-</span><span class="token variable operator" style="color:rgb(189, 147, 249);font-style:italic">/</span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">usr</span><span class="token variable operator" style="color:rgb(189, 147, 249);font-style:italic">/</span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">local</span><span class="token variable operator" style="color:rgb(189, 147, 249);font-style:italic">/</span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">spark-2.2.0-bin-hadoop2.7}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-pipeline">seatunnel Pipeline<a href="#seatunnel-pipeline" class="hash-link" aria-label="seatunnel Pipeline的直接链接" title="seatunnel Pipeline的直接链接">​</a></h3><p>我们仅需要编写一个 seatunnel Pipeline 的配置文件即可完成数据的导入。</p><p>配置文件包括四个部分，分别是 Spark、Input、filter 和 Output。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="spark">Spark<a href="#spark" class="hash-link" aria-label="Spark的直接链接" title="Spark的直接链接">​</a></h4><p>这一部分是 Spark 的相关配置，主要配置 Spark 执行时所需的资源大小。</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"seatunnel"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1g"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="input">Input<a href="#input" class="hash-link" aria-label="Input的直接链接" title="Input的直接链接">​</a></h4><p>这一部分定义数据源，如下是从 HDFS 文件中读取 text 格式数据的配置案例。</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">input </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hdfs </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        path </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hdfs://nomanode:8020/rowlog/accesslog"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access_log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token function" style="color:rgb(80, 250, 123)">format</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="filter">Filter<a href="#filter" class="hash-link" aria-label="Filter的直接链接" title="Filter的直接链接">​</a></h4><p>在 Filter 部分，这里我们配置一系列的转化，包括正则解析将日志进行拆分、时间转换将 HTTPDATE 转化为 ClickHouse 支持的日期格式、对 Number 类型的字段进行类型转换以及通过 SQL 进行字段筛减等</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">filter </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用正则解析原始日志</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    grok </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"raw_message"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pattern </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%{IP:ha_ip}\\s%{NOTSPACE:domain}\\s%{IP:remote_addr}\\s%{NUMBER:request_time}s\\s\"%{DATA:upstream_ip}\"\\s\\[%{HTTPDATE:timestamp}\\]\\s\"%{NOTSPACE:method}\\s%{DATA:url}\\s%{NOTSPACE:http_ver}\"\\s%{NUMBER:status}\\s%{NUMBER:body_bytes_send}\\s%{DATA:referer}\\s%{NOTSPACE:cookie_info}\\s\"%{DATA:user_agent}\"\\s%{DATA:uid}\\s%{DATA:session_id}\\s\"%{DATA:pool}\"\\s\"%{DATA:tag2}\"\\s%{DATA:tag3}\\s%{DATA:tag4}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 将"dd/MMM/yyyy:HH:mm:ss Z"格式的数据转换为</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># "yyyy/MM/dd HH:mm:ss"格式的数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">date</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"timestamp"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"datetime"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_time_format </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dd/MMM/yyyy:HH:mm:ss Z"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_time_format </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"yyyy/MM/dd HH:mm:ss"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用SQL筛选关注的字段，并对字段进行处理</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 甚至可以通过过滤条件过滤掉不关心的数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"select substring(date, 1, 10) as date, datetime, hostname, url, http_code, float(request_time), int(data_size), domain from access"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="output">Output<a href="#output" class="hash-link" aria-label="Output的直接链接" title="Output的直接链接">​</a></h4><p>最后我们将处理好的结构化数据写入 ClickHouse</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">output </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    clickhouse </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token function" style="color:rgb(80, 250, 123)">host</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your.clickhouse.host:8123"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"seatunnel"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access_log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        fields </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"date"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"datetime"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"hostname"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"uri"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"http_code"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"request_time"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"data_size"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"domain"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        username </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"username"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"password"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="running-seatunnel">Running seatunnel<a href="#running-seatunnel" class="hash-link" aria-label="Running seatunnel的直接链接" title="Running seatunnel的直接链接">​</a></h3><p>我们将上述四部分配置组合成为我们的配置文件 <code>config/batch.conf</code>。</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">vim</span><span class="token plain"> config/batch.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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"seatunnel"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1g"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hdfs </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        path </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"hdfs://nomanode:8020/rowlog/accesslog"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access_log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token function" style="color:rgb(80, 250, 123)">format</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"text"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用正则解析原始日志</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    grok </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"raw_message"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pattern </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'%{IP:ha_ip}\\s%{NOTSPACE:domain}\\s%{IP:remote_addr}\\s%{NUMBER:request_time}s\\s\"%{DATA:upstream_ip}\"\\s\\[%{HTTPDATE:timestamp}\\]\\s\"%{NOTSPACE:method}\\s%{DATA:url}\\s%{NOTSPACE:http_ver}\"\\s%{NUMBER:status}\\s%{NUMBER:body_bytes_send}\\s%{DATA:referer}\\s%{NOTSPACE:cookie_info}\\s\"%{DATA:user_agent}\"\\s%{DATA:uid}\\s%{DATA:session_id}\\s\"%{DATA:pool}\"\\s\"%{DATA:tag2}\"\\s%{DATA:tag3}\\s%{DATA:tag4}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 将"dd/MMM/yyyy:HH:mm:ss Z"格式的数据转换为</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># "yyyy/MM/dd HH:mm:ss"格式的数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">date</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"timestamp"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_field </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"datetime"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_time_format </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dd/MMM/yyyy:HH:mm:ss Z"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_time_format </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"yyyy/MM/dd HH:mm:ss"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用SQL筛选关注的字段，并对字段进行处理</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 甚至可以通过过滤条件过滤掉不关心的数据</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"select substring(date, 1, 10) as date, datetime, hostname, url, http_code, float(request_time), int(data_size), domain from access"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    clickhouse </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token function" style="color:rgb(80, 250, 123)">host</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your.clickhouse.host:8123"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"seatunnel"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"access_log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        fields </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"date"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"datetime"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"hostname"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"uri"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"http_code"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"request_time"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"data_size"</span><span class="token plain">, </span><span class="token string" style="color:rgb(255, 121, 198)">"domain"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        username </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"username"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"password"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>执行命令，指定配置文件，运行 Seatunnel，即可将数据写入 ClickHouse。这里我们以本地模式为例。</p><div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">./bin/start-seatunnel.sh --config config/batch.conf -e client -m </span><span class="token string" style="color:rgb(255, 121, 198)">'local[2]'</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="conclusion">Conclusion<a href="#conclusion" class="hash-link" aria-label="Conclusion的直接链接" title="Conclusion的直接链接">​</a></h2><p>在这篇文章中，我们介绍了如何使用 Seatunnel 将 HDFS 中的 Nginx 日志文件导入 ClickHouse 中。仅通过一个配置文件便可快速完成数据的导入，无需编写任何代码。除了支持 HDFS 数据源之外，Seatunnel 同样支持将数据从 Kafka 中实时读取处理写入 ClickHouse 中。我们的下一篇文章将会介绍，如何将 Hive 中的数据快速导入 ClickHouse 中。</p><p>当然，Seatunnel 不仅仅是 ClickHouse 数据写入的工具，在 Elasticsearch 以及 Kafka等 数据源的写入上同样可以扮演相当重要的角色。</p><p>希望了解 Seatunnel 和 ClickHouse、Elasticsearch、Kafka 结合使用的更多功能和案例，可以直接进入官网 <a href="https://seatunnel.apache.org/" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/</a></p><p>-- Power by <a href="https://github.com/InterestingLab" target="_blank" rel="noopener noreferrer">InterestingLab</a></p>]]></content:encoded>
            <category>HDFS</category>
            <category>ClickHouse</category>
        </item>
        <item>
            <title><![CDATA[如何快速地把 Hive 中的数据导入 ClickHouse]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/hive-to-clickhouse</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/hive-to-clickhouse</guid>
            <pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[ClickHouse是面向OLAP的分布式列式DBMS。我们部门目前已经把所有数据分析相关的日志数据存储至ClickHouse这个优秀的数据仓库之中，当前日数据量达到了300亿。]]></description>
            <content:encoded><![CDATA[<p>ClickHouse是面向OLAP的分布式列式DBMS。我们部门目前已经把所有数据分析相关的日志数据存储至ClickHouse这个优秀的数据仓库之中，当前日数据量达到了300亿。</p><p>在之前的文章 <a href="/zh-CN/blog/i18n/zh-CN/docusaurus-plugin-content-blog/current/2021-12-30-hdfs-to-clickhouse.mdtent-blog/current/2021-12-30-hdfs-to-clickhouse.md">如何快速地把HDFS中的数据导入ClickHouse</a> 中我们提到过使用 Seatunnel <a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-seatunnel</a> 对HDFS中的数据经过很简单的操作就可以将数据写入ClickHouse。HDFS中的数据一般是非结构化的数据，那么针对存储在Hive中的结构化数据，我们应该怎么操作呢？</p><p><img loading="lazy" src="/zh-CN/assets/images/hive-logo-c9aedd90b5ea9668c87fe25ad92a8e6c.png" width="962" height="518" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="hive-to-clickhouse">Hive to ClickHouse<a href="#hive-to-clickhouse" class="hash-link" aria-label="Hive to ClickHouse的直接链接" title="Hive to ClickHouse的直接链接">​</a></h2><p>假定我们的数据已经存储在Hive中，我们需要读取Hive表中的数据并筛选出我们关心的字段，或者对字段进行转换，最后将对应的字段写入ClickHouse的表中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="hive-schema">Hive Schema<a href="#hive-schema" class="hash-link" aria-label="Hive Schema的直接链接" title="Hive Schema的直接链接">​</a></h3><p>我们在Hive中存储的数据表结构如下，存储的是很常见的Nginx日志</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">CREATE TABLE `nginx_msg_detail`(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `hostname` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `domain` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `remote_addr` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `request_time` float,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `datetime` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `url` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `status` int,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `data_size` int,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `referer` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `cookie_info` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `user_agent` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `minute` string)</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> PARTITIONED BY (</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `date` string,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   `hour` string)</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="clickhouse-schema">ClickHouse Schema<a href="#clickhouse-schema" class="hash-link" aria-label="ClickHouse Schema的直接链接" title="ClickHouse Schema的直接链接">​</a></h3><p>我们的ClickHouse建表语句如下，我们的表按日进行分区</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">CREATE TABLE cms.cms_msg</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    date Date,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    datetime DateTime,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    url String,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    request_time Float32,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    status String,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hostname String,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    domain String,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    remote_addr String,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    data_size Int32</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">) ENGINE = MergeTree PARTITION BY date ORDER BY (date, hostname) SETTINGS index_granularity = 16384</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-with-clickhouse">Seatunnel with ClickHouse<a href="#seatunnel-with-clickhouse" class="hash-link" aria-label="Seatunnel with ClickHouse的直接链接" title="Seatunnel with ClickHouse的直接链接">​</a></h2><p>接下来会给大家介绍，我们如何通过 Seatunnel 将Hive中的数据写入ClickHouse中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel">Seatunnel<a href="#seatunnel" class="hash-link" aria-label="Seatunnel的直接链接" title="Seatunnel的直接链接">​</a></h3><p><a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">Seatunnel</a> 是一个非常易用，高性能，能够应对海量数据的实时数据处理产品，它构建在Spark之上。Seatunnel 拥有着非常丰富的插件，支持从Kafka、HDFS、Kudu中读取数据，进行各种各样的数据处理，并将结果写入ClickHouse、Elasticsearch或者Kafka中。</p><p>Seatunnel的环境准备以及安装步骤这里就不一一赘述了，具体安装步骤可以参考上一篇文章或者访问 <a href="/zh-CN/docs/intro/about">Seatunnel Docs</a></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel-pipeline">Seatunnel Pipeline<a href="#seatunnel-pipeline" class="hash-link" aria-label="Seatunnel Pipeline的直接链接" title="Seatunnel Pipeline的直接链接">​</a></h3><p>我们仅需要编写一个Seatunnel Pipeline的配置文件即可完成数据的导入。</p><p>配置文件包括四个部分，分别是Spark、Input、filter和Output。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="spark">Spark<a href="#spark" class="hash-link" aria-label="Spark的直接链接" title="Spark的直接链接">​</a></h4><p>这一部分是Spark的相关配置，主要配置Spark执行时所需的资源大小。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  // 这个配置必需填写</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.sql.catalogImplementation = "hive"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="input">Input<a href="#input" class="hash-link" aria-label="Input的直接链接" title="Input的直接链接">​</a></h4><p>这一部分定义数据源，如下是从Hive文件中读取text格式数据的配置案例。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hive {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pre_sql = "select * from access.nginx_msg_detail"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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中读取数据了。其中<code>pre_sql</code>是从Hive中读取数据SQL，<code>table_name</code>是将读取后的数据，注册成为Spark中临时表的表名，可为任意字段。</p><p>需要注意的是，必须保证hive的metastore是在服务状态。</p><p>在Cluster、Client、Local模式下运行时，必须把<code>hive-site.xml</code>文件置于提交任务节点的$HADOOP_CONF目录下</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="filter">Filter<a href="#filter" class="hash-link" aria-label="Filter的直接链接" title="Filter的直接链接">​</a></h4><p>在Filter部分，这里我们配置一系列的转化，我们这里把不需要的minute和hour字段丢弃。当然我们也可以在读取Hive的时候通过<code>pre_sql</code>不读取这些字段</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    remove {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = ["minute", "hour"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="output">Output<a href="#output" class="hash-link" aria-label="Output的直接链接" title="Output的直接链接">​</a></h4><p>最后我们将处理好的结构化数据写入ClickHouse</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    clickhouse {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        host = "your.clickhouse.host:8123"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table = "nginx_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        fields = ["date", "datetime", "hostname", "url", "http_code", "request_time", "data_size", "domain"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        username = "username"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password = "password"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="running-seatunnel">Running Seatunnel<a href="#running-seatunnel" class="hash-link" aria-label="Running Seatunnel的直接链接" title="Running Seatunnel的直接链接">​</a></h3><p>我们将上述四部分配置组合成为我们的配置文件<code>config/batch.conf</code>。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">vim config/batch.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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  // 这个配置必需填写</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.sql.catalogImplementation = "hive"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hive {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pre_sql = "select * from access.nginx_msg_detail"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    remove {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = ["minute", "hour"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    clickhouse {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        host = "your.clickhouse.host:8123"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table = "access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        fields = ["date", "datetime", "hostname", "uri", "http_code", "request_time", "data_size", "domain"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        username = "username"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password = "password"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>执行命令，指定配置文件，运行 Seatunnel，即可将数据写入ClickHouse。这里我们以本地模式为例。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">./bin/start-seatunnel.sh --config config/batch.conf -e client -m 'local[2]'</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="conclusion">Conclusion<a href="#conclusion" class="hash-link" aria-label="Conclusion的直接链接" title="Conclusion的直接链接">​</a></h2><p>在这篇文章中，我们介绍了如何使用 Seatunnel 将Hive中的数据导入ClickHouse中。仅仅通过一个配置文件便可快速完成数据的导入，无需编写任何代码，十分简单。</p><p>希望了解 Seatunnel 与ClickHouse、Elasticsearch、Kafka、Hadoop结合使用的更多功能和案例，可以直接进入官网 <a href="https://seatunnel.apache.org/" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/</a></p><p>-- Power by <a href="https://github.com/InterestingLab" target="_blank" rel="noopener noreferrer">InterestingLab</a></p>]]></content:encoded>
            <category>Hive</category>
            <category>ClickHouse</category>
        </item>
        <item>
            <title><![CDATA[如何使用 Spark 快速将数据写入 Elasticsearch]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/spark-execute-elasticsearch</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/spark-execute-elasticsearch</guid>
            <pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[说到数据写入 Elasticsearch，最先想到的肯定是Logstash。Logstash因为其简单上手、可扩展、可伸缩等优点被广大用户接受。但是尺有所短，寸有所长，Logstash肯定也有它无法适用的应用场景，比如：]]></description>
            <content:encoded><![CDATA[<p>说到数据写入 Elasticsearch，最先想到的肯定是Logstash。Logstash因为其简单上手、可扩展、可伸缩等优点被广大用户接受。但是尺有所短，寸有所长，Logstash肯定也有它无法适用的应用场景，比如：</p><ul><li>海量数据ETL</li><li>海量数据聚合</li><li>多源数据处理</li></ul><p>为了满足这些场景，很多同学都会选择Spark，借助Spark算子进行数据处理，最后将处理结果写入Elasticsearch。</p><p>我们部门之前利用Spark对Nginx日志进行分析，统计我们的Web服务访问情况，将Nginx日志每分钟聚合一次最后将结果写入Elasticsearch，然后利用Kibana配置实时监控Dashboard。Elasticsearch和Kibana都很方便、实用，但是随着类似需求越来越多，如何快速通过Spark将数据写入Elasticsearch成为了我们的一大问题。</p><p>今天给大家推荐一款能够实现数据快速写入的黑科技 Seatunnel <a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-seatunnel</a> 一个非常易用，高性能，能够应对海量数据的实时数据处理产品，它构建在Spark之上，简单易用，灵活配置，无需开发。</p><p><img loading="lazy" src="/zh-CN/assets/images/wd-struct-fd963482dc80fdee6e4930107709bd28.png" width="818" height="466" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="kafka-to-elasticsearch">Kafka to Elasticsearch<a href="#kafka-to-elasticsearch" class="hash-link" aria-label="Kafka to Elasticsearch的直接链接" title="Kafka to Elasticsearch的直接链接">​</a></h2><p>和Logstash一样，Seatunnel同样支持多种类型的数据输入，这里我们以最常见的Kakfa作为输入源为例，讲解如何使用 Seatunnel 将数据快速写入Elasticsearch</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="log-sample">Log Sample<a href="#log-sample" class="hash-link" aria-label="Log Sample的直接链接" title="Log Sample的直接链接">​</a></h3><p>原始日志格式如下:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">127.0.0.1 elasticsearch.cn 114.250.140.241 0.001s "127.0.0.1:80" [26/Oct/2018:21:54:32 +0800] "GET /article HTTP/1.1" 200 123 "-" - "Dalvik/2.1.0 (Linux; U; Android 7.1.1; OPPO R11 Build/NMF26X)"</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="elasticsearch-document">Elasticsearch Document<a href="#elasticsearch-document" class="hash-link" aria-label="Elasticsearch Document的直接链接" title="Elasticsearch Document的直接链接">​</a></h3><p>我们想要统计，一分钟每个域名的访问情况，聚合完的数据有以下字段:</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">domain String</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">hostname String</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">status int</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">datetime String</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">count int</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-with-elasticsearch">Seatunnel with Elasticsearch<a href="#seatunnel-with-elasticsearch" class="hash-link" aria-label="Seatunnel with Elasticsearch的直接链接" title="Seatunnel with Elasticsearch的直接链接">​</a></h2><p>接下来会给大家详细介绍，我们如何通过 Seatunnel 读取Kafka中的数据，对数据进行解析以及聚合，最后将处理结果写入Elasticsearch中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel">Seatunnel<a href="#seatunnel" class="hash-link" aria-label="Seatunnel的直接链接" title="Seatunnel的直接链接">​</a></h3><p><a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">Seatunnel</a> 同样拥有着非常丰富的插件，支持从Kafka、HDFS、Hive中读取数据，进行各种各样的数据处理，并将结果写入Elasticsearch、Kudu或者Kafka中。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="prerequisites">Prerequisites<a href="#prerequisites" class="hash-link" aria-label="Prerequisites的直接链接" title="Prerequisites的直接链接">​</a></h3><p>首先我们需要安装seatunnel，安装十分简单，无需配置系统环境变量</p><ol><li>准备Spark环境</li><li>安装 Seatunnel</li><li>配置 Seatunnel</li></ol><p>以下是简易步骤，具体安装可以参照 <a href="/zh-CN/docs/quick-start">Quick Start</a></p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd /usr/local</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget https</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">//archive.apache.org/dist/spark/spark</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">2.2.0/spark</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">2.2.0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tar </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">xvf https</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">//archive.apache.org/dist/spark/spark</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">2.2.0/spark</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">2.2.0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget https</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">//github.com/InterestingLab/seatunnel/releases/download/v1.1.1/seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">1.1.1.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">unzip seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">1.1.1.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">1.1.1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">vim config/seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">env.sh</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 指定Spark安装路径</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SPARK_HOME=$</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">SPARK_HOME</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">/usr/local/spark</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">2.2.0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">hadoop2.7</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-pipeline">Seatunnel Pipeline<a href="#seatunnel-pipeline" class="hash-link" aria-label="Seatunnel Pipeline的直接链接" title="Seatunnel Pipeline的直接链接">​</a></h3><p>与Logstash一样，我们仅需要编写一个Seatunnel Pipeline的配置文件即可完成数据的导入，相信了解Logstash的朋友可以很快入手 Seatunnel 配置。</p><p>配置文件包括四个部分，分别是Spark、Input、filter和Output。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="spark">Spark<a href="#spark" class="hash-link" aria-label="Spark的直接链接" title="Spark的直接链接">​</a></h4><p>这一部分是Spark的相关配置，主要配置Spark执行时所需的资源大小。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.streaming.batchDuration = 5</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="input">Input<a href="#input" class="hash-link" aria-label="Input的直接链接" title="Input的直接链接">​</a></h4><p>这一部分定义数据源，如下是从Kafka中读取数据的配置案例，</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    topics = "seatunnel-es"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    consumer.group.id = "seatunnel_es_group"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    consumer.rebalance.max.retries = 100</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="filter">Filter<a href="#filter" class="hash-link" aria-label="Filter的直接链接" title="Filter的直接链接">​</a></h4><p>在Filter部分，这里我们配置一系列的转化，包括正则解析将日志进行拆分、时间转换将HTTPDATE转化为Elasticsearch支持的日期格式、对Number类型的字段进行类型转换以及通过SQL进行数据聚合</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 使用正则解析原始日志</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 最开始数据都在raw_message字段中</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    grok </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = "raw_message"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pattern = '%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NOTSPACE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">hostname</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NOTSPACE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">domain</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">IP</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">remote_addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NUMBER</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">request_time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">s\\s\"%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">DATA</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">upstream_ip</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\"\\s\\</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">HTTPDATE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">\\s\"%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NOTSPACE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">method</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">DATA</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NOTSPACE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">http_ver</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\"\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NUMBER</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">status</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NUMBER</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">body_bytes_send</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">DATA</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">referer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">NOTSPACE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">cookie_info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">\\s\"%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">DATA</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">user_agent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># 将"dd/MMM/yyyy:HH:mm:ss Z"格式的数据转换为</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)"># Elasticsearch中支持的格式</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    date </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = "timestamp"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_field = "datetime"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_time_format = "dd/MMM/yyyy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">HH</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">mm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">ss Z"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_time_format = "yyyy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">MM</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">dd'T'HH</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">mm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">ss.SSS+08</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">00"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">## 利用SQL对数据进行聚合</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select domain</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> hostname</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> int(status)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> count(</span><span class="token important">*)</span><span class="token plain"> from access_log group by domain</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> hostname</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> status</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> datetime"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="output">Output<a href="#output" class="hash-link" aria-label="Output的直接链接" title="Output的直接链接">​</a></h4><p>最后我们将处理好的结构化数据写入Elasticsearch。</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">output </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    elasticsearch </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        hosts = </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"localhost:9200"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        index = "seatunnel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">$</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">now</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        es.batch.size.entries = 100000</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        index_time_format = "yyyy.MM.dd"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="running-seatunnel">Running Seatunnel<a href="#running-seatunnel" class="hash-link" aria-label="Running Seatunnel的直接链接" title="Running Seatunnel的直接链接">​</a></h3><p>我们将上述四部分配置组合成为我们的配置文件 <code>config/batch.conf</code>。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">vim config/batch.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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.streaming.batchDuration = 5</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topics = "seatunnel-es"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.group.id = "seatunnel_es_group"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.rebalance.max.retries = 100</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    # 使用正则解析原始日志</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    # 最开始数据都在raw_message字段中</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    grok {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = "raw_message"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pattern = '%{IP:hostname}\\s%{NOTSPACE:domain}\\s%{IP:remote_addr}\\s%{NUMBER:request_time}s\\s\"%{DATA:upstream_ip}\"\\s\\[%{HTTPDATE:timestamp}\\]\\s\"%{NOTSPACE:method}\\s%{DATA:url}\\s%{NOTSPACE:http_ver}\"\\s%{NUMBER:status}\\s%{NUMBER:body_bytes_send}\\s%{DATA:referer}\\s%{NOTSPACE:cookie_info}\\s\"%{DATA:user_agent}'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">   }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    # 将"dd/MMM/yyyy:HH:mm:ss Z"格式的数据转换为</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    # Elasticsearch中支持的格式</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    date {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_field = "timestamp"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_field = "datetime"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        source_time_format = "dd/MMM/yyyy:HH:mm:ss Z"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        target_time_format = "yyyy-MM-dd'T'HH:mm:00.SSS+08:00"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    ## 利用SQL对数据进行聚合</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select domain, hostname, status, datetime, count(*) from access_log group by domain, hostname, status, datetime"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    elasticsearch {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        hosts = ["localhost:9200"]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        index = "seatunnel-${now}"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        es.batch.size.entries = 100000</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        index_time_format = "yyyy.MM.dd"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>执行命令，指定配置文件，运行 Seatunnel，即可将数据写入Elasticsearch。这里我们以本地模式为例。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">./bin/start-seatunnel.sh --config config/batch.conf -e client -m 'local[2]'</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>最后，写入Elasticsearch中的数据如下，再配上Kibana就可以实现Web服务的实时监控了^_^.</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">"_source": {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    "domain": "elasticsearch.cn",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    "hostname": "localhost",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    "status": "200",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    "datetime": "2018-11-26T21:54:00.000+08:00",</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    "count": 26</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="conclusion">Conclusion<a href="#conclusion" class="hash-link" aria-label="Conclusion的直接链接" title="Conclusion的直接链接">​</a></h2><p>在这篇文章中，我们介绍了如何通过 Seatunnel 将Kafka中的数据写入Elasticsearch中。仅仅通过一个配置文件便可快速运行一个Spark Application，完成数据的处理、写入，无需编写任何代码，十分简单。</p><p>当数据处理过程中有遇到Logstash无法支持的场景或者Logstah性能无法达到预期的情况下，都可以尝试使用 Seatunnel 解决问题。</p><p>希望了解 Seatunnel 与Elasticsearch、Kafka、Hadoop结合使用的更多功能和案例，可以直接进入官网 <a href="https://seatunnel.apache.org/" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/</a></p><p><strong>我们近期会再发布一篇《如何用Spark和Elasticsearch做交互式数据分析》，敬请期待.</strong></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="contract-us">Contract us<a href="#contract-us" class="hash-link" aria-label="Contract us的直接链接" title="Contract us的直接链接">​</a></h2><ul><li>邮件列表 : <strong><a href="mailto:dev@seatunnel.apache.org" target="_blank" rel="noopener noreferrer">dev@seatunnel.apache.org</a></strong>. 发送任意内容至 <code>dev-subscribe@seatunnel.apache.org</code>， 按照回复订阅邮件列表。</li><li>Slack: 发送 <code>Request to join SeaTunnel slack</code> 邮件到邮件列表 (<code>dev@seatunnel.apache.org</code>), 我们会邀请你加入（在此之前请确认已经注册Slack）.</li><li><a href="https://space.bilibili.com/1542095008" target="_blank" rel="noopener noreferrer">bilibili B站 视频</a></li></ul>]]></content:encoded>
            <category>Spark</category>
            <category>Kafka</category>
            <category>Elasticsearch</category>
        </item>
        <item>
            <title><![CDATA[怎么用 Spark 在 TiDB 上做 OLAP 分析]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/spark-execute-tidb</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/spark-execute-tidb</guid>
            <pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[TiDB 是一款定位于在线事务处理/在线分析处理的融合型数据库产品，实现了一键水平伸缩，强一致性的多副本数据安全，分布式事务，实时 OLAP 等重要特性。]]></description>
            <content:encoded><![CDATA[<p><img loading="lazy" src="https://download.pingcap.com/images/tidb-planet.jpg" class="img_ev3q"></p><p><a href="https://github.com/pingcap/tidb" target="_blank" rel="noopener noreferrer">TiDB</a> 是一款定位于在线事务处理/在线分析处理的融合型数据库产品，实现了一键水平伸缩，强一致性的多副本数据安全，分布式事务，实时 OLAP 等重要特性。</p><p>TiSpark 是 PingCAP 为解决用户复杂 OLAP 需求而推出的产品。它借助 Spark 平台，同时融合 TiKV 分布式集群的优势。</p><p>直接使用 TiSpark 完成 OLAP 操作需要了解 Spark，还需要一些开发工作。那么，有没有一些开箱即用的工具能帮我们更快速地使用 TiSpark 在 TiDB 上完成 OLAP 分析呢？</p><p>目前开源社区上有一款工具 <strong>Seatunnel</strong>，项目地址 <a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-seatunnel</a> ，可以基于Spark，在 TiSpark 的基础上快速实现 TiDB 数据读取和 OLAP 分析。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="使用-seatunnel-操作tidb">使用 Seatunnel 操作TiDB<a href="#使用-seatunnel-操作tidb" class="hash-link" aria-label="使用 Seatunnel 操作TiDB的直接链接" title="使用 Seatunnel 操作TiDB的直接链接">​</a></h2><p>在我们线上有这么一个需求，从 TiDB 中读取某一天的网站访问数据，统计每个域名以及服务返回状态码的访问次数，最后将统计结果写入 TiDB 另外一个表中。 我们来看看 Seatunnel 是如何实现这么一个功能的。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel">Seatunnel<a href="#seatunnel" class="hash-link" aria-label="Seatunnel的直接链接" title="Seatunnel的直接链接">​</a></h3><p><a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">Seatunnel</a> 是一个非常易用，高性能，能够应对海量数据的实时数据处理产品，它构建在 Spark 之上。Seatunnel 拥有着非常丰富的插件，支持从 TiDB、Kafka、HDFS、Kudu 中读取数据，进行各种各样的数据处理，然后将结果写入 TiDB、ClickHouse、Elasticsearch 或者 Kafka 中。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="准备工作">准备工作<a href="#准备工作" class="hash-link" aria-label="准备工作的直接链接" title="准备工作的直接链接">​</a></h4><h5 class="anchor anchorWithStickyNavbar_LWe7" id="1-tidb-表结构介绍">1. TiDB 表结构介绍<a href="#1-tidb-表结构介绍" class="hash-link" aria-label="1. TiDB 表结构介绍的直接链接" title="1. TiDB 表结构介绍的直接链接">​</a></h5><p><strong>Input</strong>（存储访问日志的表）</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">CREATE TABLE access_log (</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    domain VARCHAR(255),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    datetime VARCHAR(63),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    remote_addr VARCHAR(63),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    http_ver VARCHAR(15),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    body_bytes_send INT,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    status INT,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    request_time FLOAT,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    url TEXT</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">+-----------------+--------------+------+------+---------+-------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| Field           | Type         | Null | Key  | Default | Extra |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">+-----------------+--------------+------+------+---------+-------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| domain          | varchar(255) | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| datetime        | varchar(63)  | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| remote_addr     | varchar(63)  | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| http_ver        | varchar(15)  | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| body_bytes_send | int(11)      | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| status          | int(11)      | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| request_time    | float        | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| url             | text         | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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>Output</strong>（存储结果数据的表）</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">CREATE TABLE access_collect (</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    date VARCHAR(23),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    domain VARCHAR(63),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    status INT,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    hit INT</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">+--------+-------------+------+------+---------+-------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| Field  | Type        | Null | Key  | Default | Extra |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">+--------+-------------+------+------+---------+-------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| date   | varchar(23) | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| domain | varchar(63) | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| status | int(11)     | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| hit    | int(11)     | YES  |      | NULL    |       |</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="2-安装-seatunnel">2. 安装 Seatunnel<a href="#2-安装-seatunnel" class="hash-link" aria-label="2. 安装 Seatunnel的直接链接" title="2. 安装 Seatunnel的直接链接">​</a></h5><p>有了 TiDB 输入和输出表之后， 我们需要安装 Seatunnel，安装十分简单，无需配置系统环境变量</p><ol><li>准备 Spark环境</li><li>安装 Seatunnel</li><li>配置 Seatunnel</li></ol><p>以下是简易步骤，具体安装可以参照 <a href="/zh-CN/docs/quick-start">Quick Start</a></p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain"># 下载安装Spark</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd /usr/local</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget https://archive.apache.org/dist/spark/spark-2.1.0/spark-2.1.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tar -xvf https://archive.apache.org/dist/spark/spark-2.1.0/spark-2.1.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># 下载安装seatunnel</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">https://github.com/InterestingLab/seatunnel/releases/download/v1.2.0/seatunnel-1.2.0.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">unzip seatunnel-1.2.0.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd seatunnel-1.2.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">vim config/seatunnel-env.sh</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># 指定Spark安装路径</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SPARK_HOME=${SPARK_HOME:-/usr/local/spark-2.1.0-bin-hadoop2.7}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="实现-seatunnel-处理流程">实现 Seatunnel 处理流程<a href="#实现-seatunnel-处理流程" class="hash-link" aria-label="实现 Seatunnel 处理流程的直接链接" title="实现 Seatunnel 处理流程的直接链接">​</a></h3><p>我们仅需要编写一个 Seatunnel 配置文件即可完成数据的读取、处理、写入。</p><p>Seatunnel 配置文件由四个部分组成，分别是 <code>Spark</code>、<code>Input</code>、<code>Filter</code> 和 <code>Output</code>。<code>Input</code> 部分用于指定数据的输入源，<code>Filter</code> 部分用于定义各种各样的数据处理、聚合，<code>Output</code> 部分负责将处理之后的数据写入指定的数据库或者消息队列。</p><p>整个处理流程为 <code>Input</code> -&gt; <code>Filter</code> -&gt; <code>Output</code>，整个流程组成了 Seatunnel 的 处理流程（Pipeline）。</p><blockquote><p>以下是一个具体配置，此配置来源于线上实际应用，但是为了演示有所简化。</p></blockquote><h5 class="anchor anchorWithStickyNavbar_LWe7" id="input-tidb">Input (TiDB)<a href="#input-tidb" class="hash-link" aria-label="Input (TiDB)的直接链接" title="Input (TiDB)的直接链接">​</a></h5><p>这里部分配置定义输入源，如下是从 TiDB 一张表中读取数据。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    tidb {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database = "nginx"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pre_sql = "select * from nginx.access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "spark_nginx_input"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="filter">Filter<a href="#filter" class="hash-link" aria-label="Filter的直接链接" title="Filter的直接链接">​</a></h5><p>在Filter部分，这里我们配置一系列的转化, 大部分数据分析的需求，都是在Filter完成的。Seatunnel 提供了丰富的插件，足以满足各种数据分析需求。这里我们通过 SQL 插件完成数据的聚合操作。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "spark_nginx_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select count(*) as hit, domain, status, substring(datetime, 1, 10) as date from spark_nginx_log where substring(datetime, 1, 10)='2019-01-20' group by domain, status, substring(datetime, 1, 10)"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="output-tidb">Output (TiDB)<a href="#output-tidb" class="hash-link" aria-label="Output (TiDB)的直接链接" title="Output (TiDB)的直接链接">​</a></h5><p>最后， 我们将处理后的结果写入TiDB另外一张表中。TiDB Output是通过JDBC实现的</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    tidb {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        url = "jdbc:mysql://127.0.0.1:4000/nginx?useUnicode=true&amp;characterEncoding=utf8"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table = "access_collect"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        user = "username"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password = "password"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        save_mode = "append"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="spark">Spark<a href="#spark" class="hash-link" aria-label="Spark的直接链接" title="Spark的直接链接">​</a></h5><p>这一部分是 Spark 的相关配置，主要配置 Spark 执行时所需的资源大小以及其他 Spark 配置。</p><p>我们的 TiDB Input 插件是基于 TiSpark 实现的，而 TiSpark 依赖于 TiKV 集群和 Placement Driver (PD)。因此我们需要指定 PD 节点信息以及 TiSpark 相关配置<code>spark.tispark.pd.addresses</code>和<code>spark.sql.extensions</code>。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel-tidb"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  # Set for TiSpark</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.tispark.pd.addresses = "localhost:2379"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.sql.extensions = "org.apache.spark.sql.TiExtensions"</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="运行-seatunnel">运行 Seatunnel<a href="#运行-seatunnel" class="hash-link" aria-label="运行 Seatunnel的直接链接" title="运行 Seatunnel的直接链接">​</a></h4><p>我们将上述四部分配置组合成我们最终的配置文件 <code>conf/tidb.conf</code></p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.app.name = "seatunnel-tidb"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    # Set for TiSpark</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.tispark.pd.addresses = "localhost:2379"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    spark.sql.extensions = "org.apache.spark.sql.TiExtensions"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    tidb {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        database = "nginx"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        pre_sql = "select * from nginx.access_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "spark_table"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "spark_nginx_log"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select count(*) as hit, domain, status, substring(datetime, 1, 10) as date from spark_nginx_log where substring(datetime, 1, 10)='2019-01-20' group by domain, status, substring(datetime, 1, 10)"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    tidb {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        url = "jdbc:mysql://127.0.0.1:4000/nginx?useUnicode=true&amp;characterEncoding=utf8"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table = "access_collect"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        user = "username"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        password = "password"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        save_mode = "append"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>执行命令，指定配置文件，运行 Seatunnel ，即可实现我们的数据处理逻辑。</p><ul><li>Local</li></ul><blockquote><p>./bin/start-seatunnel.sh --config config/tidb.conf --deploy-mode client --master 'local<!-- -->[2]<!-- -->'</p></blockquote><ul><li>yarn-client</li></ul><blockquote><p>./bin/start-seatunnel.sh --config config/tidb.conf --deploy-mode client --master yarn</p></blockquote><ul><li>yarn-cluster</li></ul><blockquote><p>./bin/start-seatunnel.sh --config config/tidb.conf --deploy-mode cluster -master yarn</p></blockquote><p>如果是本机测试验证逻辑，用本地模式（Local）就可以了，一般生产环境下，都是使用<code>yarn-client</code>或者<code>yarn-cluster</code>模式。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="检查结果">检查结果<a href="#检查结果" class="hash-link" aria-label="检查结果的直接链接" title="检查结果的直接链接">​</a></h4><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">mysql&gt; select * from access_collect;</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">+------------+--------+--------+------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| date       | domain | status | hit  |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">+------------+--------+--------+------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| 2019-01-20 | b.com  |    200 |   63 |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">| 2019-01-20 | a.com  |    200 |   85 |</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">+------------+--------+--------+------+</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">2 rows in set (0.21 sec)</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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 href="#总结" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接">​</a></h2><p>在这篇文章中，我们介绍了如何使用 Seatunnel 从 TiDB 中读取数据，做简单的数据处理之后写入 TiDB 另外一个表中。仅通过一个配置文件便可快速完成数据的导入，无需编写任何代码。</p><p>除了支持 TiDB 数据源之外，Seatunnel 同样支持Elasticsearch, Kafka, Kudu, ClickHouse等数据源。</p><p><strong>于此同时，我们正在研发一个重要功能，就是在 Seatunnel 中，利用 TiDB 的事务特性，实现从 Kafka 到 TiDB 流式数据处理，并且支持端（Kafka）到端（TiDB）的 Exactly-Once 数据一致性。</strong></p><p>希望了解 Seatunnel 和 TiDB，ClickHouse、Elasticsearch、Kafka结合使用的更多功能和案例，可以直接进入官网 <a href="https://seatunnel.apache.org/" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="联系我们">联系我们<a href="#联系我们" class="hash-link" aria-label="联系我们的直接链接" title="联系我们的直接链接">​</a></h2><ul><li>邮件列表 : <strong><a href="mailto:dev@seatunnel.apache.org" target="_blank" rel="noopener noreferrer">dev@seatunnel.apache.org</a></strong>. 发送任意内容至 <code>dev-subscribe@seatunnel.apache.org</code>， 按照回复订阅邮件列表。</li><li>Slack: 发送 <code>Request to join SeaTunnel slack</code> 邮件到邮件列表 (<code>dev@seatunnel.apache.org</code>), 我们会邀请你加入（在此之前请确认已经注册Slack）.</li><li><a href="https://space.bilibili.com/1542095008" target="_blank" rel="noopener noreferrer">bilibili B站 视频</a></li></ul><p>-- Power by <a href="https://github.com/InterestingLab" target="_blank" rel="noopener noreferrer">InterestingLab</a></p>]]></content:encoded>
            <category>Spark</category>
            <category>TiDB</category>
        </item>
        <item>
            <title><![CDATA[如何支持的 Spark StructuredStreaming]]></title>
            <link>https://seatunnel.apache.org/zh-CN/blog/spark-structured-streaming</link>
            <guid>https://seatunnel.apache.org/zh-CN/blog/spark-structured-streaming</guid>
            <pubDate>Thu, 30 Dec 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[前言]]></description>
            <content:encoded><![CDATA[<h3 class="anchor anchorWithStickyNavbar_LWe7" id="前言">前言<a href="#前言" class="hash-link" aria-label="前言的直接链接" title="前言的直接链接">​</a></h3><p>StructuredStreaming是Spark 2.0以后新开放的一个模块，相比SparkStreaming，它有一些比较突出的优点：<br> <!-- --> <!-- --> <!-- -->一、它能做到更低的延迟;<br>
<!-- --> <!-- --> <!-- -->二、可以做实时的聚合，例如实时计算每天每个商品的销售总额；<br>
<!-- --> <!-- --> <!-- -->三、可以做流与流之间的关联，例如计算广告的点击率，需要将广告的曝光记录和点击记录关联。<br>
以上几点如果使用SparkStreaming来实现可能会比较麻烦或者说是很难实现，但是使用StructuredStreaming实现起来会比较轻松。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="如何使用structuredstreaming">如何使用StructuredStreaming<a href="#如何使用structuredstreaming" class="hash-link" aria-label="如何使用StructuredStreaming的直接链接" title="如何使用StructuredStreaming的直接链接">​</a></h3><p>可能你没有详细研究过StructuredStreaming，但是发现StructuredStreaming能很好的解决你的需求，如何快速利用StructuredStreaming来解决你的需求？目前社区有一款工具 <strong>Seatunnel</strong>，项目地址：<a href="https://github.com/apache/incubator-seatunnel" target="_blank" rel="noopener noreferrer">https://github.com/apache/incubator-seatunnel</a> ,
可以高效低成本的帮助你利用StructuredStreaming来完成你的需求。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seatunnel">Seatunnel<a href="#seatunnel" class="hash-link" aria-label="Seatunnel的直接链接" title="Seatunnel的直接链接">​</a></h3><p>Seatunnel 是一个非常易用，高性能，能够应对海量数据的实时数据处理产品，它构建在Spark之上。Seatunnel 拥有着非常丰富的插件，支持从Kafka、HDFS、Kudu中读取数据，进行各种各样的数据处理，并将结果写入ClickHouse、Elasticsearch或者Kafka中</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="准备工作">准备工作<a href="#准备工作" class="hash-link" aria-label="准备工作的直接链接" title="准备工作的直接链接">​</a></h3><p>首先我们需要安装 Seatunnel，安装十分简单，无需配置系统环境变量</p><ol><li>准备Spark环境</li><li>安装 Seatunnel</li><li>配置 Seatunnel</li></ol><p>以下是简易步骤，具体安装可以参照 <a href="/zh-CN/docs/quick-start">Quick Start</a></p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">cd /usr/local</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget https://archive.apache.org/dist/spark/spark-2.2.0/spark-2.2.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">tar -xvf https://archive.apache.org/dist/spark/spark-2.2.0/spark-2.2.0-bin-hadoop2.7.tgz</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">wget https://github.com/InterestingLab/seatunnel/releases/download/v1.3.0/seatunnel-1.3.0.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">unzip seatunnel-1.3.0.zip</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">cd seatunnel-1.3.0</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">vim config/seatunnel-env.sh</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"># 指定Spark安装路径</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">SPARK_HOME=${SPARK_HOME:-/usr/local/spark-2.2.0-bin-hadoop2.7}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="seatunnel-pipeline">Seatunnel Pipeline<a href="#seatunnel-pipeline" class="hash-link" aria-label="Seatunnel Pipeline的直接链接" title="Seatunnel Pipeline的直接链接">​</a></h3><p>我们仅需要编写一个 Seatunnel Pipeline的配置文件即可完成数据的导入。</p><p>配置文件包括四个部分，分别是Spark、Input、filter和Output。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="spark">Spark<a href="#spark" class="hash-link" aria-label="Spark的直接链接" title="Spark的直接链接">​</a></h4><p>这一部分是Spark的相关配置，主要配置Spark执行时所需的资源大小。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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="input">Input<a href="#input" class="hash-link" aria-label="Input的直接链接" title="Input的直接链接">​</a></h4><p>下面是一个从kafka读取数据的例子</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    topics = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    schema = "{\"name\":\"string\",\"age\":\"integer\",\"addrs\":{\"country\":\"string\",\"city\":\"string\"}}"</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>通过上面的配置就可以读取kafka里的数据了 ，topics是要订阅的kafka的topic，同时订阅多个topic可以以逗号隔开，consumer.bootstrap.servers就是Kafka的服务器列表，schema是可选项，因为StructuredStreaming从kafka读取到的值(官方固定字段value)是binary类型的，详见<a href="http://spark.apache.org/docs/latest/structured-streaming-kafka-integration.html" target="_blank" rel="noopener noreferrer">http://spark.apache.org/docs/latest/structured-streaming-kafka-integration.html</a>
但是如果你确定你kafka里的数据是json字符串的话，你可以指定schema，input插件将按照你指定的schema解析</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="filter">Filter<a href="#filter" class="hash-link" aria-label="Filter的直接链接" title="Filter的直接链接">​</a></h4><p>下面是一个简单的filter例子</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">filter{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "student"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select name,age from student"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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>table_name</code>是注册成的临时表名，以便于在下面的sql使用</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="output">Output<a href="#output" class="hash-link" aria-label="Output的直接链接" title="Output的直接链接">​</a></h4><p>处理好的数据往外输出，假设我们的输出也是kafka</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">output{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafka {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topic = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        producer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        streaming_output_mode = "update"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        checkpointLocation = "/your/path"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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>topic</code> 是你要输出的topic，<code> producer.bootstrap.servers</code>是kafka集群列表，<code>streaming_output_mode</code>是StructuredStreaming的一个输出模式参数，有三种类型<code>append|update|complete</code>，具体使用参见文档<a href="http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#output-modes" target="_blank" rel="noopener noreferrer">http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#output-modes</a></p><p><code>checkpointLocation</code>是StructuredStreaming的checkpoint路径，如果配置了的话，这个目录会存储程序的运行信息，比如程序退出再启动的话会接着上次的offset进行消费。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="场景分析">场景分析<a href="#场景分析" class="hash-link" aria-label="场景分析的直接链接" title="场景分析的直接链接">​</a></h3><p>以上就是一个简单的例子，接下来我们就来介绍的稍微复杂一些的业务场景</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="场景一实时聚合场景">场景一：实时聚合场景<a href="#场景一实时聚合场景" class="hash-link" aria-label="场景一：实时聚合场景的直接链接" title="场景一：实时聚合场景的直接链接">​</a></h4><p>假设现在有一个商城，上面有10种商品，现在需要实时求每天每种商品的销售额，甚至是求每种商品的购买人数（不要求十分精确）。
这么做的巨大的优势就是海量数据可以在实时处理的时候，完成聚合，再也不需要先将数据写入数据仓库，再跑离线的定时任务进行聚合，
操作起来还是很方便的。</p><p>kafka的数据如下</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">{"good_id":"abc","price":300,"user_id":123456,"time":1553216320}</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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>那我们该怎么利用 Seatunnel 来完成这个需求呢，当然还是只需要配置就好了。</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">#spark里的配置根据业务需求配置</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">#配置input</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topics = "good_topic"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        schema = "{\"good_id\":\"string\",\"price\":\"integer\",\"user_id\":\"Long\",\"time\":\"Long\"}"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">#配置filter    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #在程序做聚合的时候，内部会去存储程序从启动开始的聚合状态，久而久之会导致OOM,如果设置了watermark，程序自动的会去清理watermark之外的状态</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #这里表示使用ts字段设置watermark，界限为1天</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    Watermark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        time_field = "time"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        time_type = "UNIX"              #UNIX表示时间字段为10为的时间戳，还有其他的类型详细可以查看插件文档</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        time_pattern = "yyyy-MM-dd"     #这里之所以要把ts对其到天是因为求每天的销售额，如果是求每小时的销售额可以对其到小时`yyyy-MM-dd HH`</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        delay_threshold = "1 day"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        watermark_field = "ts"          #设置watermark之后会新增一个字段，`ts`就是这个字段的名字</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #之所以要group by ts是要让watermark生效，approx_count_distinct是一个估值，并不是精确的count_distinct</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "good_table_2"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select good_id,sum(price) total, approx_count_distinct(user_id) person from good_table_2 group by ts,good_id"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">#接下来我们选择将结果实时输出到Kafka</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output{</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafka {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topic = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        producer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        streaming_output_mode = "update"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        checkpointLocation = "/your/path"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>如上配置完成，启动 Seatunnel，就可以获取你想要的结果了。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="场景二多个流关联场景">场景二：多个流关联场景<a href="#场景二多个流关联场景" class="hash-link" aria-label="场景二：多个流关联场景的直接链接" title="场景二：多个流关联场景的直接链接">​</a></h4><p>假设你在某个平台投放了广告，现在要实时计算出每个广告的CTR(点击率)，数据分别来自两个topic，一个是广告曝光日志，一个是广告点击日志,
此时我们就需要把两个流数据关联到一起做计算，而 Seatunnel 最近也支持了此功能，让我们一起看一下该怎么做：</p><p>点击topic数据格式</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">{"ad_id":"abc","click_time":1553216320,"user_id":12345}</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>曝光topic数据格式</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">{"ad_id":"abc","show_time":1553216220,"user_id":12345}</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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:#F8F8F2;--prism-background-color:#282A36"><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:#F8F8F2"><span class="token plain">#spark里的配置根据业务需求配置</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">spark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.app.name = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.instances = 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.cores = 1</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">  spark.executor.memory = "1g"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">#配置input</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">input {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topics = "click_topic"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        schema = "{\"ad_id\":\"string\",\"user_id\":\"Long\",\"click_time\":\"Long\"}"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "click_table"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafkaStream {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topics = "show_topic"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        consumer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        schema = "{\"ad_id\":\"string\",\"user_id\":\"Long\",\"show_time\":\"Long\"}"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "show_table"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">filter {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #左关联右表必须设置watermark</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #右关左右表必须设置watermark</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    #http://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#inner-joins-with-optional-watermarking</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    Watermark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              source_table_name = "click_table" #这里可以指定为某个临时表添加watermark，不指定的话就是为input中的第一个</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              time_field = "time"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              time_type = "UNIX"               </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              delay_threshold = "3 hours"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              watermark_field = "ts" </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">              result_table_name = "click_table_watermark" #添加完watermark之后可以注册成临时表，方便后续在sql中使用</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    Watermark {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                source_table_name = "show_table" </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                time_field = "time"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                time_type = "UNIX"               </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                delay_threshold = "2 hours"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                watermark_field = "ts" </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                result_table_name = "show_table_watermark" </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">     }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sql {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        table_name = "show_table_watermark"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        sql = "select a.ad_id,count(b.user_id)/count(a.user_id) ctr from show_table_watermark as a left join click_table_watermark as b on a.ad_id = b.ad_id and a.user_id = b.user_id "</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">#接下来我们选择将结果实时输出到Kafka</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">output {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    kafka {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        topic = "seatunnel"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        producer.bootstrap.servers = "localhost:9092"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        streaming_output_mode = "append" #流关联只支持append模式</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        checkpointLocation = "/your/path"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#F8F8F2"><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 viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" 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 viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" 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><h3 class="anchor anchorWithStickyNavbar_LWe7" id="结语">结语<a href="#结语" class="hash-link" aria-label="结语的直接链接" title="结语的直接链接">​</a></h3><p>通过配置能很快的利用StructuredStreaming做实时数据处理，但是还是需要对StructuredStreaming的一些概念了解，比如其中的watermark机制，还有程序的输出模式。</p><p>最后，Seatunnel 当然还支持spark streaming和spark 批处理。
如果你对这两个也感兴趣的话，可以阅读我们以前发布的文章《<a href="/zh-CN/blog/i18n/zh-CN/docusaurus-plugin-content-blog/current/2021-12-30-hive-to-clickhouse.mdtent-blog/current/2021-12-30-hive-to-clickhouse.md">如何快速地将Hive中的数据导入ClickHouse</a>》、
《<a href="/zh-CN/blog/i18n/zh-CN/docusaurus-plugin-content-blog/current/2021-12-30-spark-execute-tidb.mdtent-blog/current/2021-12-30-spark-execute-tidb.md">优秀的数据工程师，怎么用Spark在TiDB上做OLAP分析</a>》、
《<a href="/zh-CN/blog/i18n/zh-CN/docusaurus-plugin-content-blog/2021-12-30-spark-execute-elasticsearch.md/current/2021-12-30-spark-execute-elasticsearch.md">如何使用Spark快速将数据写入Elasticsearch</a>》</p><p>希望了解 Seatunnel 和 HBase, ClickHouse、Elasticsearch、Kafka、MySQL 等数据源结合使用的更多功能和案例，可以直接进入官网 <a href="https://seatunnel.apache.org/" target="_blank" rel="noopener noreferrer">https://seatunnel.apache.org/</a></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="联系我们">联系我们<a href="#联系我们" class="hash-link" aria-label="联系我们的直接链接" title="联系我们的直接链接">​</a></h2><ul><li>邮件列表 : <strong><a href="mailto:dev@seatunnel.apache.org" target="_blank" rel="noopener noreferrer">dev@seatunnel.apache.org</a></strong>. 发送任意内容至 <code>dev-subscribe@seatunnel.apache.org</code>， 按照回复订阅邮件列表。</li><li>Slack: 发送 <code>Request to join SeaTunnel slack</code> 邮件到邮件列表 (<code>dev@seatunnel.apache.org</code>), 我们会邀请你加入（在此之前请确认已经注册Slack）.</li><li><a href="https://space.bilibili.com/1542095008" target="_blank" rel="noopener noreferrer">bilibili B站 视频</a></li></ul>]]></content:encoded>
            <category>Spark</category>
            <category>StructuredStreaming</category>
        </item>
    </channel>
</rss>