blob: e57377a6f6ce9bff9f17f47cb42c1d13d387d716 [file] [log] [blame]
<!doctype html>
<html lang="zh-CN" dir="ltr" class="blog-wrapper blog-post-page plugin-blog plugin-id-default">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v2.3.1">
<title data-rh="true">InLong Sort ETL 方案解析 | Apache InLong</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://inlong.apache.org/zh-CN/blog/2022/06/16/inlong-sort-etl"><meta data-rh="true" name="docusaurus_locale" content="zh-CN"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="zh-CN"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="InLong Sort ETL 方案解析 | Apache InLong"><meta data-rh="true" name="description" content="1. 背景"><meta data-rh="true" property="og:description" content="1. 背景"><meta data-rh="true" property="og:type" content="article"><meta data-rh="true" property="article:published_time" content="2022-06-16T00:00:00.000Z"><meta data-rh="true" property="article:author" content="https://github.com/Oneal65"><meta data-rh="true" property="article:tag" content="Apache InLong,Sort"><link data-rh="true" rel="icon" href="/zh-CN/img/logo.svg"><link data-rh="true" rel="canonical" href="https://inlong.apache.org/zh-CN/blog/2022/06/16/inlong-sort-etl"><link data-rh="true" rel="alternate" href="https://inlong.apache.org/blog/2022/06/16/inlong-sort-etl" hreflang="en"><link data-rh="true" rel="alternate" href="https://inlong.apache.org/zh-CN/blog/2022/06/16/inlong-sort-etl" hreflang="zh-CN"><link data-rh="true" rel="alternate" href="https://inlong.apache.org/blog/2022/06/16/inlong-sort-etl" hreflang="x-default"><link data-rh="true" rel="preconnect" href="https://YUW9QEL53E-dsn.algolia.net" crossorigin="anonymous"><link rel="alternate" type="application/rss+xml" href="/zh-CN/blog/rss.xml" title="Apache InLong RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/zh-CN/blog/atom.xml" title="Apache InLong Atom Feed">
<link rel="search" type="application/opensearchdescription+xml" title="Apache InLong" href="/zh-CN/opensearch.xml">
<script src="https://www.apachecon.com/event-images/snippet.js" async></script><link rel="stylesheet" href="/zh-CN/assets/css/styles.09deabdb.css">
<link rel="preload" href="/zh-CN/assets/js/runtime~main.933f32cb.js" as="script">
<link rel="preload" href="/zh-CN/assets/js/main.32851df5.js" as="script">
</head>
<body class="navigation-with-keyboard">
<script>!function(){function e(e){document.documentElement.setAttribute("data-theme",e)}var t=function(){var e=null;try{e=localStorage.getItem("theme")}catch(e){}return e}();null!==t?e(t):window.matchMedia("(prefers-color-scheme: dark)").matches?e("dark"):(window.matchMedia("(prefers-color-scheme: light)").matches,e("light"))}()</script><div id="__docusaurus">
<div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="主导航" class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="切换导航栏" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/zh-CN/"><div class="navbar__logo"><img src="/zh-CN/img/logo.svg" alt="Apache" class="themedImage_ToTc themedImage--light_HNdA"><img src="/zh-CN/img/logo.svg" alt="Apache" class="themedImage_ToTc themedImage--dark_i4oU"></div><b class="navbar__title text--truncate">Apache InLong</b></a></div><div class="navbar__items navbar__items--right"><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a class="navbar__link" aria-haspopup="true" aria-expanded="false" role="button" href="/zh-CN/docs/introduction">文档</a><ul class="dropdown__menu"><li><a class="dropdown__link" href="/zh-CN/docs/next/introduction">Next</a></li><li><a class="dropdown__link" href="/zh-CN/docs/introduction">1.11.0</a></li><li><a class="dropdown__link" href="/zh-CN/docs/1.10.0/introduction">1.10.0</a></li><li><a class="dropdown__link" href="/zh-CN/docs/1.9.0/introduction">1.9.0</a></li><li><a class="dropdown__link" href="/zh-CN/docs/1.8.0/introduction">1.8.0</a></li><li><a class="dropdown__link" href="/zh-CN/versions/">All versions</a></li></ul></div><a class="navbar__item navbar__link" href="/zh-CN/downloads">下载</a><a class="navbar__item navbar__link" href="/zh-CN/community/how-to-contribute">社区</a><a aria-current="page" class="navbar__item navbar__link navbar__link--active" href="/zh-CN/blog">博客</a><a class="navbar__item navbar__link" href="/zh-CN/team">团队</a><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a href="#" aria-haspopup="true" aria-expanded="false" role="button" class="navbar__link">Apache</a><ul class="dropdown__menu"><li><a href="https://www.apache.org/" target="_blank" rel="noopener noreferrer" class="dropdown__link">Apache Software Foundation</a></li><li><a href="https://www.apache.org/licenses/" target="_blank" rel="noopener noreferrer" class="dropdown__link">证书</a></li><li><a href="https://www.apache.org/events/current-event" target="_blank" rel="noopener noreferrer" class="dropdown__link">事件</a></li><li><a href="https://www.apache.org/security/" target="_blank" rel="noopener noreferrer" class="dropdown__link">安全</a></li><li><a href="https://www.apache.org/foundation/sponsorship.html" target="_blank" rel="noopener noreferrer" class="dropdown__link">赞助</a></li><li><a href="https://www.apache.org/foundation/policies/privacy.html" target="_blank" rel="noopener noreferrer" class="dropdown__link">Privacy</a></li><li><a href="https://www.apache.org/foundation/thanks.html" target="_blank" rel="noopener noreferrer" class="dropdown__link">致谢</a></li></ul></div><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a href="#" aria-haspopup="true" aria-expanded="false" role="button" class="navbar__link"><svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" class="iconLanguage_nlXk"><path fill="currentColor" d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"></path></svg>简体中文</a><ul class="dropdown__menu"><li><a href="/blog/2022/06/16/inlong-sort-etl" target="_self" rel="noopener noreferrer" class="dropdown__link" lang="en">English</a></li><li><a href="/zh-CN/blog/2022/06/16/inlong-sort-etl" target="_self" rel="noopener noreferrer" class="dropdown__link dropdown__link--active" lang="zh-CN">简体中文</a></li></ul></div><a href="https://github.com/apache/inlong" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="切换浅色/暗黑模式(当前为浅色模式)" aria-label="切换浅色/暗黑模式(当前为浅色模式)" aria-live="polite"><svg viewBox="0 0 24 24" width="24" height="24" class="lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" class="darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg></button></div><div class="searchBox_ZlJk"><button type="button" class="DocSearch DocSearch-Button" aria-label="搜索"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">搜索</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0"><div class="container margin-vert--lg"><div class="row"><aside class="col col--3"><nav class="sidebar_re4s thin-scrollbar" aria-label="Blog recent posts navigation"><div class="sidebarItemTitle_pO2u margin-bottom--md">Recent posts</div><ul class="sidebarItemList_Yudw clean-list"><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/zh-CN/blog/2023/12/13/release-1.10.0">1.10.0 版本发布</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/zh-CN/blog/2023/09/25/release-1.9.0">1.9.0 版本发布</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/zh-CN/blog/2023/07/24/release-1.8.0">1.8.0 版本发布</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/zh-CN/blog/2023/05/19/release-1.7.0">1.7.0 版本发布</a></li><li class="sidebarItem__DBe"><a class="sidebarItemLink_mo7H" href="/zh-CN/blog/2023/03/23/release-1.6.0">1.6.0 版本发布</a></li></ul></nav></aside><main class="col col--7" itemscope="" itemtype="http://schema.org/Blog"><article itemprop="blogPost" itemscope="" itemtype="http://schema.org/BlogPosting"><header><h1 class="title_f1Hy" itemprop="headline">InLong Sort ETL 方案解析</h1><div class="container_mt6G margin-vert--md"><time datetime="2022-06-16T00:00:00.000Z" itemprop="datePublished">2022年6月16日</time> · <!-- -->One min read</div><div class="margin-top--md margin-bottom--sm row"><div class="col col--6 authorCol_Hf19"><div class="avatar margin-bottom--sm"><a href="https://github.com/Oneal65" target="_blank" rel="noopener noreferrer" class="avatar__photo-link"><img class="avatar__photo" src="https://avatars.githubusercontent.com/u/13121552?v=4" alt="Oneal65"></a><div class="avatar__intro" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><div class="avatar__name"><a href="https://github.com/Oneal65" target="_blank" rel="noopener noreferrer" itemprop="url"><span itemprop="name">Oneal65</span></a></div></div></div></div></div></header><div id="post-content" class="markdown" itemprop="articleBody"><h2 class="anchor anchorWithStickyNavbar_LWe7" id="1-背景">1. 背景<a href="#1-背景" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h2><p>随着 Apache InLong(incubating) 的用户和开发者逐渐增多,更丰富的使用场景和低成本运营诉求越来越强烈,其中,InLong 全链路增加 Transform(T)的需求反馈最多。经过@yunqingmoswu、@EMsnap、@gong、@thexiay 社区开发者的调研和设计,完成了基于 Flink SQL 的 InLong Sort ETL 方案,本文将详细介绍该方案的实现细节。</p><p>首先,基于 Apache Flink SQL 主要有以下方面的考量:</p><ul><li>Flink SQL 拥有强大的表达能力带来的高可扩展性、灵活性,基本上 Flink SQL 能支持社区大多数需求场景。当 Flink SQL 内置的函数不满足需求时,我们还可通过各种UDF来扩展。</li><li>Flink SQL 相比 Flink 底层 API 实现开发成本更低,只有第一次需要实现 Flink SQL 的转换逻辑,后续可专注于 Flink SQL 能力本身的构建,比如扩展 Connector、自定义函数UDF等。</li><li>一般来说,Flink SQL 将更健壮、运行也将更稳定。原因在于 Flink SQL 屏蔽了 Flink 底层大量的细节,有强大的社区支持,并且经过大量用户的实践。</li><li>对用户来说,Flink SQL 也更加通俗易懂,特别是对使用过 SQL 用户来说,使用方式简单、熟悉,这有助于用户快速落地。</li><li>对于存量实时任务的迁移,如果其原本就是 SQL 类型的任务,尤其是 Flink SQL 任务,其迁移成本极低,部分情况下甚至都不用做任何改动。</li></ul><p>注意:本方案的所有代码,可以参考 <a href="https://github.com/apache/incubator-inlong/tree/master/inlong-sort" target="_blank" rel="noopener noreferrer">Apache InLong Sort</a> 模块,所含功能可在即将发布的 1.2.0 版本中下载使用。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="2-方案介绍">2. 方案介绍<a href="#2-方案介绍" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="21-方案需求">2.1 方案需求<a href="#21-方案需求" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>该方案的主要需求,是完成的 InLong Sort 模块 Transform(T)能力,包括:</p><table><thead><tr><th align="center">Transform</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">窗口内去重</td><td align="center">在一个时间窗口内对数据去重</td></tr><tr><td align="center">时间窗口聚合</td><td align="center">在一个时间窗口内对数据进行聚合操作</td></tr><tr><td align="center">时间格式转换</td><td align="center">将一个字段的值转换为目标时间格式的字符串</td></tr><tr><td align="center">字段分割</td><td align="center">将一个字段通过某个分割符分割为多个新的字段</td></tr><tr><td align="center">字符串替换</td><td align="center">将替换一个字符串字段中的部分或全部内容</td></tr><tr><td align="center">数据过滤</td><td align="center">将满足过滤条件的数据舍弃或者保留</td></tr><tr><td align="center">内容提取</td><td align="center">提取一个字段的一部分产生一个新的字段</td></tr><tr><td align="center">连接</td><td align="center">支持两表 Join</td></tr><tr><td align="center">值替换</td><td align="center">给定一个匹配值,如果该字段的值等于该值,则将其替换为目标值</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="22-使用场景">2.2 使用场景<a href="#22-使用场景" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>大数据集成的用户,在很多业务场景下都有数据转换、连接、过滤等 Transform 需求。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="23-设计目标">2.3 设计目标<a href="#23-设计目标" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>本次设计需要达到以下目标:</p><ul><li>功能性:在 InLong Sort 现有架构、数据流模型下,覆盖基本的 Transform 能力,并具备快速扩张的能力。</li><li>兼容性:新的 InLong Sort 数据模型向前兼容,确保历史任务能够正常配置运行。</li><li>可维护性:InLong Sort 数据模型转 Flink SQL 只需实现一遍,后期有新增的功能需求时,这块不需要改动,哪怕有改动也是少量改动即可支持。</li><li>可扩展性:当出现开源 Flink Connector 或者内置 Flink SQL 函数不满足需求时,可通过自定义 Flink Connector、UDF 来实现其功能扩展。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="24-基本概念">2.4 基本概念<a href="#24-基本概念" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>核心概念参照概要设计中的名词解释</p><table><thead><tr><th align="center">名称</th><th align="center">含义</th></tr></thead><tbody><tr><td align="center">InLong Dashborad</td><td align="center">Inlong 前端管理界面</td></tr><tr><td align="center">InLong Manager Client</td><td align="center">将 Manager 当中的接口进行包装,供外部用户程序调用,不经过前端 InLong Dashboard</td></tr><tr><td align="center">InLong Manager Openapi</td><td align="center">Inlong manager 与外部系统调用接口</td></tr><tr><td align="center">InLong Manager metaData</td><td align="center">Inlong manager 元数据管理,包括 group、stream 纬度的元数据信息</td></tr><tr><td align="center">InLong Manager task manager</td><td align="center">Inlong manager中管理数据源采集任务模块,管理agent的任务下发,指令下发,心跳上报</td></tr><tr><td align="center">InLong Group</td><td align="center">数据流组,包含多个数据流,一个 Group 代表一个数据接入</td></tr><tr><td align="center">InLong Stream</td><td align="center">数据流,一个数据流有具体的流向</td></tr><tr><td align="center">Stream Source</td><td align="center">流中有对应的采集端和 sink 端,本设计中只涉及到 stream source</td></tr><tr><td align="center">Stream Info</td><td align="center">Sort 中数据流向的抽象,包含该数据流的各种来源、转换、去向等</td></tr><tr><td align="center">Group Info</td><td align="center">Sort 中对数据流向的封装,一个 GroupInfo 可包含多个 Stream Info</td></tr><tr><td align="center">Node</td><td align="center">数据同步中数据源、数据转换、数据去向的抽象</td></tr><tr><td align="center">Extract Node</td><td align="center">数据同步的来源端抽象</td></tr><tr><td align="center">Load Node</td><td align="center">数据同步的去向端抽象</td></tr><tr><td align="center">MySQL Extract Node</td><td align="center">MySQL 数据来源抽象</td></tr><tr><td align="center">Kafka Load Node</td><td align="center">Kafka 数据去向抽象</td></tr><tr><td align="center">Transform Node</td><td align="center">数据同步的转换过程抽象</td></tr><tr><td align="center">Aggregate Transform Node</td><td align="center">数据同步聚合类转换过程抽象</td></tr><tr><td align="center">Node Relation</td><td align="center">数据同步中各个节点关系抽象</td></tr><tr><td align="center">Field Relation</td><td align="center">数据同步中上下游节点字段间关系的抽象</td></tr><tr><td align="center">Function</td><td align="center">转换函数的抽象,即数据同步T中各个 T 能力实现的抽象</td></tr><tr><td align="center">Substring Function</td><td align="center">字符串截取函数的抽象</td></tr><tr><td align="center">Filter Function</td><td align="center">数据过滤函数的抽象</td></tr><tr><td align="center">Function Param</td><td align="center">函数的入参抽象</td></tr><tr><td align="center">Constant Param</td><td align="center">常量参数</td></tr><tr><td align="center">Field Info</td><td align="center">节点字段</td></tr><tr><td align="center">Meta FieldInfo</td><td align="center">节点元信息字段</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="25-领域模型">2.5 领域模型<a href="#25-领域模型" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>本次设计主要涉及到以下实体:</p><p>Group、Stream、GroupInfo、StreamInfo、Node、NodeRelation、FieldRelation、Function、FilterFunction、SubstringFunction、FunctionParam、FieldInfo、MetaFieldInfo、MySQLExtractNode、KafkaLoadNode 等</p><p>为了便于理解,本小节将对实体之间关系进行建模分析。领域模型的实体对应关系说明:</p><ul><li>一个 Group 对应 1 个 GroupInfo</li><li>一个 Stream 对应 1 个 StreamInfo</li><li>一个 Group 包含 1 个或多个 Stream</li><li>一个 GroupInfo 包含 1 个或多个 StreamInfo</li><li>一个 StreamInfo 包含多个 Node</li><li>一个 StreamInfo 包含 1 个或多个 NodeRelation</li><li>一个 NodeRelation 包含 1 个或多个 FieldRelation</li><li>一个 NodeRelation 包含 0 个或多个 FilterFunction</li><li>一个 FieldRelation 包含 1 个 Function 或 1 个 FieldInfo 作为来源字段,1 个 FieldInfo 作为目标字段</li><li>一个 Function 包含 1 个或多个 FunctionParam</li></ul><p>上述关系由 UML 对象关系图可以表示为:</p><p><img loading="lazy" alt="sort_UML" src="/zh-CN/assets/images/sort_UML-896d751427509d769add998680df9516.png" width="2576" height="869" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="26-功能用例图">2.6 功能用例图<a href="#26-功能用例图" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p><img loading="lazy" alt="sort-usecase" src="/zh-CN/assets/images/sort-usecase-fb8639f9724899ab3afcbf35b8a21902.png" width="606" height="356" class="img_ev3q"></p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="3-系统概要设计">3. 系统概要设计<a href="#3-系统概要设计" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="31-系统架构图">3.1 系统架构图<a href="#31-系统架构图" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p><img loading="lazy" alt="architecture" src="/zh-CN/assets/images/architecture-b4c0fb3783a6ed2f2868f534df98e74b.png" width="461" height="741" class="img_ev3q"></p><ul><li>Serialization:序列化实现模块</li><li>Deserialization:反序列化实现模块</li><li>Flink Source:自定义 Flink source实现模块</li><li>Flink Sink:自定义的 Flink sink 实现模块</li><li>Transformation:自定义的 Transform 实现模块</li><li>GroupInfo:对应 Inlong group</li><li>StreamInfo:对应 Inlong stream</li><li>Node:对数据同步中数据来源、数据转换、数据去向的抽象</li><li>FlinkSQLParser:SQL 解析器</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="32-inlong-sort-内部运行流程图">3.2 InLong Sort 内部运行流程图<a href="#32-inlong-sort-内部运行流程图" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p><img loading="lazy" src="/zh-CN/assets/images/sort-operation-flow-77363f12a68a011beba26db9ccc6fedb.png" width="771" height="61" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="33-模块设计">3.3 模块设计<a href="#33-模块设计" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><p>本次设计只对原有系统增加 Flink Connector、FlinkSQL Generator 两个模块,对 Data Model 模块有修改。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="331-模块结构">3.3.1 模块结构<a href="#331-模块结构" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p><img loading="lazy" src="/zh-CN/assets/images/sort-module-structure-4dd424ae93043cb912dba69c08590b33.png" width="771" height="1011" class="img_ev3q"></p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="332-模块划分">3.3.2 模块划分<a href="#332-模块划分" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>重要模块划分说明:</p><table><thead><tr><th align="center">名称</th><th align="center">说明</th></tr></thead><tbody><tr><td align="center">FlinkSQLParser</td><td align="center">用于生成 FlinkSQL 核心类,包含 GroupInfo 的引用</td></tr><tr><td align="center">GroupInfo</td><td align="center">Sort 内部对 InlongGroup 的抽象,用于封装整个 InlongGroup 同步相关信息,包含对 List\&lt;StreamInfo<!-- -->&gt;<!-- --> 的引用</td></tr><tr><td align="center">StreamInfo</td><td align="center">Sort 内部对 InlongStream 的抽象,用于封装 InlongStream 同步相关信息,包含List\&lt;Node<!-- -->&gt;<!-- -->、List\&lt;NodeRelation<!-- -->&gt;<!-- --> 的引用</td></tr><tr><td align="center">Node</td><td align="center">同步节点的顶层接口,它的各个子类实现主要用于对同步数据源、转换节点的数据封装</td></tr><tr><td align="center">ExtractNode</td><td align="center">数据extract节点抽象,继承自 Node</td></tr><tr><td align="center">LoadNode</td><td align="center">数据load节点抽象,继承自 Node</td></tr><tr><td align="center">TransformNode</td><td align="center">数据转换节点抽象,继承自 Node</td></tr><tr><td align="center">NodeRelation</td><td align="center">定义节点间的关系</td></tr><tr><td align="center">FieldRelation</td><td align="center">定义节点间字段的关系</td></tr><tr><td align="center">Function</td><td align="center">T能力执行函数的抽象</td></tr><tr><td align="center">FilterFunction</td><td align="center">用于数据过滤的 Function 抽象,继承自 Function</td></tr><tr><td align="center">SubstringFunction</td><td align="center">用于字符串截取 Function 抽象,继承自 Function</td></tr><tr><td align="center">FunctionParam</td><td align="center">用于函数参数的抽象</td></tr><tr><td align="center">ConstantParam</td><td align="center">函数常量参数的封装,继承自 FunctionParam</td></tr><tr><td align="center">FieldInfo</td><td align="center">节点字段的封装,也可做函数入参使用,继承自 FunctionParam</td></tr><tr><td align="center">MetaFieldInfo</td><td align="center">内置字段的封装,目前主要用于 canal-json 的元数据字段场景,继承自 FieldInfo</td></tr></tbody></table><h2 class="anchor anchorWithStickyNavbar_LWe7" id="4-系统详细设计">4. 系统详细设计<a href="#4-系统详细设计" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h2><p>下面以同步 MySQL 中的数据到 Kafka 为例来说明 SQL 的生成原理。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="41-node-生成-sql">4.1 Node 生成 SQL<a href="#41-node-生成-sql" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="411-extractnode-生成-sql">4.1.1 ExtractNode 生成 SQL<a href="#411-extractnode-生成-sql" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>节点配置为:</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"> private Node buildMySQLExtractNode() {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FieldInfo&gt; fields = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()));</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> return new MySqlExtractNode(&quot;1&quot;, &quot;mysql_input&quot;, fields,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> null, null, &quot;id&quot;,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Collections.singletonList(&quot;tableName&quot;), &quot;localhost&quot;, &quot;root&quot;, &quot;password&quot;,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;inlong&quot;, null, null,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> null, 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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>生成的 SQL 为:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">mysql_1</span><span class="token identifier 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 identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</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 keyword" style="color:rgb(189, 147, 249);font-style:italic">with</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 string" style="color:rgb(255, 121, 198)">&#x27;connector&#x27;</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)">&#x27;mysql-cdc-inlong&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;hostname&#x27;</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)">&#x27;localhost&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;username&#x27;</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)">&#x27;root&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;password&#x27;</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)">&#x27;password&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;database-name&#x27;</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)">&#x27;inlong&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;table-name&#x27;</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)">&#x27;tableName&#x27;</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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="412-transformnode-生成-sql">4.1.2 TransformNode 生成 SQL<a href="#412-transformnode-生成-sql" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>节点配置为:</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FilterFunction&gt; filters = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new SingleValueFilterFunction(EmptyOperator.getInstance(),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> LessThanOperator.getInstance(), new ConstantParam(25)),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new SingleValueFilterFunction(AndOperator.getInstance(),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> MoreThanOrEqualOperator.getInstance(), new ConstantParam(18))</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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>生成的 SQL 为:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">SELECT</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AS</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AS</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">FROM</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">mysql_1</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">WHERE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token operator">&lt;</span><span class="token plain"> </span><span class="token number">25</span><span class="token plain"> </span><span class="token operator">AND</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token operator">&gt;=</span><span class="token plain"> </span><span class="token number">18</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="413-loadnode-生成-sql">4.1.3 LoadNode 生成 SQL<a href="#413-loadnode-生成-sql" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>节点配置为:</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain"> private Node buildKafkaLoadNode(FilterStrategy filterStrategy) {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FieldInfo&gt; fields = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo())</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"> List&lt;FieldRelation&gt; relations = Arrays</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> .asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldRelation(new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo())),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldRelation(new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()))</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"> List&lt;FilterFunction&gt; filters = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new SingleValueFilterFunction(EmptyOperator.getInstance(),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> LessThanOperator.getInstance(), new ConstantParam(25)),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new SingleValueFilterFunction(AndOperator.getInstance(),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> MoreThanOrEqualOperator.getInstance(), new ConstantParam(18))</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"> return new KafkaLoadNode(&quot;2&quot;, &quot;kafka_output&quot;, fields, relations, filters,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> filterStrategy, &quot;topic1&quot;, &quot;localhost:9092&quot;,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new CanalJsonFormat(), null,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> null, &quot;id&quot;);</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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>生成的 SQL 为:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">kafka_3</span><span class="token identifier 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 identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</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 keyword" style="color:rgb(189, 147, 249);font-style:italic">with</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"></span><span class="token string" style="color:rgb(255, 121, 198)">&#x27;connector&#x27;</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)">&#x27;kafka-inlong&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;topic&#x27;</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)">&#x27;topic1&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;properties.bootstrap.servers&#x27;</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)">&#x27;localhost:9092&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;format&#x27;</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)">&#x27;canal-json-inlong&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;canal-json-inlong.ignore-parse-errors&#x27;</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)">&#x27;true&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;canal-json-inlong.map-null-key.mode&#x27;</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)">&#x27;DROP&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;canal-json-inlong.encode.decimal-as-plain-number&#x27;</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)">&#x27;true&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;canal-json-inlong.timestamp-format.standard&#x27;</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)">&#x27;SQL&#x27;</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 string" style="color:rgb(255, 121, 198)">&#x27;canal-json-inlong.map-null-key.literal&#x27;</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)">&#x27;null&#x27;</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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="42-字段-t-生成-sql">4.2 字段 T 生成 SQL<a href="#42-字段-t-生成-sql" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="421-过滤算子">4.2.1 过滤算子<a href="#421-过滤算子" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>相关配置见 4.1 节点配置</p><p>生成的 SQL 为:</p><div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INSERT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTO</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">kafka_3</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">SELECT</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AS</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">name</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">AS</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">FROM</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">mysql_1</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">WHERE</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token operator">&lt;</span><span class="token plain"> </span><span class="token number">25</span><span class="token plain"> </span><span class="token operator">AND</span><span class="token plain"> </span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token identifier">age</span><span class="token identifier punctuation" style="color:rgb(248, 248, 242)">`</span><span class="token plain"> </span><span class="token operator">&gt;=</span><span class="token plain"> </span><span class="token number">18</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="422-水位线">4.2.2 水位线<a href="#422-水位线" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading"></a></h4><p>GroupInfo 完整配置如下:</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">private Node buildMySqlExtractNode() {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FieldInfo&gt; fields = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;ts&quot;, new TimestampFormatInfo()));</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> WatermarkField wk = new WatermarkField(new FieldInfo(&quot;ts&quot;, new TimestampFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new StringConstantParam(&quot;1&quot;),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new TimeUnitConstantParam(TimeUnit.MINUTE));</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> return new MySqlExtractNode(&quot;1&quot;, &quot;mysql_input&quot;, fields,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> wk, null, &quot;id&quot;,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Collections.singletonList(&quot;tableName&quot;), &quot;localhost&quot;, &quot;root&quot;, &quot;password&quot;,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;inlong&quot;, null, null,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> null, null);</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"> private Node buildKafkaNode() {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FieldInfo&gt; fields = Arrays.asList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;ts&quot;, new TimestampFormatInfo()));</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;FieldRelation&gt; relations = Arrays</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> .asList(new FieldRelation(new FieldInfo(&quot;name&quot;, new StringFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;name&quot;, new StringFormatInfo())),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldRelation(new FieldInfo(&quot;age&quot;, new IntFormatInfo()),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> new FieldInfo(&quot;age&quot;, new IntFormatInfo()))</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"> return new KafkaLoadNode(&quot;2&quot;, &quot;kafka_output&quot;, fields, relations, null, null,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> &quot;topic&quot;, &quot;localhost:9092&quot;, new JsonFormat(),</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> 1, null, &quot;id&quot;);</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"> private NodeRelation buildNodeRelation(List&lt;Node&gt; inputs, List&lt;Node&gt; outputs) {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;String&gt; inputIds = inputs.stream().map(Node::getId).collect(Collectors.toList());</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> List&lt;String&gt; outputIds = outputs.stream().map(Node::getId).collect(Collectors.toList());</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> return new NodeRelation(inputIds, outputIds);</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"> @Override</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> public GroupInfo getTestObject() {</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Node input = buildMySqlExtractNode();</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> Node output = buildKafkaNode();</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> StreamInfo streamInfo = new StreamInfo(&quot;1&quot;, Arrays.asList(input, output), Collections.singletonList(</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> buildNodeRelation(Collections.singletonList(input), Collections.singletonList(output))));</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> return new GroupInfo(&quot;1&quot;, Collections.singletonList(streamInfo));</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="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div><footer class="row docusaurus-mt-lg blogPostFooterDetailsFull_mRVl"><div class="col"><b>Tags:</b><ul class="tags_jXut padding--none margin-left--sm"><li class="tag_QGVx"><a class="tag_zVej tagRegular_sFm0" href="/zh-CN/blog/tags/apache-in-long">Apache InLong</a></li><li class="tag_QGVx"><a class="tag_zVej tagRegular_sFm0" href="/zh-CN/blog/tags/sort">Sort</a></li></ul></div><div class="col margin-top--sm"><a href="https://github.com/apache/inlong-website/edit/master/blog/blog/2022-06-16-inlong-sort-etl.md" target="_blank" rel="noreferrer noopener" class="theme-edit-this-page"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_Z9Sw" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--prev" href="/zh-CN/blog/2022/06/22/release-1.2.0"><div class="pagination-nav__sublabel">Newer Post</div><div class="pagination-nav__label">1.2.0 版本发布</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/zh-CN/blog/2022/04/25/release-1.1.0"><div class="pagination-nav__sublabel">Older Post</div><div class="pagination-nav__label">1.1.0 版本发布</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#1-背景" class="table-of-contents__link toc-highlight">1. 背景</a></li><li><a href="#2-方案介绍" class="table-of-contents__link toc-highlight">2. 方案介绍</a><ul><li><a href="#21-方案需求" class="table-of-contents__link toc-highlight">2.1 方案需求</a></li><li><a href="#22-使用场景" class="table-of-contents__link toc-highlight">2.2 使用场景</a></li><li><a href="#23-设计目标" class="table-of-contents__link toc-highlight">2.3 设计目标</a></li><li><a href="#24-基本概念" class="table-of-contents__link toc-highlight">2.4 基本概念</a></li><li><a href="#25-领域模型" class="table-of-contents__link toc-highlight">2.5 领域模型</a></li><li><a href="#26-功能用例图" class="table-of-contents__link toc-highlight">2.6 功能用例图</a></li></ul></li><li><a href="#3-系统概要设计" class="table-of-contents__link toc-highlight">3. 系统概要设计</a><ul><li><a href="#31-系统架构图" class="table-of-contents__link toc-highlight">3.1 系统架构图</a></li><li><a href="#32-inlong-sort-内部运行流程图" class="table-of-contents__link toc-highlight">3.2 InLong Sort 内部运行流程图</a></li><li><a href="#33-模块设计" class="table-of-contents__link toc-highlight">3.3 模块设计</a></li></ul></li><li><a href="#4-系统详细设计" class="table-of-contents__link toc-highlight">4. 系统详细设计</a><ul><li><a href="#41-node-生成-sql" class="table-of-contents__link toc-highlight">4.1 Node 生成 SQL</a></li><li><a href="#42-字段-t-生成-sql" class="table-of-contents__link toc-highlight">4.2 字段 T 生成 SQL</a></li></ul></li></ul></div></div></div></div></div><footer class="footer"><div class="container container-fluid"><div class="row footer__links"><div class="col footer__col"><div class="footer__title">事件</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://www.apachecon.com/" target="_blank" rel="noopener noreferrer" class="footer__link-item">ApacheCon<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li class="footer__item"><a class="acevent" data-format="square" data-mode="dark" data-event="random"></a></li></ul></div><div class="col footer__col"><div class="footer__title">社区</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://twitter.com/ApacheInlong" target="_blank" rel="noopener noreferrer" class="footer__link-item">Twitter<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li class="footer__item"><a href="https://inlong.apache.org/img/apache-inlong-wechat.jpg" target="_blank" rel="noopener noreferrer" class="footer__link-item">WeChat<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li class="footer__item"><a href="mailto:dev@inlong.apache.org" target="_blank" rel="noopener noreferrer" class="footer__link-item">Email</a></li></ul></div><div class="col footer__col"><div class="footer__title">更多</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/zh-CN/blog">Blog</a></li><li class="footer__item"><a href="https://github.com/apache/inlong" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="margin-bottom--sm"><img src="/zh-CN/img/asf_logo.svg" alt="Apache InLong" class="themedImage_ToTc themedImage--light_HNdA footer__logo"><img src="/zh-CN/img/asf_logo.svg" alt="Apache InLong" class="themedImage_ToTc themedImage--dark_i4oU footer__logo"></div><div class="footer__copyright"><div style="font-family: Avenir-Medium;font-size: 14px;color: #999;">
<div>Copyright © 2020-2024 The Apache Software Foundation. Licensed under the Apache License, Version 2.0.</div>
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #666;line-height: 20px;">The Apache Software Foundation Apache InLong, InLong, Apache, the Apache feather, and the Apache InLong project logo are either registered trademarks or trademarks of the Apache Software Foundation.</div>
</div></div></div></div></footer></div>
<script src="/zh-CN/assets/js/runtime~main.933f32cb.js"></script>
<script src="/zh-CN/assets/js/main.32851df5.js"></script>
</body>
</html>