<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾同步推荐</title>
    <link>https://itindex.net/categories/同步</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/categories/同步</link>
    </image>
    <item>
      <title>神同步OpenAI！中国团队Deep Principle领衔发布LLMs for Science评测，引爆外网</title>
      <link>https://itindex.net/detail/63143-%E5%90%8C%E6%AD%A5-openai-%E4%B8%AD%E5%9B%BD</link>
      <description>&lt;p data-pm-slice="0 0 []"&gt;作者丨论文团队&lt;/p&gt;&lt;p&gt;编辑丨ScienceAI&lt;/p&gt;&lt;p&gt;最近，一篇由中国团队领衔全球 24 所 TOP 高校机构发布，用于评测 LLMs for Science 能力高低的论文，在外网炸了！&lt;/p&gt;&lt;p&gt;当晚，Keras （最高效易用的深度学习框架之一）缔造者 Fran&amp;ccedil;ois Chollet 转发论文链接，并喊出：「我们迫切需要新思路来推动人工智能走向科学创新。」&lt;/p&gt;&lt;section&gt;&lt;img data-src="https://mmbiz.qpic.cn/mmbiz_png/XLCp9HBkwLkofGjfaXtexfrcoqPEIj6T07oR3O8SToy6MOcEdaCia3MFibcqNpJn09ZfERm9oTKF1y8BD2cBklpQ/640?wx_fmt=png&amp;from=appmsg#imgIndex=2" data-ratio="0.6963087248322147" data-s="300,640" data-type="png" data-w="596" type="block" data-imgfileid="100027148" data-aistatus="1" data-original-style="null" data-index="2" src="https://image.jiqizhixin.com/uploads/editor/42dae098-8008-456b-b948-ab8794d333ee/640.png" alt="图片" data-before-load-time="1768543355556" data-report-img-idx="2" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;p&gt;AI 领域 KOL Alex Prompter 分享论文核心摘要后，NBA 独行侠队老板 Mark Cuban 跟帖转发，硅谷投资人、欧洲家族办公室、体育媒体同时涌进评论区。&lt;/p&gt;&lt;section&gt;&lt;img data-src="https://mmbiz.qpic.cn/mmbiz_png/XLCp9HBkwLkofGjfaXtexfrcoqPEIj6TEqsW5RwicCMFdr2pMGHCAptN835wBptmNlKIiam8Fl6TEmXfESZdOZbQ/640?wx_fmt=png&amp;from=appmsg#imgIndex=3" data-ratio="1.6446850393700787" data-s="300,640" data-type="png" data-w="1016" type="block" data-imgfileid="100027149" data-aistatus="1" data-original-style="null" data-index="3" src="https://image.jiqizhixin.com/uploads/editor/99212310-ea85-4c65-8425-672c41048151/640.png" alt="图片" data-before-load-time="1768543355562" data-report-img-idx="4" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;p&gt;仅一夜，累计阅读量逼近 200 万。&lt;/p&gt;&lt;p&gt;值得一提的是，同一时间窗里，OpenAI 也发布了对于 AI 在科学发现领域能力评测的论文《FrontierScience: Evaluating Al&amp;#39;s Ability to Perform Scientific Research Tasks》概述，指出现有评测标准在 AI for Science 领域失灵。&lt;/p&gt;&lt;section&gt;&lt;img data-src="https://mmbiz.qpic.cn/mmbiz_png/XLCp9HBkwLkofGjfaXtexfrcoqPEIj6TKUNcXnKX4X5P4lialI0hzJcO4VJRYW2z06d3Nx3g8X7wGHMlg6raKow/640?wx_fmt=png&amp;from=appmsg#imgIndex=4" data-ratio="1.1327731092436975" data-s="300,640" data-type="png" data-w="595" type="block" data-imgfileid="100027150" data-aistatus="1" data-original-style="null" data-index="4" src="https://image.jiqizhixin.com/uploads/editor/2de8dc8d-f04b-4938-9839-bdce91aa4dcd/640.png" alt="图片" data-before-load-time="1768543355562" data-report-img-idx="3" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;p&gt;神同步 OpenAI、海外讨论出圈，究竟是什么样的一份工作成果，搅动了全球 AI 舆论场？&lt;/p&gt;&lt;p&gt;&lt;strong&gt;AI 距离可以助力科学发现还有多远？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;前段时间，美国推出「创世纪计划」，号称要调动「自阿波罗计划以来最大规模的联邦科研资源」，目标是在十年内将美国科研的生产力和影响力翻倍。&lt;/p&gt;&lt;p&gt;但在人工智能估值泡沫隐现、能耗与产出比饱受质疑的当下，一面是资本的狂欢，另一面却是 AI 能力困于「文生图」等表层应用的尴尬；一面是各类大语言模型频繁霸榜 GPQA、MMMU 等题库式 Benchmark 的层出不穷，另一面却是现有 LLMs 还无法准确解析简单核磁图谱的尴尬现状。&lt;/p&gt;&lt;p&gt;人们不禁要问：能在题库拿高分，就能助力科学发现吗？现在的模型距离科学发现还有多远？究竟什么样的 AI 模型可以胜任，拓宽人类的生存边界？这些讨论，在中美 AI 竞争白热化的当下变得愈发浓烈。&lt;/p&gt;&lt;p&gt;在此背景下，由中国 AI for Science 领域的初创企业「深度原理&amp;nbsp;Deep Principle」领衔麻省理工学院、哈佛、普林斯顿、斯坦福、剑桥、牛津等全球 24 所科研院校共同发布的《Evaluating LLMs in Scientific Discovery》论文，正式回答该时代之问。&lt;/p&gt;&lt;p&gt;论文推出了 LLM for Science 首套评测体系&amp;nbsp;SDE（Scientific Discovery Evaluation），从科学问题到研究项目，对 GPT-5、Claude-4.5、DeepSeek-R1、Grok-4 等全球主流大语言模型在生物、化学、材料、物理领域的科学研究与发现能力完成摸底。&lt;/p&gt;&lt;section&gt;&lt;img data-src="https://mmbiz.qpic.cn/mmbiz_png/XLCp9HBkwLkofGjfaXtexfrcoqPEIj6TIBotoxAD2nzVI4EUpCfb92oCE7RAK9ztkTIQPCE2VfjTeC96IDddDw/640?wx_fmt=png&amp;from=appmsg#imgIndex=5" data-ratio="0.7231481481481481" data-s="300,640" data-type="png" data-w="1080" type="block" data-imgfileid="100027151" data-aistatus="1" data-original-style="null" data-index="5" src="https://image.jiqizhixin.com/uploads/editor/88674268-ed99-48c6-ad30-303eeb250907/640.png" alt="图片" data-before-load-time="1768543355622" data-report-img-idx="5" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;p&gt;同以往评测体系不同的是，SDE 对模型能力的考量，从简单的问答式，引向了具体的「假设 -&amp;gt; 实验 -&amp;gt; 分析」实验场景。&lt;/p&gt;&lt;p&gt;研究发现，GPT-5、Claude-4.5、DeepSeek-R1、Grok-4 平均准确率 50&amp;ndash;70%，远低于它们在 GPQA、MMMU 等题库上的 80&amp;ndash;90%；在 86 道「SDE-Hard」难题中，最高分不足 12%，共同暴露出多步推理、不确定性量化和实验与理论闭环的短板。&lt;/p&gt;&lt;p&gt;更值得警惕的是，模型规模与推理能力的提升已呈现明显的「边际效益递减」。&lt;/p&gt;&lt;p&gt;GPT-5 相较于前一代模型，参数规模和推理算力显著增加，但在 SDE 基准的四大科学领域中，平均准确率仅提升 3%-5%，部分场景（如 NMR 结构解析）甚至出现性能下滑。&lt;/p&gt;&lt;p&gt;换句话说，当前大语言模型在推动科学发现方面的表现，还不如一个普通的本科生。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;能领衔 24 所顶尖科研院校发布的背后团队是谁？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;《Evaluating LLMs in Scientific Discovery》论文通讯作者段辰儒，是「深度原理 Deep Principle」创始人兼 CTO。早在 2021 年，在 MIT 攻读化学博士期间，他就已在图灵奖得主 Yoshua Bengio 的支持下，发起了 AI for Science 社区的建立，并在 NeurIPS 上举办 AI for Science workshop。&lt;/p&gt;&lt;p&gt;2024 年初，他与 MIT 物理化学博士贾皓钧回国，共同创立「深度原理 Deep Principle」。贾皓钧任 CEO，段辰儒任 CTO，两人虽为 95 后，但已在全球 AI for Science 创业领域小有名气。&lt;/p&gt;&lt;p&gt;创业一年半以来，其已获得线性资本、高瓴创投、蚂蚁集团等多家知名机构的投资，且与晶泰科技、深势科技等 AI for Science 领域的知名企业建立战略合作关系。&lt;/p&gt;&lt;p&gt;「深度原理 Deep Principle」从创立之初，就带着全球 AI for Science 头部研究者们的期待。目前「深度原理 Deep Principle」已深入全球材料研发中的第一线，将生成式人工智能同量子化学结合起来，致力于推动材料发现等领域进入新纪元。&lt;/p&gt;&lt;p&gt;在过去的一年中，他们在 Nature 大子刊和 JACS 等顶级期刊上不断扔出重磅成果，宣告着他们的技术领先和开放交流的「95 后创业公司」心态。从开拓扩散生成模型（Diffusion Models）在化学反应的生成，证明「不止要生成材料，更需要生成材料的合成路径」，到机器学习势（Machine Learning Potentials, MLPs）和扩散生成模型的直接对比，证明传统的机器学习势不是「万能」的，再到现在组织各大顶级学者和高校推出 SDE，证明传统一问一答的 Benchmark 不能带领我们走向科学超级智能，精准切入 AI for Science 领域的核心冲突。&lt;/p&gt;&lt;p&gt;但同时，对于所有的 AI4S 公司而言，在商业真金白银的检验中，AI 能否真正解决新产品研发问题、满足客户期待，是日复一日必须面对的拷问。&lt;/p&gt;&lt;p&gt;随着与行业头部客户的商业化合作落地，「深度原理 Deep Principle」的数据库中已经汇聚了来源于客户与自己实验室、大量来自第一线的真实工业研发场景数据和模型应用经验。&lt;/p&gt;&lt;p&gt;学术圈的深耕与在 AI for Science 商业化第一线的积累，让「深度原理 Deep Principle」在提出要构建一把新尺子评测 LLMs for Science 能力时，一呼百应，摇来了 23 家全球 TOP 科学发现机构的 50 余位科学家，成立了制定 SDE 的「梦之队」。&lt;/p&gt;&lt;p&gt;这其中，不乏活跃在 LLM 领域的大牛学者们，比如：&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;孙欢（Huan Sun），MMMU 发起人，俄亥俄州立教授&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;杜沅岂（Yuanqi Du），康奈尔博士，AI4Science 社区「运营大管家」&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;王梦迪，普林斯顿最年轻教授，AI+Bio Safety 先驱者&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Philippe Schwaller，IBM RXN&amp;nbsp;之父，EPFL 教授&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;而「深度原理 Deep Principle」前期积累的科学发现场景，成为了后来 SDE 评测体系的前身。&lt;/p&gt;&lt;p&gt;在经历近 9 个月的跨高校跨学科跨时区的协作后，《Evaluating LLMs in Scientific Discovery》论文正式发布，通讯单位赫然写着：深度原理，杭州，中国。 &amp;nbsp;&lt;/p&gt;&lt;section&gt;&lt;img data-src="https://mmbiz.qpic.cn/mmbiz_png/XLCp9HBkwLkofGjfaXtexfrcoqPEIj6TZ9Bm2yttmJFP3pDLWM7byticdciah5xjfw72YVdibVFibYKLUINKWaC5RA/640?wx_fmt=png&amp;from=appmsg#imgIndex=6" data-ratio="1.2836601307189544" data-s="300,640" data-type="png" data-w="765" type="block" data-imgfileid="100027152" data-aistatus="1" data-original-style="null" data-index="6" src="https://image.jiqizhixin.com/uploads/editor/d3809a4e-b413-4edc-a284-f3928d5b7b14/640.png" alt="图片" data-before-load-time="1768543355960" data-report-img-idx="6" data-fail="0" class="fr-fic fr-dib" style="width: 700%;"&gt;&lt;/section&gt;&lt;p&gt;自此，汇聚着全球顶级科学发现机构的集体智慧，来自中国的创业团队「深度原理 Deep Principle」，和大洋彼岸的 OpenAI，同时站在了向 AI for Science&amp;mdash;&amp;mdash; 这一人类通往终极 AGI 顶峰攀登的起跑线。&lt;/p&gt;&lt;p&gt;或许千百年后，当人类回望 AGI 时代，在 21 世纪的四分之一结束的当口，这场由中美团队共同呼应的，对于 AI for Science 的严肃讨论，把 LLMs 在各类问答式榜单上的内卷，向真正科学发现的星辰大海推近了一步。&lt;/p&gt;&lt;p&gt;至于怎么通往彼岸，段辰儒表示：「当大语言模型在各种科学问答榜单表现饱和，但还不能有效支持科学发现时，就像『考试成绩好』不等于『顶级研究者』，说明我们需要新的评测体系与训练路径。」&lt;/p&gt;&lt;p&gt;「深度原理 Deep Principle」与 20 多所机构的 50 多位合作者的研究证明了，目前 LLM 的发展路径并不能「顺便攻克」科学发现。&lt;/p&gt;&lt;p&gt;这条通往科学超级智能之路，需要更多有识之士共同并肩而行。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/63143-%E5%90%8C%E6%AD%A5-openai-%E4%B8%AD%E5%9B%BD</guid>
      <pubDate>Fri, 16 Jan 2026 14:03:00 CST</pubDate>
    </item>
    <item>
      <title>开源实时数据同步工具NiFi</title>
      <link>https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;h2&gt;Apache NiFi简介&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 是一个强大的数据流管理和自动化工具，旨在简化数据的采集、传输、处理和分发。它特别适合于构建和管理复杂的数据流管道，支持从各种数据源到不同目标系统的数据传输。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="245" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/NiFi.png" width="525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;Apache NiFi主要功能&lt;/h3&gt;
 &lt;p&gt;Apache NiFi 是一个用于自动化数据流的强大工具，具有广泛的功能集，旨在支持从各种数据源到不同目标的复杂数据流管道。以下是 Apache NiFi 的主要功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与传输&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持从多种数据源采集数据，包括文件系统、数据库、HTTP 服务、消息队列（如 Kafka）、传感器设备等。&lt;/li&gt;
    &lt;li&gt;支持将数据传输到多种目标系统，如 HDFS、数据库、云存储服务、REST API 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据流可视化&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供直观的 Web 用户界面，用户可以通过拖拽和配置处理器来设计和管理数据流。&lt;/li&gt;
    &lt;li&gt;实时显示数据流的状态和性能指标，便于监控和调试。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据处理与转换&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供丰富的内置处理器，支持数据的解析、转换、清洗、聚合和格式化等操作。&lt;/li&gt;
    &lt;li&gt;支持复杂的数据处理逻辑，如条件路由、数据分片、合并和拆分。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;动态路由与优先级控制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持根据数据内容或属性动态路由数据到不同的处理器或目标。&lt;/li&gt;
    &lt;li&gt;允许为数据流设置优先级，以控制数据处理的顺序和速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时流处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持实时数据流处理，能够在数据到达时立即执行处理操作。&lt;/li&gt;
    &lt;li&gt;事件驱动架构，处理器在接收到数据或触发条件时自动执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;分布式架构与扩展性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
    &lt;li&gt;集群中的节点通过 Apache ZooKeeper 进行协调和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据安全与合规&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据加密、访问控制和用户身份验证，确保数据的安全性。&lt;/li&gt;
    &lt;li&gt;提供数据审计功能，记录数据流的处理历史和用户操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;错误处理与重试机制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;自动处理数据传输和处理过程中出现的错误，支持重试和故障转移。&lt;/li&gt;
    &lt;li&gt;提供数据回退和恢复功能，确保数据的可靠性和完整性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;可扩展性与集成性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持自定义处理器和控制器服务的开发，用户可以根据需要扩展 NiFi 的功能。&lt;/li&gt;
    &lt;li&gt;与其他大数据工具和框架（如 Apache Kafka、Hadoop、Spark）紧密集成，支持复杂的数据处理和分析工作流。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的日志记录和监控功能，帮助用户了解数据流的执行状态和性能指标。&lt;/li&gt;
    &lt;li&gt;支持告警和通知机制，用户可以根据特定条件设置告警，及时响应异常情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的设计目标是提供一个灵活、高效且易于管理的数据流管理平台，适用于各种数据集成和处理场景。其丰富的功能集使其成为企业数据管道构建和管理的理想选择。&lt;/p&gt;
 &lt;h3&gt;Apache NiFi的优势&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;灵活性：通过丰富的处理器和自定义开发能力，NiFi 可以适应各种复杂的数据处理需求。&lt;/li&gt;
  &lt;li&gt;可扩展性：支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
  &lt;li&gt;可视化管理：提供直观的 Web UI，用户可以轻松设计和管理数据流，无需编写复杂的代码。&lt;/li&gt;
  &lt;li&gt;高可用性：通过故障转移和数据重试机制，确保数据流的高可用性和可靠性。&lt;/li&gt;
  &lt;li&gt;安全性：支持数据加密、访问控制和审计，确保数据的安全性和隐私保护。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Apache NiFi的架构&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 的架构设计旨在提供一个灵活、高效且可扩展的数据流管理平台。它采用模块化设计，支持分布式部署，能够处理各种规模和复杂度的数据流任务。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="510" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/nifi-2.png" width="970"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;核心组件&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;Web UI（用户界面）：NiFi 提供了一个直观的 Web 用户界面，用于设计、监控和管理数据流。用户可以通过拖拽和配置组件来构建数据流，并实时查看数据流的状态和性能指标。&lt;/li&gt;
  &lt;li&gt;FlowFile：FlowFile 是 NiFi 中的数据单元，包含数据内容和属性。每个 FlowFile 都有唯一标识符和元数据，支持数据的高效传输和处理。&lt;/li&gt;
  &lt;li&gt;处理器（Processor）：处理器是执行特定数据处理任务的基本单元。NiFi 提供了丰富的内置处理器，用于数据采集、转换、路由和传输。用户还可以开发自定义处理器以满足特定需求。&lt;/li&gt;
  &lt;li&gt;连接（Connection）：连接用于在处理器之间传递 FlowFile。连接可以配置为使用不同的队列策略，以控制数据的流动速度和优先级。&lt;/li&gt;
  &lt;li&gt;流程组（Process Group）：流程组用于组织和管理多个处理器和连接，形成逻辑上的子流程。这有助于复杂数据流的模块化设计和维护。&lt;/li&gt;
  &lt;li&gt;控制器服务（Controller Service）：控制器服务提供共享的配置和服务，例如数据库连接池、分布式缓存等。它们可以在多个处理器之间复用，提高资源利用率。&lt;/li&gt;
  &lt;li&gt;报告任务（Reporting Task）：报告任务用于生成和发送 NiFi 系统的运行状态和指标数据，通常用于监控和告警系统。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;工作流和数据流&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;数据流从输入处理器开始，输入处理器从外部数据源（如文件系统、HTTP、Kafka）获取数据并生成 FlowFile。&lt;/li&gt;
    &lt;li&gt;中间处理器对 FlowFile 进行处理，包括数据解析、转换、过滤和聚合等操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据路由与分发&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;根据业务规则和条件，NiFi 可以将 FlowFile 路由到不同的处理器或流程组。&lt;/li&gt;
    &lt;li&gt;输出处理器将处理后的 FlowFile 发送到目标系统（如 HDFS、数据库、云存储）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;Web UI 提供实时数据流监控功能，用户可以查看处理器的性能指标、队列长度、处理速率等。&lt;/li&gt;
    &lt;li&gt;用户可以动态调整数据流的配置和参数，以优化性能和处理逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;分布式架构&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;多节点集群&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 支持多节点集群部署，可以通过增加节点来扩展处理能力。集群中的每个节点都可以执行数据流任务。&lt;/li&gt;
    &lt;li&gt;集群节点通过 Apache ZooKeeper 进行协调和管理，以确保任务的负载均衡和高可用性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;高可用性与故障转移&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 采用主从架构，集群中一个节点为主节点（Primary Node），负责调度任务和管理集群配置。&lt;/li&gt;
    &lt;li&gt;在主节点故障时，集群会自动选举新的主节点，确保数据流的持续性和可靠性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;安全性&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;用户认证与授权&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多种认证机制（如 LDAP、Kerberos），确保只有授权用户才能访问和管理 NiFi 系统。&lt;/li&gt;
    &lt;li&gt;提供细粒度的权限控制，用户可以对不同的数据流组件和操作进行授权。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据加密&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据传输加密和存储加密，确保数据在传输和存储过程中的安全性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;审计与日志&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的审计日志记录，记录用户操作和数据流处理历史，便于合规性检查和故障排查。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的架构设计使其成为一个灵活、可扩展和安全的数据流管理平台，适用于各种规模和复杂度的数据集成和处理任务。其模块化设计和丰富的功能集使得用户能够高效地构建和管理复杂的数据流管道。&lt;/p&gt;
 &lt;h2&gt;Airflow、Kafka的对比&lt;/h2&gt;
 &lt;p&gt;Apache NiFi、Apache Airflow 和 Apache Kafka 都是现代数据处理和管理生态系统中的重要工具，但它们各自的设计目的和应用场景有所不同。以下是它们的详细对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;特性&lt;/td&gt;
   &lt;td&gt;Apache NiFi&lt;/td&gt;
   &lt;td&gt;Apache Airflow&lt;/td&gt;
   &lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;主要用途&lt;/td&gt;
   &lt;td&gt;实时数据流管理和自动化&lt;/td&gt;
   &lt;td&gt;工作流调度和管理&lt;/td&gt;
   &lt;td&gt;消息队列和流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;架构特点&lt;/td&gt;
   &lt;td&gt;可视化界面，事件驱动架构&lt;/td&gt;
   &lt;td&gt;编程接口定义工作流（DAGs），基于调度器和执行器&lt;/td&gt;
   &lt;td&gt;发布/订阅模型，分布式架构&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;数据处理&lt;/td&gt;
   &lt;td&gt;实时数据采集、转换和路由&lt;/td&gt;
   &lt;td&gt;批处理任务调度，不直接处理数据流&lt;/td&gt;
   &lt;td&gt;高吞吐量的消息传输，支持实时流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;扩展性与部署&lt;/td&gt;
   &lt;td&gt;多节点集群，水平扩展&lt;/td&gt;
   &lt;td&gt;分布式调度和执行，支持多种执行器&lt;/td&gt;
   &lt;td&gt;水平扩展，通过分区和副本实现容错&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;安全性&lt;/td&gt;
   &lt;td&gt;细粒度权限控制和数据加密&lt;/td&gt;
   &lt;td&gt;用户认证和授权（RBAC）&lt;/td&gt;
   &lt;td&gt;SSL 加密、SASL 认证和 ACL 授权&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;应用场景&lt;/td&gt;
   &lt;td&gt;实时数据集成、物联网数据采集、日志管理和监控&lt;/td&gt;
   &lt;td&gt;定时数据处理任务、复杂的 ETL 管道、机器学习工作流&lt;/td&gt;
   &lt;td&gt;实时数据传输、日志收集和分析、事件驱动架构&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;对比总结&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时 vs 批处理：
   &lt;ul&gt;
    &lt;li&gt;NiFi：适合实时数据流处理和自动化。&lt;/li&gt;
    &lt;li&gt;Airflow：适合批处理任务调度和复杂的工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：适合高吞吐量的消息传输和实时流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;用户界面 vs 编程接口：
   &lt;ul&gt;
    &lt;li&gt;NiFi：提供可视化界面，适合需要快速构建和管理数据流的场景。&lt;/li&gt;
    &lt;li&gt;Airflow：提供编程接口，适合需要灵活定义复杂工作流的场景。&lt;/li&gt;
    &lt;li&gt;Kafka：主要通过编程接口和命令行工具进行管理和配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;数据流管理 vs 工作流调度 vs 消息队列：
   &lt;ul&gt;
    &lt;li&gt;NiFi：专注于数据流的管理和处理。&lt;/li&gt;
    &lt;li&gt;Airflow：专注于任务调度和工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：专注于消息队列和流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;根据具体的需求和场景，企业可以选择合适的工具或组合使用这些工具来构建复杂的数据处理和管理生态系统。例如，可以使用 NiFi 进行数据采集和预处理，使用 Kafka 进行高吞吐量的消息传输，使用 Airflow 进行批处理任务的调度和管理。&lt;/p&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/kafka.html" rel="bookmark" title="&amp;#20998;&amp;#24067;&amp;#24335;&amp;#28040;&amp;#24687;&amp;#31995;&amp;#32479;Kafka"&gt;分布式消息系统Kafka&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-sql-guide.html" rel="bookmark" title="Hive SQL&amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Hive SQL系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-schedule.html" rel="bookmark" title="Python&amp;#33258;&amp;#21160;&amp;#21270;&amp;#20043;&amp;#23450;&amp;#26102;&amp;#20219;&amp;#21153;"&gt;Python自动化之定时任务&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 大数据</category>
      <guid isPermaLink="true">https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Sat, 09 Nov 2024 21:04:10 CST</pubDate>
    </item>
    <item>
      <title>✨基于Spring-Data-Elasticsearch 优雅的实现 多字段搜索 + 高亮 + 分页 + 数据同步✨</title>
      <link>https://itindex.net/detail/62458-spring-data-elasticsearch</link>
      <description>&lt;hr&gt;&lt;/hr&gt;
 &lt;h2&gt;theme: devui-blue
highlight: a11y-dark&lt;/h2&gt;
 &lt;p&gt;持续创作，加速成长！这是我参与「掘金日新计划 · 10 月更文挑战」的第17天，  &lt;a href="https://juejin.cn/post/7147654075599978532" title="https://juejin.cn/post/7147654075599978532"&gt;点击查看活动详情&lt;/a&gt;&lt;/p&gt;
 &lt;h1&gt;系列说明&lt;/h1&gt;
 &lt;p&gt;本系列文章基于我的开源微服务项目【校园博客】进行分析和讲解，所有源码均可在GitHub仓库上找到。
系列文章地址请见我的   &lt;a href="https://juejin.cn/column/7149909884270542856"&gt;校园博客专栏&lt;/a&gt;。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;GitHub地址：   &lt;a href="https://github.com/stick-i/scblogs"&gt;https://github.com/stick-i/scblogs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;目前项目还有很大改进和完善的空间，欢迎各位有意愿的同学参与项目贡献&lt;/strong&gt;（尤其前端），一起学习一起进步。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;项目的技术栈主要是：
   &lt;br /&gt;
后端 Java + SpringBoot + SpringCloud + Nacos + Getaway + Fegin + MybatisPlus + MySQL + Redis + ES + RabbitMQ + Minio + 七牛云OSS + Jenkins + Docker
   &lt;br /&gt;
前端 Vue + ElementUI + Axios（说实话前端我不太清楚）&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h1&gt;前言&lt;/h1&gt;
 &lt;p&gt;本篇文章主要是一些对Spring-Data-Elasticsearch使用上的记录和讲解，对原理和基础知识并没有介绍，适合有一定ES基础的朋友阅读。&lt;/p&gt;
 &lt;p&gt;为了给项目添加一个好的搜索功能，我去学习了一下elasticsearch。&lt;/p&gt;
 &lt;p&gt;在学习elasticsearch-client的期间，发现它提供的api不太优雅，用起来也不太舒服，而且我觉得有些操作完全是可以封装在内部的，比如获取数据后，对数据转化为bean的操作；还有属性高亮，不仅设置比较麻烦，而且设置完成的高亮居然是单独在一个字段里的，需要开发者去手动的替换才行，这些操作我觉得其实都可以封装在内部的，害，个人感慨，请勿介意。&lt;/p&gt;
 &lt;p&gt;然后我就去看了一下spring-data里面提供的 es 操作库，发现有很多操作都封装的比较完善，使用起来也比较优雅，于是我便使用spring-data-elasticsearch完成了这个功能，查阅了很多资料、博客、官方文档，有些地方我觉得官方文档讲的也不够详细，导致走了很多弯路，也可能是我没有找到详细的文档。&lt;/p&gt;
 &lt;p&gt;为了方便大家学习和少走弯路，也便于本人日后回顾，故记录于此。&lt;/p&gt;
 &lt;blockquote&gt;
  &lt;p&gt;本篇文章讲的内容是在项目的 /blog-service/blog-content-server 路径下，感兴趣的同学欢迎随时查看，觉得不错的话也欢迎点点star噢。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h1&gt;技术要点&lt;/h1&gt;
 &lt;ol&gt;
  &lt;li&gt;使用    &lt;em&gt;copyTo&lt;/em&gt; 和     &lt;em&gt;ElasticsearchRepository&lt;/em&gt; 完成的多字段搜索。&lt;/li&gt;
  &lt;li&gt;使用注解    &lt;em&gt;@Highlight&lt;/em&gt; 和    &lt;em&gt;@HighlightField&lt;/em&gt; 完成的高亮显示。&lt;/li&gt;
  &lt;li&gt;使用    &lt;em&gt;Pageable&lt;/em&gt; 和    &lt;em&gt;SearchPage&lt;/em&gt; 实现分页和高亮两不误的接口。&lt;/li&gt;
  &lt;li&gt;使用    &lt;em&gt;RabbitMQ&lt;/em&gt; 完成    &lt;em&gt;MySQL&lt;/em&gt; 和    &lt;em&gt;elasticsearch&lt;/em&gt; 的数据同步。&lt;/li&gt;
&lt;/ol&gt;
 &lt;h1&gt;依赖项&lt;/h1&gt;
 &lt;p&gt;我当前的环境：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;springboot 2.6.6&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;elasticsearch 7.12&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;kibana 7.12（这个不是必须的）&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;然后当前版本的spring默认是用的 7.15.2 的我担心和我的es不兼容，就加了个标签给它改了一下版本：&lt;/p&gt;
   &lt;pre&gt;    &lt;code&gt;&amp;lt;elasticsearch.version&amp;gt;7.12.1&amp;lt;/elasticsearch.version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;核心依赖其实就这一个，这里面已经依赖了elasticsearch需要的一些依赖，例如   &lt;code&gt;elasticsearch-rest-high=level-client&lt;/code&gt;。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;spring-boot-starter-data-elasticsearch&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后如果跟我一样使用   &lt;code&gt;RabbitMQ&lt;/code&gt; 做数据同步的话，还需要引用mq的依赖：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;&amp;lt;!--AMQP依赖，包含RabbitMQ--&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;spring-boot-starter-amqp&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!-- json序列化依赖，需要手动配置bean --&amp;gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;com.fasterxml.jackson.core&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;jackson-databind&amp;lt;/artifactId&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;配置文件&lt;/h1&gt;
 &lt;p&gt;这里需要配置elasticsearch的账号密码&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;spring:
  elasticsearch:
    uris: &amp;quot;http://localhost:9200&amp;quot;
    username: 12345
    password: 12345
&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;核心代码&lt;/h1&gt;
 &lt;h2&gt;实体类BlogDoc&lt;/h2&gt;
 &lt;p&gt;下面是我代码当中跟 es 进行交互的实体类，代码上有相关的注释，我将一些多余的、意义不大的属性删掉了，方便大家查看。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;package cn.sticki.blog.content.pojo;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

/**
 * Blog ES文档类型
 *
 * @author 阿杆
 * @version 1.0
 * @date 2022/7/8 15:24
 */
@Data
@Document(indexName = &amp;quot;blog&amp;quot;)
public class BlogDoc {

/**
 * 博客id
 */
@Id
Integer id;

/**
 * 封面图链接
 */
@Field(type = FieldType.Keyword, index = false)
String coverImage;

/**
 * 标题
 */
@Field(type = FieldType.Text, analyzer = &amp;quot;ik_max_word&amp;quot;, copyTo = &amp;quot;descriptiveContent&amp;quot;)
String title;

/**
 * 描述
 */
@Field(type = FieldType.Text, analyzer = &amp;quot;ik_max_word&amp;quot;, copyTo = &amp;quot;descriptiveContent&amp;quot;)
String description;

/**
 * 创建时间
 */
@Field(type = FieldType.Date, pattern = &amp;quot;uuuu-MM-dd HH:mm:ss&amp;quot;)
Date createTime;

/**
 * 发表状态（1表示已发表、2表示未发表、3为仅自己可见、4为回收站、5为审核中）
 */
@Field(type = FieldType.Integer)
Integer status;

/**
 * 由其他属性copy而来，主要用于搜索功能，不需要储存数据
 */
@JsonIgnore
@Field(type = FieldType.Text, analyzer = &amp;quot;ik_max_word&amp;quot;, ignoreFields = &amp;quot;descriptiveContent&amp;quot;, excludeFromSource = true)
String descriptiveContent;

}

&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;注解说明：&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;
   &lt;p&gt;@Document(indexName = &amp;quot;blog&amp;quot;)：声明该实体类对应es中的哪个    &lt;strong&gt;索引库&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;@Id：声明该字段对应索引库当中的id。&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;@JsonIgnore：这个应该很熟悉吧，就是    &lt;strong&gt;在json序列化时将对象中的一些属性忽略掉，使返回的json数据不包含该属性&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;@Field(...) ，这些其实都    &lt;strong&gt;对应es的api调用时传入的字段&lt;/strong&gt;，有一点es基础会很容易看懂，也可以看看我写的elasticsearch专栏下的其他文章，前几篇是我学基础的时候记录的。&lt;/p&gt;
   &lt;ul&gt;
    &lt;li&gt;
     &lt;p&gt;type = FieldType.Integer ：声明字段属性，如果不写，默认为auto，就是es会帮你自动匹配成最合适的字段类型，建议还是写一下。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;index = false ：声明该字段不需要建立索引，一般用于不会被拿来搜索、排序、统计的字段，比如我这里写的封面图链接。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;analyzer = &amp;quot;ik_max_word&amp;quot; ： 声明该text字段需要使用的分词器，我这里是用的ik分词器，需要开发者去手动安装，但对中文分词比较友好。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;excludeFromSource = true：翻译出来意思是“从源中排除”，应该是指这个字段的属性不会插入到es索引库当中吧，这个字段是我用来``copy_to`的，主要是搜索的时候使用，本身并不会直接存入数据，所以这个字段如果有数据，我希望插入的时候把它忽略。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;copyTo = &amp;quot;descriptiveContent&amp;quot;：这个就是跟es的copy_to一样，就是说把当前属性拷贝到“descriptiveContent”当中，可以拷贝多个属性到同一个字段中，便于搜索、查询。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;pattern = &amp;quot;uuuu-MM-dd HH:mm:ss&amp;quot; ： 声明该自定义的格式字符串，一般在type = FieldType.Date时使用。&lt;/p&gt;
&lt;/li&gt;
    &lt;li&gt;
     &lt;p&gt;format：跟pattern差不多，官方解释是用于定义至少一种预定义格式。如果未定义，则使用默认值*_date_optional_time      &lt;em&gt;和&lt;/em&gt;epoch_millis*。也就是只能使用给定的枚举值，不能自定义，自定义的话得用pattern。下图是谷歌翻译的官方解释：&lt;/p&gt;
     &lt;p&gt;      &lt;img alt="image-20221016134857918" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33a46def0631422d93d4cce030282295~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;实体类属性copy_to&lt;/h3&gt;
 &lt;p&gt;大家都知道，在es当中如果有多个字段需要被同时查询（比如我的博客业务，要搜索内容的时候，我会把用户输入的关键字同时拿来匹配标题和文章描述），那可以用  &lt;em&gt;multi_match&lt;/em&gt;、  &lt;em&gt;query_string&lt;/em&gt;进行多字段查询，也可以用  &lt;em&gt;copy_to&lt;/em&gt;将多个字段复制到一个新属性上再去查新属性，这几种方法都是可以的，但是copy_to它的性能会高一些，尤其是在同时要查的属性非常多的时候，这属于是一种储存换取速度的方式。&lt;/p&gt;
 &lt;p&gt;  &lt;em&gt;copy_to&lt;/em&gt;的属性在上面已经讲过了，跟es的api用来起来差不多的，但是我上面的代码还写了一个  &lt;code&gt;descriptiveContent&lt;/code&gt;：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;/**
 * 由其他属性copy而来，主要用于搜索功能，不需要储存数据
 */
@JsonIgnore
@Field(type = FieldType.Text, analyzer = &amp;quot;ik_max_word&amp;quot;, ignoreFields = &amp;quot;descriptiveContent&amp;quot;, excludeFromSource = true)
String descriptiveContent;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这个属性就是被cope_to到的那个属性，但实际上我们在写代码的时候并不会给它赋值或者取值或者别的怎么样，总是就是希望他尽可能透明，仅在对es时有效，因为es里是已经提前定义好这个索引库了的，es创建索引库的代码我会贴在文章最后。&lt;/p&gt;
 &lt;p&gt;这是因为，后面我们要使用  &lt;em&gt;ElasticsearchRepository&lt;/em&gt;的时候，被查询的字段如果不存在于这个实体类，idea会有一个很碍眼的提示，  &lt;s&gt;作为强迫症患者&lt;/s&gt;，这就引发了我的思考，是不是我们在定义实体类的时候，要和定义索引库的时候一样给出全部的字段呢？尽管这个字段只是一个“隐身”的字段。  &lt;s&gt;为了把这个碍眼的提示去掉&lt;/s&gt; 为了让代码变得更可读一点，所以我加上了这个字段，并加了一些忽略的属性使它尽可能隐身。&lt;/p&gt;
 &lt;h2&gt;Mapper层（Repository）&lt;/h2&gt;
 &lt;p&gt;核心代码如下，具体解释和分析在下面：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;package cn.sticki.blog.content.mapper;

import cn.sticki.blog.content.pojo.BlogDoc;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.annotations.Highlight;
import org.springframework.data.elasticsearch.annotations.HighlightField;
import org.springframework.data.elasticsearch.annotations.HighlightParameters;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

/**
 * BlogRepository操作类
 * 提供save、findById、findAll、count、delete、exists等接口
 *
 * @author 阿杆
 * @version 1.0
 * @date 2022/7/9 10:53
 */
public interface BlogRepository extends ElasticsearchRepository&amp;lt;BlogDoc, Long&amp;gt; {

/**
 * 通过描述内容来搜索博客
 *
 * @param descriptiveContent 描述语句
 * @param pageable           分页
 * @return 博客列表
 */
@SuppressWarnings(&amp;quot;SpringDataRepositoryMethodReturnTypeInspection&amp;quot;)
@Highlight(fields = {
@HighlightField(name = &amp;quot;title&amp;quot;, parameters = @HighlightParameters(requireFieldMatch = false)),
@HighlightField(name = &amp;quot;description&amp;quot;, parameters = @HighlightParameters(requireFieldMatch = false)),
})
SearchPage&amp;lt;BlogDoc&amp;gt; findByDescriptiveContent(String descriptiveContent, Pageable pageable);

}
&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;继承ElasticsearchRepository&lt;/h3&gt;
 &lt;ol&gt;
  &lt;li&gt;
   &lt;p&gt;这个其实就有点像继承    &lt;em&gt;BaseMapper&lt;/em&gt;，它会给你提供一些基础的CRUD方法，方便你直接使用，比如save、delete、find之类的。&lt;/p&gt;
   &lt;p&gt;    &lt;img alt="image-20221016142548009" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/acc596c245f64c3a8e6079fe13d82f90~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;它是个泛型类，两个参数分别是     &lt;strong&gt;&amp;lt;实体类，id的类型&amp;gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;在该接口下（BlogRepository）按照特殊的命名规则声明的方法，可以直接调用，不需要开发者实现接口，且它返回的内容是已经封装好的，    &lt;strong&gt;你需要的数据会被封装在你提供的实体类里面&lt;/strong&gt;（不用手动解析数据）。&lt;/p&gt;
   &lt;p&gt;大概就是     &lt;em&gt;findByXxxAndXxxOrXxx()&lt;/em&gt; 这个类型，具体的可以参考官网：    &lt;a href="https://docs.spring.io/spring-data/elasticsearch/docs/4.3.5/reference/html/#elasticsearch.query-methods.criterions"&gt;https://docs.spring.io/spring-data/elasticsearch/docs/4.3.5/reference/html/#elasticsearch.query-methods.criterions&lt;/a&gt;，这里也截一点给大家看看（谷歌浏览器翻译的）：&lt;/p&gt;
   &lt;p&gt;    &lt;img alt="image-20221016142312365" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9c9082d9564f477a850ecfff40e28c2c~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
   &lt;p&gt;    &lt;img alt="image-20221016142406280" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/952e69fe1d514da9a27a8e3f35a3cb81~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;/li&gt;
  &lt;li&gt;
   &lt;p&gt;也可以使用     &lt;em&gt;@Query&lt;/em&gt; 注解写原生的 api 请求接口，不太优雅，个人不推荐使用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;然后这里我只添加了一个方法：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SearchPage&amp;lt;BlogDoc&amp;gt; findByDescriptiveContent(String descriptiveContent, Pageable pageable);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;这个意思就是所通过   &lt;code&gt;DescriptiveContent&lt;/code&gt; 属性来查询数据，后面的两个参数一个是搜索的内容，一个是分页的参数（分页需要配合支持分页的返回值才行）。&lt;/p&gt;
 &lt;p&gt;这个findByXxx的Xxx属性必须是实体类里面存在的属性才可以，不然会提示错误：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image-20221016142649418" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2ccb6f038db444b0a68ee1f0f87d66a3~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;高亮显示&lt;/h3&gt;
 &lt;pre&gt;  &lt;code&gt;@SuppressWarnings(&amp;quot;SpringDataRepositoryMethodReturnTypeInspection&amp;quot;)
@Highlight(fields = {
@HighlightField(name = &amp;quot;title&amp;quot;, parameters = @HighlightParameters(requireFieldMatch = false)),
@HighlightField(name = &amp;quot;description&amp;quot;, parameters = @HighlightParameters(requireFieldMatch = false)),
})
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;使用注解   &lt;em&gt;@Highlight&lt;/em&gt; 和   &lt;em&gt;@HighlightField&lt;/em&gt;，来设置高亮的字段，使用   &lt;em&gt;@HighlightParameters&lt;/em&gt; 来添加高亮的参数。&lt;/p&gt;
 &lt;p&gt;我这里设置了requireFieldMatch = false，这个参数是  &lt;strong&gt;取消只有字段匹配才给高亮的规则&lt;/strong&gt;，这是因为我搜索的字段是由另外两个字符copyTo而来的，高亮的内容肯定是在另外两个字段里面，设置该参数可以让其他字段的高亮也展示出来。&lt;/p&gt;
 &lt;p&gt;这里还有一篇高亮显示的教程文章，我讲的比较粗糙，他这个写的比较详细，贴给大家学习：  &lt;a href="https://blog.csdn.net/qq_45794678/article/details/111188548"&gt;https://blog.csdn.net/qq_45794678/article/details/111188548&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;官方文档给的说明就这么点。。。  &lt;s&gt;怕我学会了然后教别人吗&lt;/s&gt;。。。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image-20221016142838476" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1cf9226fb92d4ab485c215addece891c~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;分页功能&lt;/h3&gt;
 &lt;p&gt;通过   &lt;em&gt;Pageable&lt;/em&gt; 做参数和   &lt;em&gt;SearchPage&lt;/em&gt; 做返回值来完成了对分页的需求，传参的时候使用   &lt;code&gt;PageRequest.of(page, size)&lt;/code&gt; 来创建分页参数即可。&lt;/p&gt;
 &lt;p&gt;得到结果后仅需将分页的内容替换掉实体类的内容即可，并且数据里面包含有获取页码的信息的接口：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image-20221016143118800" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1709100680214e49abeff7c48567142f~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Service层&lt;/h2&gt;
 &lt;p&gt;核心代码如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@Service
public class BlogContentServiceImpl implements BlogContentService {

@Resource
private BlogRepository blogRepository;

/**
 * 搜索博客
 *
 * @param key 搜索内容
 * @param page 页码
 * @param size 页大小
 * @return 搜索到的结果列表
 */
@Override
public List&amp;lt;BlogDoc&amp;gt; searchBlog(String key, int page, int size) {
// 1. 获取数据
SearchPage&amp;lt;BlogDoc&amp;gt; searchPage = blogRepository.findByDescriptiveContent(
// 1.1 设置key和分页，这里是从第0页开始的，所以要-1
key,PageRequest.of(page - 1, size));
// 2. 高亮数据替换
List&amp;lt;SearchHit&amp;lt;BlogDoc&amp;gt;&amp;gt; searchHitList = searchPage.getContent();
ArrayList&amp;lt;BlogDoc&amp;gt; blogDocList = new ArrayList&amp;lt;&amp;gt;(searchHitList.size());
for (SearchHit&amp;lt;BlogDoc&amp;gt; blogHit : searchHitList) {
// 2.1 获取博客数据
BlogDoc blogDoc = blogHit.getContent();
// 2.2 获取高亮数据
Map&amp;lt;String, List&amp;lt;String&amp;gt;&amp;gt; fields = blogHit.getHighlightFields();
if (fields.size() &amp;gt; 0) {
// 2.3 通过反射，将高亮数据替换到原来的博客数据中
BeanMap beanMap = BeanMap.create(blogDoc);
for (String name : fields.keySet()) {
beanMap.put(name, fields.get(name).get(0));
}
}
// 2.4 博客数据插入列表
blogDocList.add(blogDoc);
}
return blogDocList;
}

}
&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;替换高亮数据&lt;/h3&gt;
 &lt;p&gt;到这里其实就只要做一件事了，因为Repository返回的数据已经帮你封装好实体类了，不需要再去json转bean了，它唯一的缺点就是，高亮数据还是得自己去做替换，所以我上面这些代码也就是做了这一件事，就是把高亮的数据替换掉原来的数据。&lt;/p&gt;
 &lt;p&gt;这里我用到了   &lt;em&gt;BeanMap&lt;/em&gt;，代码里不用写死属性名称，相对来说更优雅一点，如果有需要的话，也可以把中间这一段分离成一个单独的方法，可以提供给不同的类使用。&lt;/p&gt;
 &lt;h2&gt;数据同步&lt;/h2&gt;
 &lt;p&gt;数据同步指的是   &lt;em&gt;elasticsearch&lt;/em&gt; 和   &lt;em&gt;MySQL&lt;/em&gt; 的数据同步，由于我的项目做的是微服务架构，我的  &lt;strong&gt;博客服务&lt;/strong&gt;和  &lt;strong&gt;博客内容服务&lt;/strong&gt;是两个微服务（本文讲的是博客内容服务），博客服务提供文章的  &lt;strong&gt;增删改查&lt;/strong&gt;功能，并连接MySQL，博客内容服务提供  &lt;strong&gt;搜索功能&lt;/strong&gt;，并连接ES，故两者的数据需要同步。&lt;/p&gt;
 &lt;p&gt;这里我使用的是RabbitMQ，主要逻辑如下：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;用户新建修改或删除博客时，博客服务发送消息到MQ中，发到自己的交换机里，并指定key。&lt;/li&gt;
  &lt;li&gt;内容服务提前创建队列并绑定到博客服务的交换机中。&lt;/li&gt;
  &lt;li&gt;当内容服务接收到消息时，做出对应的操作。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;核心代码如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;/**
 * 内容服务对博客服务的消息队列监听器
 *
 * @author 阿杆
 * @version 1.0
 * @date 2022/7/10 9:32
 */
@Slf4j
@Component
public class BlogServerListener {

@Resource
private BlogRepository blogRepository;

@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(name = BLOG_EXCHANGE),
value = @Queue(name = BLOG_SAVE_QUEUE),
key = {BLOG_INSERT_KEY, BLOG_UPDATE_KEY}
))
public void saveListener(BlogDoc blogDoc) {
log.debug(&amp;quot;save blogDoc，{}&amp;quot;, blogDoc);
blogRepository.save(blogDoc);
}

@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(name = BLOG_EXCHANGE),
value = @Queue(name = BLOG_DELETE_QUEUE),
key = BLOG_DELETE_KEY
))
public void deleteListener(Long blogId) {
log.debug(&amp;quot;delete blog ,id-&amp;gt;{}&amp;quot;, blogId);
blogRepository.deleteById(blogId);
}

}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;其实可以看出，通过Repository来实现这些操作都是很简单的。&lt;/p&gt;
 &lt;p&gt;需要注意的是，这里的save操作，是  &lt;strong&gt;ES的全量更新&lt;/strong&gt;，所以发送过来的数据，一定要是完整的数据，否则会导致部分字段丢失。&lt;/p&gt;
 &lt;p&gt;然后发送消息的大概就是代码是：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;rabbitTemplate.convertAndSend(BLOG_EXCHANGE, BLOG_UPDATE_KEY, blog);
&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;MQ序列化配置&lt;/h3&gt;
 &lt;p&gt;这里RabbitMQ的序列化配置我也贴一下，这个可以让MQ消息变成json格式的。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;package cn.sticki.common.amqp.autoconfig;

import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 阿杆
 * @version 1.0
 * @date 2022/6/25 18:01
 */
@Configuration
public class AmqpMessageConverterConfig {

@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}

}
&lt;/code&gt;&lt;/pre&gt;
 &lt;h1&gt;后记&lt;/h1&gt;
 &lt;p&gt;本篇文章主要使用了   &lt;em&gt;ElasticsearchRepository&lt;/em&gt; 和相关注解来完成了一些常有的需求，比较优雅（个人认为）的实现了查询分页和高亮的功能（网上找到的教程都没有把分页和高亮一起适配的）。但如果有更为复杂的需求，可能还是需要使用  &lt;em&gt;ElasticsearchRestTemplate&lt;/em&gt;来完成。
官网：  &lt;a href="https://docs.spring.io/spring-data/elasticsearch/docs/4.3.5/reference/html/#elasticsearch.operations.resttemplate"&gt;https://docs.spring.io/spring-data/elasticsearch/docs/4.3.5/reference/html/#elasticsearch.operations.resttemplate&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62458-spring-data-elasticsearch</guid>
      <pubDate>Sun, 16 Oct 2022 15:13:47 CST</pubDate>
    </item>
    <item>
      <title>超级好用的免费开源文件同步工具：Syncthing</title>
      <link>https://itindex.net/detail/61975-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90-%E6%96%87%E4%BB%B6%E5%90%8C%E6%AD%A5</link>
      <description>&lt;p&gt;相信不少小伙伴跟TJ君有一样的经历，当然相信也会有小伙伴是因为更换设备导致的文件丢失，不管怎么说，临时用用还行，平时大家还是不要把重要的文件直接存储在微信上，还是该备份的备份，该传输到本地的传输。存到网盘？你确定不开通个VIP那上传下载速度能用？&lt;/p&gt; &lt;p&gt;那么今天的问题就来了，平时大家都用什么文件传输工具呢？&lt;/p&gt; &lt;p&gt;今天TJ君要和大家分享的就是一款免费、开源的文件同步工具，  &lt;strong&gt;Syncthing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images/pasted-815.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;作为一款P2P性质的文件同步工具，Syncthing在Github上广受欢迎，已经收获了42k的Star！&lt;/p&gt; &lt;p&gt;Syncthing有五大特点：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;1、避免数据丢失&lt;/li&gt;  &lt;li&gt;2、安全防范攻击者&lt;/li&gt;  &lt;li&gt;3、便捷使用&lt;/li&gt;  &lt;li&gt;4、自动化运行&lt;/li&gt;  &lt;li&gt;5、高兼容性&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;像Windwos、macOS、Android、Linux这些主流平台都支持Syncthing，所以Syncthing可以跨平台的进行文件传输。&lt;/p&gt; &lt;p&gt;目前最新的版本是v1.18.5版本，在18天前更新，说明作者也一直在优化迭代。&lt;/p&gt; &lt;p&gt;以windows为例，只需要安装好Syncthing电脑端的应用程序，打开浏览器访问   &lt;a href="http://127.0.0.1:8384/" rel="external nofollow noopener noreferrer" target="_blank"&gt;http://127.0.0.1:8384/&lt;/a&gt; 就可以进入 Syncthing传输平台。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images/pasted-816.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;如果在其他设备，例如手机端也安装完Syncthing，便会有同步提示，通过后就可以进行文件传输了。速度很快哦！&lt;/p&gt; &lt;p&gt;不知道大家有没有从TJ的图中发现，虽然Syncthing的开源项目都是英文描述，但是其使用界面却是中文的，算不算也是给我们大天朝的一个小小的福利呢？想试试这款工具传输文件进行备份的小伙伴们，赶紧来试试吧！&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;扫描下方二维码，关注公众号“TJ君”，回复“Syncthing”，获取仓库地址！&lt;/strong&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>开源推荐 开源</category>
      <guid isPermaLink="true">https://itindex.net/detail/61975-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90-%E6%96%87%E4%BB%B6%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Mon, 27 Dec 2021 10:09:20 CST</pubDate>
    </item>
    <item>
      <title>数据同步工具之FlinkCDC/Canal/Debezium对比-技术圈</title>
      <link>https://itindex.net/detail/61862-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;h2&gt;前言&lt;/h2&gt;  &lt;p&gt;数据准实时复制（CDC）是目前行内实时数据需求大量使用的技术，随着国产化的需求，我们也逐步考虑基于开源产品进行准实时数据同步工具的相关开发，逐步实现对商业产品的替代。本文把市面上常见的几种开源产品，Canal、Debezium、Flink CDC 从原理和适用做了对比，供大家参考。&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;本文首发微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;Debezium&lt;/h3&gt;  &lt;blockquote&gt;    &lt;p&gt;Debezium is an open source distributed platform for change data capture. Start it up, point it at your databases, and your apps can start responding to all of the inserts, updates, and deletes that other apps commit to your databases. Debezium is durable and fast, so your apps can respond quickly and never miss an event, even when things go wrong.&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;Debezium是一种CDC（Change Data Capture）工具，工作原理类似大家所熟知的Canal, DataBus, Maxwell等，是通过抽取数据库日志来获取变更。&lt;/p&gt;  &lt;p&gt;Debezium最初设计成一个Kafka Connect 的Source Plugin，目前开发者虽致力于将其与Kafka Connect解耦，但当前的代码实现还未变动。下图引自Debeizum官方文档，可以看到一个Debezium在一个完整CDC系统中的位置。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/b3a928c97a568c48ebef867672693abc/be52c146afcab849ad5f27b8e7fbd5de.webp"&gt;&lt;/img&gt;  &lt;p&gt;Kafka Connect 为Source Plugin提供了一系列的编程接口，最主要的就是要实现SourceTask的poll方法，其返回    &lt;code&gt;List&amp;lt;SourceRecord&amp;gt;&lt;/code&gt;将会被以最少一次语义的方式投递至Kafka。&lt;/p&gt;  &lt;h4&gt;Debezium MySQL 架构&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/789cad63362a1dfb80efb4c160ed8981/1a195756c164459e3ded9500115c561a.webp"&gt;&lt;/img&gt;Debezium抽取原理  &lt;p&gt;Reader体系构成了MySQL模块中代码的主线，我们的分析从Reader开始。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/ab8da090cad3365d86aa4e660a7909d1/61999f94e706f06115e87c4b633d0dfe.webp"&gt;&lt;/img&gt;Reader继承关系  &lt;p&gt;从名字上应该可以看出，真正主要的是SnapshotReader和BinlogReader，分别实现了对MySQL数据的全量读取和增量读取，他们继承于AbstractReader，里面封装了共用逻辑，下图是AbstractReader的内部设计。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/8b66e9c48aaaa4cab0e4a142796c583e/b014d49d506fc84ee1e8987b0213f361.webp"&gt;&lt;/img&gt;  &lt;p&gt;可以看到，AbstractReader在实现时，并没有直接将enqueue喂进来的record投递进Kafka，而是通过一个内存阻塞队列BlockingQueue进行了解耦，这种设计有诸多好处：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;职责解耦&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;如上的图中，在喂入BlockingQueue之前，要根据条件判断是否接受该record；在向Kafka投递record之前，判断task的running状态。这样把同类的功能限定在特定的位置。&lt;/p&gt;  &lt;ol start="2"&gt;    &lt;li&gt;线程隔离&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;BlockingQueue是一个线程安全的阻塞队列，通过BlockingQueue实现的生产者消费者模型，是可以跑在不同的线程里的，这样避免局部的阻塞带来的整体的干扰。如上图中的右侧，消费者会定期判断running标志位，若running被stop信号置为了false，可以立刻停止整个task,而不会因MySQL IO阻塞延迟相应。&lt;/p&gt;  &lt;ol start="3"&gt;    &lt;li&gt;Single与Batch的互相转化&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;Enqueue record是单条的投递record，drain_to是批量的消费records。这个用法也可以反过来，实现batch到single的转化。&lt;/p&gt;  &lt;p&gt;可能你还知道阿里开源的另一个MySQL CDC工具canal，他只负责stream过程，并没有处理snapshot过程，这也是debezium相较于canal的一个优势。&lt;/p&gt;  &lt;p&gt;对于Debezium来说，基本沿用了官方搭建从库的这一思路，让我们看下官方文档描述的详细步骤。&lt;/p&gt;  &lt;p&gt;MySQL连接器每次获取快照的时候会执行以下的步骤：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;获取一个全局读锁，从而阻塞住其他数据库客户端的写操作。&lt;/li&gt;    &lt;li&gt;开启一个可重复读语义的事务，来保证后续的在同一个事务内读操作都是在一个一致性快照中完成的。&lt;/li&gt;    &lt;li&gt;读取binlog的当前位置。&lt;/li&gt;    &lt;li&gt;读取连接器中配置的数据库和表的模式（schema）信息。&lt;/li&gt;    &lt;li&gt;释放全局读锁，允许其他的数据库客户端对数据库进行写操作。&lt;/li&gt;    &lt;li&gt;（可选）把DDL改变事件写入模式改变topic（schema change topic），包括所有的必要的DROP和CREATEDDL语句。&lt;/li&gt;    &lt;li&gt;扫描所有数据库的表，并且为每一个表产生一个和特定表相关的kafka topic创建事件（即为每一个表创建一个kafka topic）。&lt;/li&gt;    &lt;li&gt;提交事务。&lt;/li&gt;    &lt;li&gt;记录连接器成功完成快照任务时的连接器偏移量。&lt;/li&gt;&lt;/ol&gt;  &lt;h4&gt;部署&lt;/h4&gt;  &lt;h5&gt;基于 Kafka Connect&lt;/h5&gt;  &lt;p&gt;最常见的架构是通过 Apache Kafka Connect 部署 Debezium。Kafka Connect 为在 Kafka 和外部存储系统之间系统数据提供了一种可靠且可伸缩性的方式。它为 Connector 插件提供了一组 API 和一个运行时：Connect 负责运行这些插件，它们则负责移动数据。通过 Kafka Connect 可以快速实现 Source Connector 和 Sink Connector 进行交互构造一个低延迟的数据 Pipeline：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Source Connector（例如，Debezium）：将记录发送到 Kafka&lt;/li&gt;    &lt;li&gt;Sink Connector：将 Kafka Topic 中的记录发送到其他系统&lt;/li&gt;&lt;/ul&gt;  &lt;img src="https://filescdn.proginn.com/1498c2f30dc76c84989f81a1a7029e43/8b4f7c420d88d43fa11a68286d9e77cb.webp"&gt;&lt;/img&gt;  &lt;p&gt;如上图所示，部署了 MySQL 和 PostgresSQL 的 Debezium Connector 以捕获这两种类型数据库的变更。每个 Debezium Connector 都会与其源数据库建立连接：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;MySQL Connector 使用客户端库来访问 binlog。&lt;/li&gt;    &lt;li&gt;PostgreSQL Connector 从逻辑副本流中读取数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;除了 Kafka Broker 之外，Kafka Connect 也作为一个单独的服务运行。默认情况下，数据库表的变更会写入名称与表名称对应的 Kafka Topic 中。如果需要，您可以通过配置 Debezium 的 Topic 路由转换来调整目标 Topic 名称。例如，您可以：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;将记录路由到名称与表名不同的 Topic 中&lt;/li&gt;    &lt;li&gt;将多个表的变更事件记录流式传输到一个 Topic 中&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;变更事件记录在 Apache Kafka 中后，Kafka Connect 生态系统中的不同 Sink Connector 可以将记录流式传输到其他系统、数据库，例如 Elasticsearch、数据仓库、分析系统或者缓存（例如 Infinispan）。&lt;/p&gt;  &lt;h5&gt;Debezium Server&lt;/h5&gt;  &lt;p&gt;另一种部署 Debezium 的方法是使用 Debezium Server。Debezium Server 是一个可配置的、随时可用的应用程序，可以将变更事件从源数据库流式传输到各种消息中间件上。&lt;/p&gt;  &lt;p&gt;下图展示了基于 Debezium Server 的变更数据捕获 Pipeline 架构：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/64e54b6b8ea4238f9e704cfbda80d813/ff16300a88216ec18a497378a7ce2b75.webp"&gt;&lt;/img&gt;  &lt;p&gt;Debezium Server 配置使用 Debezium Source Connector 来捕获源数据库中的变更。变更事件可以序列化为不同的格式，例如 JSON 或 Apache Avro，然后发送到各种消息中间件，例如 Amazon Kinesis、Google Cloud Pub/Sub 或 Apache Pulsar。&lt;/p&gt;  &lt;h5&gt;嵌入式引擎&lt;/h5&gt;  &lt;p&gt;使用 Debezium Connector 的另一种方法是嵌入式引擎。在这种情况下，Debezium 不会通过 Kafka Connect 运行，而是作为嵌入到您自定义 Java 应用程序中的库运行。这对于在您的应用程序本身内获取变更事件非常有帮助，无需部署完整的 Kafka 和 Kafka Connect 集群，也不用将变更流式传输到 Amazon Kinesis 等消息中间件上。&lt;/p&gt;  &lt;h4&gt;特性&lt;/h4&gt;  &lt;p&gt;Debezium 是一组用于 Apache Kafka Connect 的 Source Connector。每个 Connector 都通过使用该数据库的变更数据捕获 (CDC) 功能从不同的数据库中获取变更。与其他方法（例如轮询或双重写入）不同，Debezium 的实现基于日志的 CDC：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;确保捕获所有的数据变更。&lt;/li&gt;    &lt;li&gt;以极低的延迟生成变更事件，同时避免因为频繁轮询导致 CPU 使用率增加。例如，对于 MySQL 或 PostgreSQL，延迟在毫秒范围内。&lt;/li&gt;    &lt;li&gt;不需要更改您的数据模型，例如 ‘Last Updated’ 列。&lt;/li&gt;    &lt;li&gt;可以捕获删除操作。&lt;/li&gt;    &lt;li&gt;可以捕获旧记录状态以及其他元数据，例如，事务 ID，具体取决于数据库的功能和配置。&lt;/li&gt;&lt;/ul&gt;  &lt;h3&gt;Flink CDC&lt;/h3&gt;  &lt;ul&gt;    &lt;li&gt;2020 年 7 月提交了第一个 commit，这是基于个人兴趣孵化的项目；&lt;/li&gt;    &lt;li&gt;2020 年 7 中旬支持了 MySQL-CDC；&lt;/li&gt;    &lt;li&gt;2020 年 7 月末支持了 Postgres-CDC；&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;一年的时间，该项目在 GitHub 上的 star 数已经超过 800。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/6260f47533c281f4f3abc1055df438d7/b7d4fd62a0402da92b208f46fad7abea.webp"&gt;&lt;/img&gt;  &lt;h4&gt;Flink CDC 发展&lt;/h4&gt;  &lt;p&gt;Flink CDC 底层封装了 Debezium， Debezium 同步一张表分为两个阶段：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;全量阶段：查询当前表中所有记录；&lt;/li&gt;    &lt;li&gt;增量阶段：从 binlog 消费变更数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;大部分用户使用的场景都是全量 + 增量同步，加锁是发生在全量阶段，目的是为了确定全量阶段的初始位点，保证增量 + 全量实现一条不多，一条不少，从而保证数据一致性。从下图中我们可以分析全局锁和表锁的一些加锁流程，左边红色线条是锁的生命周期，右边是 MySQL 开启可重复读事务的生命周期。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/8ac9c8448e1c7e07e0275cb68027aed2/9d589e58cc9b5aa1aba4ae14e77ff88f.webp"&gt;&lt;/img&gt;  &lt;p&gt;以全局锁为例，首先是获取一个锁，然后再去开启可重复读的事务。这里锁住操作是读取 binlog 的起始位置和当前表的 schema。这样做的目的是保证 binlog 的起始位置和读取到的当前 schema 是可以对应上的，因为表的 schema 是会改变的，比如如删除列或者增加列。在读取这两个信息后，SnapshotReader 会在可重复读事务里读取全量数据，在全量数据读取完成后，会启动 BinlogReader 从读取的 binlog 起始位置开始增量读取，从而保证全量数据 + 增量数据的无缝衔接。&lt;/p&gt;  &lt;p&gt;表锁是全局锁的退化版，因为全局锁的权限会比较高，因此在某些场景，用户只有表锁。表锁锁的时间会更长，因为表锁有个特征：锁提前释放了可重复读的事务默认会提交，所以锁需要等到全量数据读完后才能释放。&lt;/p&gt;  &lt;p&gt;经过上面分析，接下来看看这些锁到底会造成怎样严重的后果：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/10b5222f24e46b19337dce08b01aa159/7eb734fe6fc7cd5426d24cd799b8e121.webp"&gt;&lt;/img&gt;  &lt;p&gt;Flink CDC 1.x 可以不加锁，能够满足大部分场景，但牺牲了一定的数据准确性。Flink CDC 1.x 默认加全局锁，虽然能保证数据一致性，但存在上述 hang 住数据的风险。&lt;/p&gt;  &lt;p&gt;Flink CDC 1.x得到了很多用户在社区的反馈，主要归纳为三个：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/704e9423a78d7bf7b081d0cd8be1c4de/5485a5ce44ab873d23995005a100af9c.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;全量 + 增量读取的过程需要保证所有数据的一致性，因此需要通过加锁保证，但是加锁在数据库层面上是一个十分高危的操作。底层 Debezium 在保证数据一致性时，需要对读取的库或表加锁，全局锁可能导致数据库锁住，表级锁会锁住表的读，DBA 一般不给锁权限。&lt;/li&gt;    &lt;li&gt;不支持水平扩展，因为 Flink CDC 底层是基于 Debezium，起架构是单节点，所以Flink CDC 只支持单并发。在全量阶段读取阶段，如果表非常大 (亿级别)，读取时间在小时甚至天级别，用户不能通过增加资源去提升作业速度。&lt;/li&gt;    &lt;li&gt;全量读取阶段不支持 checkpoint：CDC 读取分为两个阶段，全量读取和增量读取，目前全量读取阶段是不支持 checkpoint 的，因此会存在一个问题：当我们同步全量数据时，假设需要 5 个小时，当我们同步了 4 小时的时候作业失败，这时候就需要重新开始，再读取 5 个小时。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;通过上面的分析，可以知道 2.0 的设计方案，核心要解决上述的三个问题，即支持无锁、水平扩展、checkpoint。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/62cb88c16be3e82cb1ef7873c5e62314/3fde547f99b55512a2c5f0258a93b421.webp"&gt;&lt;/img&gt;  &lt;p&gt;目前，Flink CDC 2.0 也已经正式发布，此次的核心改进和提升包括：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;并发读取，全量数据的读取性能可以水平扩展；&lt;/li&gt;    &lt;li&gt;全程无锁，不对线上业务产生锁的风险；&lt;/li&gt;    &lt;li&gt;断点续传，支持全量阶段的 checkpoint。&lt;/li&gt;&lt;/ul&gt;  &lt;blockquote&gt;    &lt;p&gt;本文发自微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;Canal&lt;/h3&gt;  &lt;p&gt;canal [kə&amp;apos;næl]，译意为水道/管道/沟渠，主要用途是基于 MySQL 数据库增量日志解析，提供增量数据订阅和消费。&lt;/p&gt;  &lt;p&gt;早期阿里巴巴因为杭州和美国双机房部署，存在跨机房同步的业务需求，实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始，业务逐步尝试数据库日志解析获取增量变更进行同步，由此衍生出了大量的数据库增量订阅和消费业务。&lt;/p&gt;  &lt;p&gt;基于日志增量订阅和消费的业务包括：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;数据库镜像&lt;/li&gt;    &lt;li&gt;数据库实时备份&lt;/li&gt;    &lt;li&gt;索引构建和实时维护(拆分异构索引、倒排索引等)&lt;/li&gt;    &lt;li&gt;业务 cache 刷新&lt;/li&gt;    &lt;li&gt;带业务逻辑的增量数据处理&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;当前的canal支持源端MySQL版本&lt;/strong&gt;包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。&lt;/p&gt;  &lt;h4&gt;工作原理&lt;/h4&gt;  &lt;p&gt;    &lt;strong&gt;MySQL主备复制原理&lt;/strong&gt;&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/53704c9541ae1aa99a71a7e34a56e8b2/80a4dfe48edecc7452f4b3c7f677e64d.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events，可以通过 show binlog events 进行查看)&lt;/li&gt;    &lt;li&gt;MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)&lt;/li&gt;    &lt;li&gt;MySQL slave 重放 relay log 中事件，将数据变更反映它自己的数据&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;canal 工作原理&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal 模拟 MySQL slave 的交互协议，伪装自己为MySQL slave,向MySQL master发送dump协议&lt;/li&gt;    &lt;li&gt;MySQL master收到 dump 请求，开始推送 binary log 给 slave (即 canal )&lt;/li&gt;    &lt;li&gt;canal 解析 binary log 对象(原始为 byte 流)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Binlog获取详解&lt;/h4&gt;  &lt;p&gt;Binlog发送接收流程，流程如下图所示:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/c2ec0b12686363ecb23c75654b2110c4/fd75578e0540fa59a92e525b0d4f889e.webp"&gt;&lt;/img&gt;  &lt;p&gt;首先，我们需要伪造一个slave，向master注册，这样master才会发送binlog event。注册很简单，就是向master发送COM_REGISTER_SLAVE命令，带上slave相关信息。这里需要注意，因为在MySQL的replication topology中，都需要使用一个唯一的server id来区别标示不同的server实例，所以这里我们伪造的slave也需要一个唯一的server id。&lt;/p&gt;  &lt;p&gt;接着实现binlog的dump。MySQL只支持一种binlog dump方式，也就是指定binlog filename + position，向master发送COM_BINLOG_DUMP命令。在发送dump命令的时候，我们可以指定flag为BINLOG_DUMP_NON_BLOCK，这样master在没有可发送的binlog event之后，就会返回一个EOF package。不过通常对于slave来说，一直把连接挂着可能更好，这样能更及时收到新产生的binlog event。&lt;/p&gt;  &lt;p&gt;Dump命令包图如下所示:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/e50bd094fdeca5db8841bce6672f723c/e3e8dcbda217354469e025313b8c6212.webp"&gt;&lt;/img&gt;  &lt;p&gt;如上图所示,在报文中塞入binlogPosition和binlogFileName即可让master从相应的位置发送binlog event。&lt;/p&gt;  &lt;h4&gt;canal结构&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/e3876f7de70c73c957b77e17f8345489/cbcf2513c390aa84ac9fca2bc8a8c27c.webp"&gt;&lt;/img&gt;  &lt;p&gt;说明：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;server代表一个canal运行实例，对应于一个jvm，也可以理解为一个进程&lt;/li&gt;    &lt;li&gt;instance对应于一个数据队列 （1个server对应1..n个instance)，每一个数据队列可以理解为一个数据库实例。&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Server设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/967e1399036e10b41c44bce63d4ffdd6/9954b0ff61456f35965260e13843dfc9.webp"&gt;&lt;/img&gt;  &lt;p&gt;server代表了一个canal的运行实例，为了方便组件化使用，特意抽象了Embeded(嵌入式) / Netty(网络访问)的两种实现&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Embeded : 对latency和可用性都有比较高的要求，自己又能hold住分布式的相关技术(比如failover)&lt;/li&gt;    &lt;li&gt;Netty : 基于netty封装了一层网络协议，由canal server保证其可用性，采用的pull模型，当然latency会稍微打点折扣，不过这个也视情况而定。(阿里系的notify和metaq，典型的push/pull模型，目前也逐步的在向pull模型靠拢，push在数据量大的时候会有一些问题)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Instance设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/0b18a349c4287788b19ea432719aac52/21e2856411588ee8f77ee146ec9a19dd.webp"&gt;&lt;/img&gt;  &lt;p&gt;instance代表了一个实际运行的数据队列，包括了EventPaser,EventSink,EventStore等组件。&lt;/p&gt;  &lt;p&gt;抽象了CanalInstanceGenerator，主要是考虑配置的管理方式：&lt;/p&gt;  &lt;p&gt;manager方式：和你自己的内部web console/manager系统进行对接。(目前主要是公司内部使用，Otter采用这种方式)
spring方式：基于spring xml + properties进行定义，构建spring配置.&lt;/p&gt;  &lt;p&gt;下面是canalServer和instance如何运行：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;canalServer.setCanalInstanceGenerator(new CanalInstanceGenerator() {      &lt;br /&gt;      &lt;br /&gt;            public CanalInstance generate(String destination) {      &lt;br /&gt;                Canal canal = canalConfigClient.findCanal(destination);      &lt;br /&gt;                // 此处省略部分代码 大致逻辑是设置canal一些属性      &lt;br /&gt;      &lt;br /&gt;                CanalInstanceWithManager instance = new CanalInstanceWithManager(canal, filter) {      &lt;br /&gt;      &lt;br /&gt;                    protected CanalHAController initHaController() {      &lt;br /&gt;                        HAMode haMode = parameters.getHaMode();      &lt;br /&gt;                        if (haMode.isMedia()) {      &lt;br /&gt;                            return new MediaHAController(parameters.getMediaGroup(),      &lt;br /&gt;                                parameters.getDbUsername(),      &lt;br /&gt;                                parameters.getDbPassword(),      &lt;br /&gt;                                parameters.getDefaultDatabaseName());      &lt;br /&gt;                        } else {      &lt;br /&gt;                            return super.initHaController();      &lt;br /&gt;                        }      &lt;br /&gt;                    }      &lt;br /&gt;      &lt;br /&gt;                    protected void startEventParserInternal(CanalEventParser parser, boolean isGroup) {      &lt;br /&gt;                        //大致逻辑是 设置支持的类型      &lt;br /&gt;                        //初始化设置MysqlEventParser的主库信息，这处抽象不好，目前只支持mysql      &lt;br /&gt;                    }      &lt;br /&gt;      &lt;br /&gt;                };      &lt;br /&gt;                return instance;      &lt;br /&gt;            }      &lt;br /&gt;        });      &lt;br /&gt;        canalServer.start(); //启动canalServer      &lt;br /&gt;      &lt;br /&gt;        canalServer.start(destination);//启动对应instance      &lt;br /&gt;        this.clientIdentity = new ClientIdentity(destination, pipeline.getParameters().getMainstemClientId(), filter);      &lt;br /&gt;        canalServer.subscribe(clientIdentity);// 发起一次订阅，当监听到instance配置时，调用generate方法注入新的instance      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;instance模块：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;eventParser (数据源接入，模拟slave协议和master进行交互，协议解析)&lt;/li&gt;    &lt;li&gt;eventSink (Parser和Store链接器，进行数据过滤，加工，分发的工作)&lt;/li&gt;    &lt;li&gt;eventStore (数据存储)&lt;/li&gt;    &lt;li&gt;metaManager (增量订阅&amp;amp;消费信息管理器)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;EventParser设计&lt;/h4&gt;  &lt;p&gt;大致过程：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/d230e190bdf6bedafc28dec70fce5f6e/5a369b870f597237e4d0e38afbdf88ea.webp"&gt;&lt;/img&gt;  &lt;p&gt;整个parser过程大致可分为几步：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Connection获取上一次解析成功的位置 (如果第一次启动，则获取初始指定的位置或者是当前数据库的binlog位点)&lt;/li&gt;    &lt;li&gt;Connection建立链接，发送BINLOG_DUMP指令&lt;/li&gt;&lt;/ul&gt;  &lt;pre&gt;    &lt;code&gt;// 0. write command number      &lt;br /&gt;// 1. write 4 bytes bin-log position to start at      &lt;br /&gt;// 2. write 2 bytes bin-log flags      &lt;br /&gt;// 3. write 4 bytes server id of the slave      &lt;br /&gt;// 4. write bin-log file name      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;ul&gt;    &lt;li&gt;Mysql开始推送Binaly Log&lt;/li&gt;    &lt;li&gt;接收到的Binaly Log的通过Binlog parser进行协议解析，补充一些特定信息(补充字段名字，字段类型，主键信息，unsigned类型处理)&lt;/li&gt;    &lt;li&gt;传递给EventSink模块进行数据存储，是一个阻塞操作，直到存储成功&lt;/li&gt;    &lt;li&gt;存储成功后，由CanalLogPositionManager定时记录Binaly Log位置&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;EventSink设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/89e1cadc45ebb42b3f6ebfd04d08ab26/8679b22f6ffdd35e7879b639de3a1bbb.webp"&gt;&lt;/img&gt;  &lt;p&gt;说明：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;数据过滤：支持通配符的过滤模式，表名，字段内容等&lt;/li&gt;    &lt;li&gt;数据路由/分发：解决1:n (1个parser对应多个store的模式)&lt;/li&gt;    &lt;li&gt;数据归并：解决n:1 (多个parser对应1个store)&lt;/li&gt;    &lt;li&gt;数据加工：在进入store之前进行额外的处理，比如join&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;数据1:n业务&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了合理的利用数据库资源， 一般常见的业务都是按照schema进行隔离，然后在mysql上层或者dao这一层面上，进行一个数据源路由，屏蔽数据库物理位置对开发的影响，阿里系主要是通过cobar/tddl来解决数据源路由问题。&lt;/p&gt;  &lt;p&gt;所以，一般一个数据库实例上，会部署多个schema，每个schema会有由1个或者多个业务方关注。&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;数据n:1业务&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;同样，当一个业务的数据规模达到一定的量级后，必然会涉及到水平拆分和垂直拆分的问题，针对这些拆分的数据需要处理时，就需要链接多个store进行处理，消费的位点就会变成多份，而且数据消费的进度无法得到尽可能有序的保证。&lt;/p&gt;  &lt;p&gt;所以，在一定业务场景下，需要将拆分后的增量数据进行归并处理，比如按照时间戳/全局id进行排序归并。&lt;/p&gt;  &lt;h4&gt;EventStore设计&lt;/h4&gt;  &lt;ol&gt;    &lt;li&gt;目前仅实现了Memory内存模式，后续计划增加本地file存储，mixed混合模式。&lt;/li&gt;    &lt;li&gt;借鉴了Disruptor的RingBuffer的实现思路&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;RingBuffer设计：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/c07a36d6234b06daadce941f0cc3e757/3bd0c7600cfb45d05c6826834d40c057.webp"&gt;&lt;/img&gt;  &lt;p&gt;定义了3个cursor&lt;/p&gt;  &lt;p&gt;Put : Sink模块进行数据存储的最后一次写入位置
Get : 数据订阅获取的最后一次提取位置
Ack : 数据消费成功的最后一次消费位置&lt;/p&gt;  &lt;p&gt;借鉴Disruptor的RingBuffer的实现，将RingBuffer拉直来看：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/b32902a1df77414a2a5056ef09c64688/139557144a8c05ea6822e49ab70dea1f.webp"&gt;&lt;/img&gt;  &lt;p&gt;实现说明：&lt;/p&gt;  &lt;p&gt;Put/Get/Ack cursor用于递增，采用long型存储buffer的get操作，通过取余或者与操作。(与操作：cusor &amp;amp; (size - 1) , size需要为2的指数，效率比较高)&lt;/p&gt;  &lt;h4&gt;HA机制设计&lt;/h4&gt;  &lt;p&gt;canal的ha分为两部分，canal server和canal client分别有对应的ha实现&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal server: 为了减少对mysql dump的请求，不同server上的instance要求同一时间只能有一个处于running，其他的处于standby状态.&lt;/li&gt;    &lt;li&gt;canal client: 为了保证有序性，一份instance同一时间只能由一个canal client进行get/ack/rollback操作，否则客户端接收无法保证有序。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;整个HA机制的控制主要是依赖了zookeeper的几个特性，watcher和EPHEMERAL节点(和session生命周期绑定)，可以看下我之前zookeeper的相关文章。&lt;/p&gt;  &lt;p&gt;Canal Server:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/5a507f75139c733a39ec1ab2ecd34bf9/942851ac3b9c84f0f148e727c6785792.webp"&gt;&lt;/img&gt;  &lt;p&gt;大致步骤：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal server要启动某个canal instance时都先向zookeeper进行一次尝试启动判断 (实现：创建EPHEMERAL节点，谁创建成功就允许谁启动)&lt;/li&gt;    &lt;li&gt;创建zookeeper节点成功后，对应的canal server就启动对应的canal instance，没有创建成功的canal instance就会处于standby状态&lt;/li&gt;    &lt;li&gt;一旦zookeeper发现canal server A创建的节点消失后，立即通知其他的canal server再次进行步骤1的操作，重新选出一个canal server启动instance&lt;/li&gt;    &lt;li&gt;canal client每次进行connect时，会首先向zookeeper询问当前是谁启动了canal instance，然后和其建立链接，一旦链接不可用，会重新尝试connect&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;Canal Client的方式和canal server方式类似，也是利用zookeeper的抢占EPHEMERAL节点的方式进行控制。&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;本文发自微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;总结&lt;/h3&gt;  &lt;p&gt;CDC 的技术方案非常多，目前业界主流的实现机制可以分为两种：&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;基于查询的 CDC：&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;离线调度查询作业，批处理。把一张表同步到其他系统，每次通过查询去获取表中最新的数据；&lt;/li&gt;    &lt;li&gt;无法保障数据一致性，查的过程中有可能数据已经发生了多次变更；&lt;/li&gt;    &lt;li&gt;不保障实时性，基于离线调度存在天然的延迟。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;基于日志的 CDC：&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;实时消费日志，流处理，例如 MySQL 的 binlog 日志完整记录了数据库中的变更，可以把 binlog 文件当作流的数据源；&lt;/li&gt;    &lt;li&gt;保障数据一致性，因为 binlog 文件包含了所有历史变更明细；&lt;/li&gt;    &lt;li&gt;保障实时性，因为类似 binlog 的日志文件是可以流式消费的，提供的是实时数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;对比常见的开源 CDC 方案，我们可以发现：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/fe533b3f620f580d4a2650a44258df98/b6dfa94cd1049c800f5d38a79bcc5f65.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;p&gt;对比增量同步能力:&lt;/p&gt;      &lt;pre&gt;        &lt;code&gt;- 基于日志的方式，可以很好的做到增量同步；          &lt;br /&gt;- 而基于查询的方式是很难做到增量同步的。          &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;对比全量同步能力，基于查询或者日志的 CDC 方案基本都支持，除了 Canal。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;而对比全量 + 增量同步的能力，只有 Flink CDC、Debezium、Oracle Goldengate 支持较好。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;从架构角度去看，该表将架构分为单机和分布式，这里的分布式架构不单纯体现在数据读取能力的水平扩展上，更重要的是在大数据场景下分布式系统接入能力。例如 Flink CDC 的数据入湖或者入仓的时候，下游通常是分布式的系统，如 Hive、HDFS、Iceberg、Hudi 等，那么从对接入分布式系统能力上看，Flink CDC 的架构能够很好地接入此类系统。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;在数据转换 / 数据清洗能力上，当数据进入到 CDC 工具的时候是否能较方便的对数据做一些过滤或者清洗，甚至聚合？&lt;/p&gt;&lt;/li&gt;    &lt;ul&gt;      &lt;li&gt;在 Flink CDC 上操作相当简单，可以通过 Flink SQL 去操作这些数据；&lt;/li&gt;      &lt;li&gt;但是像 DataX、Debezium 等则需要通过脚本或者模板去做，所以用户的使用门槛会比较高。&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;  &lt;p&gt;另外，在生态方面，这里指的是下游的一些数据库或者数据源的支持。Flink CDC 下游有丰富的 Connector，例如写入到 TiDB、MySQL、Pg、HBase、Kafka、ClickHouse 等常见的一些系统，也支持各种自定义 connector。&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61862-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Sun, 31 Oct 2021 12:10:29 CST</pubDate>
    </item>
    <item>
      <title>基于Binlog的实时同步功能——debezium、canel、databus技术选型 | holmofy</title>
      <link>https://itindex.net/detail/61859-binlog-%E5%AE%9E%E6%97%B6-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;    &lt;p&gt;   &lt;/p&gt;  &lt;p&gt;       &lt;a href="https://blog.hufeifei.cn/2020/04/25/Alibaba/MetaQ&amp;Notify/"&gt;去年的一篇文章大致地讲了我对MQ的一些认识&lt;/a&gt;，事实上Kafka在内的现代MQ，功能远不止这些。后面整理好自己的思路，肯定会再写一篇文章来讲讲。这篇文章的主角就是与MQ息息相关的CDC技术。&lt;/p&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#1-cdc-ji-zhu"&gt;#&lt;/a&gt;1. CDC技术&lt;/h1&gt;     &lt;p&gt;       &lt;a href="https://en.wikipedia.org/wiki/Change_data_capture" rel="noopener" target="_blank"&gt;CDC&lt;/a&gt;全称叫：change data capture，是一种基于数据库数据变更的事件型软件设计模式。&lt;/p&gt;     &lt;p&gt;比如有一张订单表trade，订单每一次变更录入到一张trade_change的队列表。然后另外一个调度线程可以消费trade_change这张队列表来做一些数据统计，如每日的付款用户统计、每日的下单用户统计等。&lt;/p&gt;     &lt;p&gt;这就是我毕业入职的第一家公司的报表统计逻辑。这个设计在订单量小的时候是看不出问题的，而一旦某一时刻订单量增多。基于MySQL的队列表由于B+树的写入吞吐量不够，导致MySQL CPU经常飙升。比如双十一，618这样的大促，程序员就得在颤颤巍巍中度过。&lt;/p&gt;     &lt;p&gt;其次，从MySQL同步到ElaticSearch是根据       &lt;code&gt;last_modify_time&lt;/code&gt;时间扫索引增量同步的，这就要求表上必须创建       &lt;code&gt;last_modify_time&lt;/code&gt;索引，Scheduler一多也会无形地增加MySQL的读取负担。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;B+的写入性能肯定是不如直接顺序写文件的，B+树的本质就是牺牲写性能，换取磁盘上的随机读的查找结构，所以大部分数据库都会设计         &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html" rel="noopener" target="_blank"&gt;Buffer Pool&lt;/a&gt;来管理B+树脏页，以避免频繁的随机IO。         &lt;br /&gt;同时为了防止Buffer数据丢失同时为了保证事务的ACID，所以就有了         &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html" rel="noopener" target="_blank"&gt;Redo-log&lt;/a&gt;来进行崩溃恢复，         &lt;a href="https://dev.mysql.com/doc/refman/5.6/en/innodb-undo-logs.html" rel="noopener" target="_blank"&gt;Undo-log&lt;/a&gt;来做未提交事务的撤销。这些日志都是顺序写入，远比B+树的随机写性能高。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;       &lt;img alt="database architecture" src="https://p.pstatp.com/origin/pgc-image/85c4945860ba4dd793acc42691226c8a"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#2-ji-yu-binlog-de-cdc"&gt;#&lt;/a&gt;2. 基于Binlog的CDC&lt;/h1&gt;     &lt;p&gt;       &lt;a href="https://dev.mysql.com/doc/internals/en/binary-log.html" rel="noopener" target="_blank"&gt;Binlog&lt;/a&gt;是MySQL 3.23.14引进的，       &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/binary-log.html" rel="noopener" target="_blank"&gt;它包含所有的描述数据库修改的事件&lt;/a&gt;——DML(增删改)、DDL(表结构定义与修改)操作。&lt;/p&gt;     &lt;p&gt;       &lt;img alt="MySQL architecture" src="https://p.pstatp.com/origin/pgc-image/f20dc094cd6a4163829e616e02197acd"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;与InnoDB中的       &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html" rel="noopener" target="_blank"&gt;redo-log&lt;/a&gt;、       &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-logs.html" rel="noopener" target="_blank"&gt;undo-log&lt;/a&gt;不同，binlog和       &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/slow-query-log.html" rel="noopener" target="_blank"&gt;slow_query_log&lt;/a&gt;一样是server层的日志，所以InnoDB和MyISAM等各种存储引擎的数据修改都会记录到这个日志中。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;MySQL拥有分层架构，支持可插拔的存储引擎，所以服务层的binlog与         &lt;a href="https://mysqlserverteam.com/mysql-8-0-new-lock-free-scalable-wal-design/" rel="noopener" target="_blank"&gt;InnoDB引擎的redo-log&lt;/a&gt;是不同的两个事物，这也是为什么MySQL支持以         &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/binary-log-setting.html" rel="noopener" target="_blank"&gt;           &lt;code&gt;STATEMENT&lt;/code&gt;格式&lt;/a&gt;直接将sql语句存入binlog。而像PostgreSQL这样的数据库，         &lt;a href="https://www.postgresql.org/docs/8.1/wal-intro.html" rel="noopener" target="_blank"&gt;WAL日志&lt;/a&gt;除了作为redo-log用于保证事务的持久性外，WAL日志在Replica过程中也扮演着与MySQL的binlog相同的角色, 但是需要用         &lt;a href="https://www.postgresql.org/docs/9.4/logicaldecoding-explanation.html" rel="noopener" target="_blank"&gt;Logical Decoding&lt;/a&gt;将WAL日志解析成数据流或SQL语句。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;       &lt;img alt="CDC architecture" src="https://debezium.io/documentation/reference/1.4/_images/debezium-architecture.png"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;对于CDC的架构设计，在大数据量的分布式场景下，我们都是使用binlog来做事件源。&lt;/p&gt;     &lt;p&gt;一方面，将binlog复制到Kafka，再由Kafka下游的消费者处理这些事件不影响数据库的核心业务，可以降低系统的耦合度；&lt;/p&gt;     &lt;p&gt;另一方面，binlog和Kafka都是基于日志的顺序写入，Kafka的吞吐量远比B+树高，系统的整体性能也能得到改善。&lt;/p&gt;     &lt;p&gt;目前基于binlog的CDC技术已经很成熟了，在github上也有很多实现，通过       &lt;a href="https://github.com/search?q=change+data+capture&amp;s=stars" rel="noopener" target="_blank"&gt;Change Data Capture&lt;/a&gt;、       &lt;a href="https://github.com/search?q=replication&amp;s=stars" rel="noopener" target="_blank"&gt;replication&lt;/a&gt;、       &lt;a href="https://github.com/search?q=binlog&amp;s=stars" rel="noopener" target="_blank"&gt;binlog&lt;/a&gt;等关键词可以搜索到相关项目。在此列举一下：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;th&gt;Project&lt;/th&gt;         &lt;th&gt;Language&lt;/th&gt;         &lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/alibaba/canal" rel="noopener" target="_blank"&gt;alibaba/Canal&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/alibaba/canal"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;阿里巴巴 MySQL binlog 增量订阅&amp;amp;消费组件&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/debezium/debezium" rel="noopener" target="_blank"&gt;debezium/debezium&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/debezium/debezium"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;Debezium is an open source distributed platform for change data capture. Replicates from MySQL to Kafka. Uses mysql-binlog-connector-java. Kafka Connector. A funded project supported by Redhat with employees working on it full time.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/linkedin/databus" rel="noopener" target="_blank"&gt;linkedin/databus&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/linkedin/databus"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;Precursor to Kafka. Reads from MySQL and Oracle, and replicates to its own log structure. In production use at LinkedIn. No Kafka integration. Uses Open Replicator.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/zendesk/maxwell" rel="noopener" target="_blank"&gt;zendesk/Maxwell&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/zendesk/maxwell"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;Reads MySQL event stream, output events as JSON. Parses ALTER/CREATE TABLE/etc statements to keep schema in sync. Written in java. Well maintained.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/noplay/python-mysql-replication" rel="noopener" target="_blank"&gt;noplay/python-mysql-replication&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/noplay/python-mysql-replication"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Python&lt;/td&gt;         &lt;td&gt;Pure python library that parses MySQL binary logs and lets you process the replication events. Basically, the python equivalent of mysql-binlog-connector-java&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/shyiko/mysql-binlog-connector-java" rel="noopener" target="_blank"&gt;shyiko/mysql-binlog-connector-java&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/shyiko/mysql-binlog-connector-java"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;Library that parses MySQL binary logs and calls your code to process them. Fork/rewrite of Open Replicator. Has tests.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/confluentinc/bottledwater-pg" rel="noopener" target="_blank"&gt;confluentinc/bottledwater-pg&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/confluentinc/bottledwater-pg"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;C&lt;/td&gt;         &lt;td&gt;Change data capture from PostgreSQL into Kafka&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/uber/storagetapper" rel="noopener" target="_blank"&gt;uber/storagetapper&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/uber/storagetapper"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Go&lt;/td&gt;         &lt;td&gt;StorageTapper is a scalable realtime MySQL change data streaming, logical backup and logical replication service&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/moiot/gravity" rel="noopener" target="_blank"&gt;moiot/gravity&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/moiot/gravity"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Go&lt;/td&gt;         &lt;td&gt;A Data Replication Center&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/whitesock/open-replicator" rel="noopener" target="_blank"&gt;whitesock/open-replicator&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/whitesock/open-replicator"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;Open Replicator is a high performance MySQL binlog parser written in Java. It unfolds the possibilities that you can parse, filter and broadcast the binlog events in a real time manner.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/mardambey/mypipe" rel="noopener" target="_blank"&gt;mardambey/mypipe&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/mardambey/mypipe"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Scala&lt;/td&gt;         &lt;td&gt;Reads MySQL event stream, and emits events corresponding to INSERTs, DELETEs, UPDATEs. Written in Scala. Emits Avro to Kafka.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/Yelp/mysql_streamer" rel="noopener" target="_blank"&gt;Yelp/mysql_streamer&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/Yelp/mysql_streamer"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Python&lt;/td&gt;         &lt;td&gt;MySQLStreamer is a database change data capture and publish system. It’s responsible for capturing each individual database change, enveloping them into messages and publishing to Kafka.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/actiontech/dtle" rel="noopener" target="_blank"&gt;actiontech/dtle&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/actiontech/dtle"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Go&lt;/td&gt;         &lt;td&gt;Distributed Data Transfer Service for MySQL&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/krowinski/php-mysql-replication" rel="noopener" target="_blank"&gt;krowinski/php-mysql-replication&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/krowinski/php-mysql-replication"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;PHP&lt;/td&gt;         &lt;td&gt;Pure PHP Implementation of MySQL replication protocol. This allow you to receive event like insert, update, delete with their data and raw SQL queries.&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/dianping/puma" rel="noopener" target="_blank"&gt;dianping/puma&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/dianping/puma"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Java&lt;/td&gt;         &lt;td&gt;本系统还会实现数据库同步（同构和异构），以满足数据库冗余备份，数据迁移的需求。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;a href="https://github.com/JarvusInnovations/lapidus" rel="noopener" target="_blank"&gt;JarvusInnovations/Lapidus&lt;/a&gt;           &lt;img src="https://img.shields.io/github/stars/JarvusInnovations/Lapidus"&gt;&lt;/img&gt;&lt;/td&gt;         &lt;td&gt;Javascript&lt;/td&gt;         &lt;td&gt;Streams data from MySQL, PostgreSQL and MongoDB as newline delimited JSON. Can be run as a daemon or included as a Node.js module.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;这里只讨论Java语言的几个实现。首先       &lt;a href="https://github.com/whitesock/open-replicator" rel="noopener" target="_blank"&gt;whitesock/open-replicator&lt;/a&gt;和       &lt;a href="https://github.com/shyiko/mysql-binlog-connector-java" rel="noopener" target="_blank"&gt;shyiko/mysql-binlog-connector-java&lt;/a&gt;是专门用来解析MySQL binlog的库，后者也是在前者的基础上重构的。       &lt;a href="https://github.com/debezium/debezium" rel="noopener" target="_blank"&gt;debezium/debezium&lt;/a&gt;、       &lt;a href="https://github.com/linkedin/databus" rel="noopener" target="_blank"&gt;linkedin/databus&lt;/a&gt;、       &lt;a href="https://github.com/zendesk/maxwell" rel="noopener" target="_blank"&gt;zendesk/Maxwell&lt;/a&gt;三个中间件binlog解析都是基于这两个库。&lt;/p&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#3-canal-vs-debezium-vs-databus-vs-maxwell"&gt;#&lt;/a&gt;3. Canal vs. Debezium vs. databus vs. MaxWell&lt;/h1&gt;     &lt;p&gt;1、       &lt;a href="https://github.com/alibaba/canal" rel="noopener" target="_blank"&gt;alibaba/Canal&lt;/a&gt;       &lt;img src="https://img.shields.io/github/stars/alibaba/canal"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;优点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;阿里开源，有大厂实践背书&lt;/li&gt;       &lt;li&gt;资料大都是中文的，方便学习&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;缺点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;定位于MySQL binlog解析，所以只能支持MySQL数据库的CDC&lt;/li&gt;       &lt;li&gt;Github上项目活跃度很一般，issue堆积了太多，13、14年的问题都还没解决。&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;2、       &lt;a href="https://github.com/debezium/debezium" rel="noopener" target="_blank"&gt;debezium/debezium&lt;/a&gt;       &lt;img src="https://img.shields.io/github/stars/debezium/debezium"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;优点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;Rethat开源，专干开源的国际大厂背书&lt;/li&gt;       &lt;li&gt;支持MySQL、PostgreSQL、Oracle、SqlServer、MongoDB主流数据库&lt;/li&gt;       &lt;li&gt;         &lt;a href="https://debezium.io/documentation/" rel="noopener" target="_blank"&gt;文档&lt;/a&gt;详细，资料齐全&lt;/li&gt;       &lt;li&gt;社区完善，在         &lt;a href="https://gitter.im/debezium/user" rel="noopener" target="_blank"&gt;Gitter&lt;/a&gt;上有专门的问题讨论区。&lt;/li&gt;       &lt;li&gt;与Kafka很好集成，可作为Kafka Connector插件使用，         &lt;a href="https://debezium.io/documentation/reference/1.4/development/engine.html" rel="noopener" target="_blank"&gt;embed模式&lt;/a&gt;支持嵌入自己的程序方便控制，也支持         &lt;a href="https://debezium.io/documentation/reference/1.4/operations/debezium-server.html" rel="noopener" target="_blank"&gt;Server模式&lt;/a&gt;单独运行。&lt;/li&gt;       &lt;li&gt;支持SMT消息体转换，OpenTracing分布式链路追踪等集成功能&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;缺点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;文档大多数是英文的，得多花点耐心&lt;/li&gt;&lt;/ul&gt;     &lt;blockquote&gt;       &lt;p&gt;有意思的是阿里开源的         &lt;a href="https://ci.apache.org/projects/flink/flink-docs-stable/dev/table/connectors/formats/debezium.html" rel="noopener" target="_blank"&gt;Flink流处理&lt;/a&gt;系统也是使用Debezium来做CDC，当然它还支持Canel、Maxwell&lt;/p&gt;       &lt;p&gt;Kafka创始人创办的         &lt;a href="https://github.com/confluentinc" rel="noopener" target="_blank"&gt;confluentinc&lt;/a&gt;刚开始开源了         &lt;a href="https://github.com/confluentinc/bottledwater-pg" rel="noopener" target="_blank"&gt;bottledwater-pg&lt;/a&gt;，最后也投入了debezium的怀抱，有官方的认可。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;3、       &lt;a href="https://github.com/linkedin/databus" rel="noopener" target="_blank"&gt;linkedin/databus&lt;/a&gt;       &lt;img src="https://img.shields.io/github/stars/linkedin/databus"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;优点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;国际大厂领英开源&lt;/li&gt;       &lt;li&gt;支持MySQL和Oracle&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;缺点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;项目已经很久没有人维护了&lt;/li&gt;       &lt;li&gt;         &lt;a href="https://github.com/linkedin/databus/wiki/_pages" rel="noopener" target="_blank"&gt;文档&lt;/a&gt;也很一般&lt;/li&gt;       &lt;li&gt;暂时不支持Kafka集成，只能用         &lt;a href="https://github.com/linkedin/databus/wiki/Databus-2.0-Client" rel="noopener" target="_blank"&gt;Databus Client&lt;/a&gt;消费binlog。&lt;/li&gt;&lt;/ul&gt;     &lt;blockquote&gt;       &lt;p&gt;Kafka最早是         &lt;a href="https://www.linkedin.com/in/jaykreps" rel="noopener" target="_blank"&gt;Jay Kreps&lt;/a&gt;在领英创建并开源的，可能是Jay Kreps觉得Kafka在大数据领域大有可图，所以就带着Linkedin的几个工程师一起创立了         &lt;a href="https://www.confluent.io/about/" rel="noopener" target="_blank"&gt;Confluent&lt;/a&gt;​专注于Kafka生态的开发与维护。&lt;/p&gt;       &lt;p&gt;在Kafka文档         &lt;a href="https://kafka.apache.org/documentation/#compaction" rel="noopener" target="_blank"&gt;Log-Compact一节&lt;/a&gt;可以看到这段话：&lt;/p&gt;       &lt;p&gt;This functionality is inspired by one of LinkedIn’s oldest and most successful pieces of infrastructure—a database changelog caching service called         &lt;a href="https://github.com/linkedin/databus" rel="noopener" target="_blank"&gt;Databus&lt;/a&gt;. Unlike most log-structured storage systems Kafka is built for subscription and organizes data for fast linear reads and writes. Unlike Databus, Kafka acts as a source-of-truth store so it is useful even in situations where the upstream data source would not otherwise be replayable.&lt;/p&gt;       &lt;p&gt;可以看出Databus是Linkedin非常老的一个基础服务，Kafka的Log Compact的一些设计也源自于Databus。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;4、       &lt;a href="https://github.com/zendesk/maxwell" rel="noopener" target="_blank"&gt;zendesk/maxwell&lt;/a&gt;       &lt;img src="https://img.shields.io/github/stars/zendesk/maxwell"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;优点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;相当简单，下载下来，简单进行配置就能运行&lt;/li&gt;       &lt;li&gt;         &lt;a href="http://maxwells-daemon.io/quickstart/" rel="noopener" target="_blank"&gt;文档&lt;/a&gt;相对来说，还算齐全&lt;/li&gt;       &lt;li&gt;支持Kafka、RabbitMQ、Redis等队列&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;缺点：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;文档是英文的，不过好在maxwell相对简单。&lt;/p&gt;&lt;/li&gt;       &lt;li&gt;         &lt;p&gt;没啥明显缺点。非要说个缺点，就是和前三者比身份不够显赫，zendesk这家美国公司没怎么听过。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;     &lt;blockquote&gt;       &lt;p&gt;综合下来，Debezium是最佳选择。&lt;/p&gt;&lt;/blockquote&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#4-debezimu-mysql-de-pei-zhi"&gt;#&lt;/a&gt;4. Debezimu-MySQL的配置&lt;/h1&gt;     &lt;p&gt;要使用debezium需要       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#setting-up-mysql" rel="noopener" target="_blank"&gt;预先对mysql服务进行配置&lt;/a&gt;。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#4-1-mysql-pei-zhi"&gt;#&lt;/a&gt;4.1. MySQL配置&lt;/h2&gt;     &lt;p&gt;       &lt;strong&gt;1）&lt;/strong&gt;创建单独的用户，并授予debezium需要的权限&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;mysql&amp;gt;CREATEUSER&amp;apos;user&amp;apos;@&amp;apos;localhost&amp;apos;IDENTIFIEDBY&amp;apos;password&amp;apos;;             &lt;br /&gt;mysql&amp;gt;GRANTSELECT, RELOAD,SHOWDATABASES, REPLICATION SLAVE, REPLICATION CLIENTON*.*TO&amp;apos;user&amp;apos;IDENTIFIEDBY&amp;apos;password&amp;apos;;             &lt;br /&gt;mysql&amp;gt;FLUSH PRIVILEGES;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;blockquote&gt;       &lt;p&gt;MySQL提供的权限：         &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html" rel="noopener" target="_blank"&gt;https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;debezium需要几个权限的作用：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;th align="left"&gt;Keyword&lt;/th&gt;         &lt;th align="left"&gt;Description&lt;/th&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;SELECT&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;           &lt;code&gt;SELECT&lt;/code&gt;查询权限。只被用在初始化阶段。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;RELOAD&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;执行           &lt;code&gt;FLUSH&lt;/code&gt;语句清除重新加载内部缓存。只被用在初始化阶段。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;SHOW DATABASES&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;执行           &lt;code&gt;SHOW DATABASE&lt;/code&gt;语句。只被用在初始化阶段。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;REPLICATION SLAVE&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;读取MySQL binlog。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;REPLICATION CLIENT&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;执行           &lt;code&gt;SHOW MASTER STATUS&lt;/code&gt;、           &lt;code&gt;SHOW SLAVE STATUS&lt;/code&gt;、           &lt;code&gt;SHOW BINARY LOGS&lt;/code&gt;等语句。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;       &lt;strong&gt;2）&lt;/strong&gt;开启MySQL服务的binlog功能&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;server-id=223344             &lt;br /&gt;log_bin=mysql-bin             &lt;br /&gt;binlog_format=ROW             &lt;br /&gt;binlog_row_image=FULL             &lt;br /&gt;expire_logs_days=10             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;各项配置的作用：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;th align="left"&gt;Property&lt;/th&gt;         &lt;th align="left"&gt;Description&lt;/th&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;server-id&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;在MySQL集群中每个server和replication的           &lt;code&gt;server-id&lt;/code&gt;必须是唯一的。Debezium是作为MySQL的replication，启动后也会分配一个           &lt;code&gt;server-id&lt;/code&gt;给debezium-connector。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;log_bin&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;binlog文件的前缀&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;binlog_format&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;           &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_format" rel="noopener" target="_blank"&gt;             &lt;code&gt;binlog-format&lt;/code&gt;&lt;/a&gt;必须设置成           &lt;code&gt;ROW&lt;/code&gt;模式。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;binlog_row_image&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;           &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_row_image" rel="noopener" target="_blank"&gt;             &lt;code&gt;binlog_row_image&lt;/code&gt;&lt;/a&gt;必须设置成           &lt;code&gt;FULL&lt;/code&gt;。           &lt;code&gt;ROW&lt;/code&gt;模式下binlog需要记录所有的列。&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td align="left"&gt;           &lt;code&gt;expire_logs_days&lt;/code&gt;&lt;/td&gt;         &lt;td align="left"&gt;binlog的过期时间。默认位           &lt;code&gt;0&lt;/code&gt;, 意味着不会自动删除。这个值可根据自己的环境需求进行设置。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;blockquote&gt;       &lt;p&gt;mysql的         &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/binary-log-formats.html" rel="noopener" target="_blank"&gt;binlog有三种模式&lt;/a&gt;：         &lt;br /&gt;         &lt;code&gt;STATEMENT&lt;/code&gt;模式只记录SQL语句，从节点通过执行同步过来的sql在从库中再执行一遍。         &lt;code&gt;STATEMENT&lt;/code&gt;模式的问题是有些语句(比如         &lt;code&gt;update t set num=num+1 limit 1&lt;/code&gt;)可能会产生不一致性，而且         &lt;code&gt;STATEMENT&lt;/code&gt;模式下sql发给异构系统将会无法使用。         &lt;br /&gt;         &lt;code&gt;ROW&lt;/code&gt;模式会直接复制修改的数据行，但是有可能会导致日志量过大，比如执行一条         &lt;code&gt;update t set num=num+1&lt;/code&gt;，修改了一万行就会有一万行日志，肯定没有         &lt;code&gt;STATEMENT&lt;/code&gt;模式来的快。         &lt;br /&gt;         &lt;code&gt;MIXED&lt;/code&gt;模式，则将两者结合，默认情况下使用statement，某些情况会切换为基于行的复制。         &lt;br /&gt;具体可以参考         &lt;a href="https://serverfault.com/questions/212549/which-binlog-format-to-use-for-mysql-replication/359889#359889" rel="noopener" target="_blank"&gt;这个回答&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;     &lt;blockquote&gt;       &lt;p&gt;还有几项可选配置项：&lt;/p&gt;       &lt;ul&gt;         &lt;li&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#enable-mysql-gtids" rel="noopener" target="_blank"&gt;开启全局事务ID(GTIDs)&lt;/a&gt;方便确认主从备份切换时之间的数据一致性。MySQL有主从切换就不能用binlog物理位置来标识binlog消费offset了，此时需要用           &lt;a href="https://stackoverflow.com/questions/23485871/why-use-gtids-in-mysql-replication/23581370#23581370" rel="noopener" target="_blank"&gt;全局的gtid&lt;/a&gt;。&lt;/li&gt;         &lt;li&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-session-timeouts" rel="noopener" target="_blank"&gt;配置MySQL会话超时时间&lt;/a&gt;用于大表的快照读阶段。&lt;/li&gt;         &lt;li&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#enable-query-log-events" rel="noopener" target="_blank"&gt;开启原始SQL语句的记录&lt;/a&gt;用于查看每条binlog记录的原始SQL。&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#4-2-zhun-bei-kafka-huan-jing-zai-kafka-connect-zhong-an-zhuang-debezium"&gt;#&lt;/a&gt;4.2. 准备Kafka环境，在Kafka-connect中安装Debezium&lt;/h2&gt;     &lt;blockquote&gt;       &lt;p&gt;         &lt;a href="https://kafka.apache.org/" rel="noopener" target="_blank"&gt;Kafka&lt;/a&gt;需要依赖         &lt;a href="https://zookeeper.apache.org/" rel="noopener" target="_blank"&gt;Zookeeper&lt;/a&gt;管理集群，所以还需要准备zookeeper环境。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;1）下载Debezium：       &lt;a href="https://debezium.io/releases/" rel="noopener" target="_blank"&gt;https://debezium.io/releases/&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;2）配置Kafka-connect插件路径，并将Debezimu插件解压到该目录&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;plugin.path=/kafka/connect             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;3）启动Kafka-connect进程：&lt;/p&gt;     &lt;p&gt;Kafka-connect可以用       &lt;a href="https://docs.confluent.io/home/connect/userguide.html#standalone-vs-distributed-mode" rel="noopener" target="_blank"&gt;单机版(         &lt;code&gt;standalone&lt;/code&gt;)和分布式版(         &lt;code&gt;distributed&lt;/code&gt;)&lt;/a&gt;两种启动方式：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;         &lt;code&gt;standalone&lt;/code&gt;模式下，启动时直接提供         &lt;code&gt;properties&lt;/code&gt;文件来创建Connector任务。&lt;/li&gt;       &lt;li&gt;         &lt;code&gt;distributed&lt;/code&gt;模式下，提供         &lt;a href="https://docs.confluent.io/platform/current/connect/references/restapi.html" rel="noopener" target="_blank"&gt;REST接口&lt;/a&gt;对Connector任务进行增删改查。&lt;/li&gt;&lt;/ul&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#4-3-debezium-de-ji-chu-pei-zhi"&gt;#&lt;/a&gt;4.3. Debezium的基础配置&lt;/h2&gt;     &lt;p&gt;在       &lt;code&gt;distributed&lt;/code&gt;模式下可以，调用       &lt;a href="https://docs.confluent.io/platform/current/connect/references/restapi.html#post--connectors" rel="noopener" target="_blank"&gt;         &lt;code&gt;POST /connectors&lt;/code&gt;&lt;/a&gt;接口创建Debezium的Connector任务，任务的基本配置如下：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;12             &lt;br /&gt;13             &lt;br /&gt;14             &lt;br /&gt;15             &lt;br /&gt;16             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{             &lt;br /&gt;&amp;quot;name&amp;quot;:&amp;quot;inventory-connector&amp;quot;,             &lt;br /&gt;&amp;quot;config&amp;quot;: {             &lt;br /&gt;&amp;quot;connector.class&amp;quot;:&amp;quot;io.debezium.connector.mysql.MySqlConnector&amp;quot;,             &lt;br /&gt;&amp;quot;database.hostname&amp;quot;:&amp;quot;192.168.99.100&amp;quot;,             &lt;br /&gt;&amp;quot;database.port&amp;quot;:&amp;quot;3306&amp;quot;,             &lt;br /&gt;&amp;quot;database.user&amp;quot;:&amp;quot;debezium-user&amp;quot;,             &lt;br /&gt;&amp;quot;database.password&amp;quot;:&amp;quot;debezium-user-pw&amp;quot;,             &lt;br /&gt;&amp;quot;database.server.id&amp;quot;:&amp;quot;184054&amp;quot;,             &lt;br /&gt;&amp;quot;database.server.name&amp;quot;:&amp;quot;fullfillment&amp;quot;,             &lt;br /&gt;&amp;quot;database.include.list&amp;quot;:&amp;quot;inventory&amp;quot;,             &lt;br /&gt;&amp;quot;database.history.kafka.bootstrap.servers&amp;quot;:&amp;quot;kafka:9092&amp;quot;,             &lt;br /&gt;&amp;quot;database.history.kafka.topic&amp;quot;:&amp;quot;dbhistory.fullfillment&amp;quot;,             &lt;br /&gt;&amp;quot;include.schema.changes&amp;quot;:&amp;quot;true&amp;quot;             &lt;br /&gt;}             &lt;br /&gt;}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;blockquote&gt;       &lt;p&gt;这个配置主要是数据库的用户名密码，需要同步的数据库和相关数据表，以及kafka地址和数据库schema变更存储的topic。&lt;/p&gt;       &lt;p&gt;Debezium-Connector的所有配置：         &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-connector-properties" rel="noopener" target="_blank"&gt;https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-connector-properties&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#5-binlog-jie-xi-de-nan-dian-yu-debezium-gong-zuo-yuan-li"&gt;#&lt;/a&gt;5. binlog解析的难点与Debezium工作原理&lt;/h1&gt;     &lt;p&gt;binlog的       &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_format" rel="noopener" target="_blank"&gt;ROW模式&lt;/a&gt;下类似于csv是没有shema的，我们将       &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_row_image" rel="noopener" target="_blank"&gt;row_image&lt;/a&gt;设置成full模式，不管update操作只涉及几列，都会把完整的行数据写入到binlog。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#5-1-biao-jie-gou-sui-shi-du-hui-xiu-gai-xu-yao-jie-xi-ddl-bing-wei-hu-yi-fen-schema-yong-yu-shi-jian-de-sheng-cheng"&gt;#&lt;/a&gt;5.1. 表结构随时都会修改，需要解析ddl并维护一份schema用于事件的生成&lt;/h2&gt;     &lt;p&gt;数据库客户端查询数据库的时候，客户端拿到的都是数据库当前的schema。因为schema随时可以改变，这意味着主从备份的时候，debezium不能只使用当前的schema，因为debezium可能正在处理较旧的事件。&lt;/p&gt;     &lt;p&gt;比如，有一张trade_info表，在某个时间点T添加了payment字段，在T之前的binlog是没有payment字段的，T之后的binlog才有payment。那Debezium生成事件也应该是在T之前有payment字段，T之后没有payment字段。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;MySQL在binlog中不仅包含行级修改，还包括了数据库的DDL语句。当Debezium的Connector读取binlog并遇到这些DDL语句时，它会解析这些DDL并更新内存中每个表shema。Debezium使用这个shema就能标识每次增删改操作的结构从而生成事件。&lt;/p&gt;&lt;/blockquote&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#5-2-nei-cun-li-de-schema-wei-hu-cun-zai-wen-ti"&gt;#&lt;/a&gt;5.2. 内存里的schema维护存在问题&lt;/h2&gt;     &lt;p&gt;崩溃或正常重启后，怎么还原schema，如果使用数据库当前的schema会怎样呢：&lt;/p&gt;     &lt;ol&gt;       &lt;li&gt;假如在T0~T1的时间内，表结构A发生过增加列的DDL操作，那在处理T0时间段A表的binlog时，拿到的表结构为T1的schema，就会出现列不匹配的情况. 比如之前的异常: column size is not match for table: xx , 12 vs 13&lt;/li&gt;       &lt;li&gt;假如在T0~T1发生了增加 C1列、删除了C2列，此时拿到的列的总数还是和T0时保持一致，但是对应的列会错位&lt;/li&gt;       &lt;li&gt;假如在T0~T1发生了drop table的DDL，此时拿表结构时会出现无法找到表的异常，一直阻塞整个binlog处理，比如not found [xx] in db&lt;/li&gt;&lt;/ol&gt;     &lt;p&gt;很明显，不能直接查数据库当前的schema来为之前的binlog生成事件。Debezium和Canal都有自己的解决方案：&lt;/p&gt;     &lt;p&gt;Debezium会把所有DDL语句以及DDL在binlog的位置单独存在一个       &lt;a href="https://debezium.io/documentation/reference/1.5/connectors/mysql.html#mysql-schema-history-topic" rel="noopener" target="_blank"&gt;history的topic&lt;/a&gt;中，这个topic可以用       &lt;a href="https://debezium.io/documentation/reference/1.5/connectors/mysql.html#mysql-property-database-history-kafka-topic" rel="noopener" target="_blank"&gt;database.history.kafka.topic&lt;/a&gt;进行配置。       &lt;br /&gt;当Debezium的Connector崩溃或正常停止重启后，Connector重新从原来的位置读取binlog。但是存在内存里的schema已经没有了，所以它会重新解析history中的DDL语句重建表结构。&lt;/p&gt;     &lt;p&gt;       &lt;a href="https://github.com/alibaba/canal" rel="noopener" target="_blank"&gt;alibaba/canal&lt;/a&gt;提供了       &lt;a href="https://github.com/alibaba/canal/wiki/TableMetaTSDB" rel="noopener" target="_blank"&gt;TableMetaTSDB&lt;/a&gt;的功能可以存储表结构的时序数据。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#5-3-kafka-wu-fa-bao-zheng-duo-ge-partition-de-xiao-fei-shun-xu"&gt;#&lt;/a&gt;5.3. Kafka无法保证多个partition的消费顺序&lt;/h2&gt;     &lt;p&gt;因为Debezium会重新解析history topic的DDL语句，我们希望DDL语句能按正常顺序解析，但是Kafka无法保证多个partition的消费顺序，所以history的topic的partition个数必须设置成1。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#5-4-xiao-fei-ddl"&gt;#&lt;/a&gt;5.4. 消费DDL&lt;/h2&gt;     &lt;p&gt;Debezium不希望用户直接使用history topic。因为里面包含了binlog中的所有ddl语句。&lt;/p&gt;     &lt;p&gt;如果用户想要消费自己关心的表的DDL语句，Debezium提供了       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-schema-change-topic" rel="noopener" target="_blank"&gt;schema change topic&lt;/a&gt;，这个topic名字被命名为       &lt;code&gt;serverName&lt;/code&gt;，这个serverName通过       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-property-database-server-name" rel="noopener" target="_blank"&gt;         &lt;code&gt;database.server.name&lt;/code&gt;&lt;/a&gt;配置。&lt;/p&gt;     &lt;h1&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-debezium-cai-keng-ji-lu"&gt;#&lt;/a&gt;6. Debezium踩坑记录&lt;/h1&gt;     &lt;p&gt;debezium配置起来还是比较简单的，但是这么复杂的项目，坑还是比较多的。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-1-guan-bi-kuai-zhao-chu-shi-hua"&gt;#&lt;/a&gt;6.1. 关闭快照初始化&lt;/h2&gt;     &lt;p&gt;Debezium的Connector第一次启动时，会给你的数据库执行一次       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-snapshots" rel="noopener" target="_blank"&gt;快照初始化&lt;/a&gt;。&lt;/p&gt;     &lt;p&gt;因为对于老项目，早期的binlog肯定已经被删掉了，这个时候Debezium会帮你把数据库的所有数据都写到Kafka里，这次快照之后的增删改操作通过解析binlog写入kafka。这也是为什么Debezium需要获取数据库       &lt;code&gt;SELECT&lt;/code&gt;权限的原因。&lt;/p&gt;     &lt;p&gt;但是快照读有这么几个问题：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;在执行快照初始化过程中，Connector重启或者Kafka-connect Rebalance，重启后Debezium会重新初始化快照。因为Debezium的快照是通过         &lt;a href="https://github.com/debezium/debezium/blob/1.4/debezium-connector-mysql/src/main/java/io/debezium/connector/mysql/SnapshotReader.java#L650" rel="noopener" target="_blank"&gt;           &lt;code&gt;SELECT * FROM table&lt;/code&gt;&lt;/a&gt;扫描全表实现的，没有记录进度，非常粗暴。&lt;/li&gt;       &lt;li&gt;为了防止快照初始化过程中表的schema会变更，快照初始化前会获取全局读锁。&lt;/li&gt;&lt;/ul&gt;     &lt;blockquote&gt;       &lt;p&gt;可以通过         &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-property-snapshot-locking-mode" rel="noopener" target="_blank"&gt;           &lt;code&gt;snapshot.locking.mode&lt;/code&gt;&lt;/a&gt;属性配置是否获取全局读锁，         &lt;code&gt;snapshot.locking.mode=none&lt;/code&gt;即可关闭。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;snapshot只适合在备份从库上执行，否则可能会影响正常用户的使用，通过       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-property-snapshot-mode" rel="noopener" target="_blank"&gt;         &lt;code&gt;snapshot.mode&lt;/code&gt;&lt;/a&gt;可以对初始化进行配置，这个选项支持以下几个配置值：&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;initial&lt;/code&gt;(default)- 只有当binlog的offset没有记录的时候才会执行一次快照初始化。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;when_needed&lt;/code&gt;- 有需要时就会执行，比如第一次offset没有记录，或者Connector停了很久早期的binlog被删掉了，当前的offset已经不可用了，或者GTID对不上的时候。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;never&lt;/code&gt;- 从不执行初始化。第一次启动Connector时就从binlog头部开始读取。需要注意，这种配置需要binlog包含所有的历史记录。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;schema_only&lt;/code&gt;- Connector初始化时只读取表的       &lt;code&gt;schame&lt;/code&gt;而不读取数据。如果你只需要Connector启动后的数据库变更，那这个配置很有用。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;schema_only_recovery&lt;/code&gt;- 用于恢复重启后丢失的schema，       &lt;a href="https://debezium.io/blog/2018/03/16/note-on-database-history-topic-configuration/" rel="noopener" target="_blank"&gt;但是这个只能用在自上次提交binlog-offset后&lt;/a&gt;，schema没有发生任何变更。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;initial_only&lt;/code&gt;- 这个配置在文档里没有，       &lt;a href="https://github.com/debezium/debezium/blob/1.5/debezium-connector-mysql/src/main/java/io/debezium/connector/mysql/MySqlConnectorConfig.java#L169" rel="noopener" target="_blank"&gt;代码里&lt;/a&gt;可以看到，这个是只用来执行快照的。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;用一句话总结一下：         &lt;code&gt;initial&lt;/code&gt;先全量后增量同步，         &lt;code&gt;schema_only&lt;/code&gt;和         &lt;code&gt;never&lt;/code&gt;是只增量同步，         &lt;code&gt;initial_only&lt;/code&gt;是只全量同步。&lt;/p&gt;&lt;/blockquote&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-2-xiu-gai-topic"&gt;#&lt;/a&gt;6.2. 修改topic&lt;/h2&gt;     &lt;p&gt;Debezium默认的行为是将一张表上的       &lt;code&gt;INSERT&lt;/code&gt;、       &lt;code&gt;UPDATE&lt;/code&gt;、       &lt;code&gt;DELETE&lt;/code&gt;操作记录到一个topic。Topic命名规则是       &lt;code&gt;&amp;lt;serverName&amp;gt;.&amp;lt;databaseName&amp;gt;.&amp;lt;tableName&amp;gt;&lt;/code&gt;&lt;/p&gt;     &lt;p&gt;如果进行分库了，比如       &lt;code&gt;server0&lt;/code&gt;上有       &lt;code&gt;db01&lt;/code&gt;和       &lt;code&gt;db02&lt;/code&gt;两个逻辑库，       &lt;code&gt;server1&lt;/code&gt;上有       &lt;code&gt;db11&lt;/code&gt;和       &lt;code&gt;db12&lt;/code&gt;两个逻辑库，这四个逻辑库上都有一张       &lt;code&gt;order&lt;/code&gt;表。那此时就会有4个topic。&lt;/p&gt;     &lt;p&gt;如果我们想把它们路由到同一个topic上，就需要用到       &lt;a href="https://docs.confluent.io/platform/current/connect/transforms/overview.html" rel="noopener" target="_blank"&gt;Kafka-Connect提供的SMT功能&lt;/a&gt;了：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;transforms=route             &lt;br /&gt;transforms.route.type=org.apache.kafka.connect.transforms.RegexRouter             &lt;br /&gt;transforms.route.regex=([^.]+)\\.([^.]+)\\.([^.]+)             &lt;br /&gt;transforms.route.replacement=$3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;Kafka-Connect提供了一个       &lt;a href="https://docs.confluent.io/platform/current/connect/transforms/regexrouter.html" rel="noopener" target="_blank"&gt;         &lt;code&gt;RegexRouter&lt;/code&gt;&lt;/a&gt;、       &lt;a href="https://docs.confluent.io/platform/current/connect/transforms/timestamprouter.html" rel="noopener" target="_blank"&gt;         &lt;code&gt;TimestampRouter&lt;/code&gt;&lt;/a&gt;、       &lt;a href="https://docs.confluent.io/platform/current/connect/transforms/messagetimestamprouter.html" rel="noopener" target="_blank"&gt;MessageTimestampRouter&lt;/a&gt;几个SMT让我们修改数据存入的topic。这里的RegexRouter，允许我们用正则表达式来对       &lt;code&gt;Debezium&lt;/code&gt;默认的topic进行修改。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-3-decimal-shu-ju-de-chu-li"&gt;#&lt;/a&gt;6.3. Decimal数据的处理&lt;/h2&gt;     &lt;p&gt;对于MySQL中的       &lt;a href="https://dev.mysql.com/doc/refman/8.0/en/fixed-point-types.html" rel="noopener" target="_blank"&gt;         &lt;code&gt;decimal&lt;/code&gt;&lt;/a&gt;类型的数据，Java里会转成       &lt;code&gt;BigDecimal&lt;/code&gt;，但是以json格式存入kafka的时候就会丢失精度。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;毕竟json出自JS，         &lt;a href="https://stackoverflow.com/questions/35709595/why-would-you-use-a-string-in-json-to-represent-a-decimal-number" rel="noopener" target="_blank"&gt;JS中只支持number数值类型&lt;/a&gt;，对应到Java就是double类型。&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;Debezium支持       &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-property-decimal-handling-mode" rel="noopener" target="_blank"&gt;         &lt;code&gt;decimal.handling.mode&lt;/code&gt;&lt;/a&gt;选项可以将decimal配置成       &lt;code&gt;string&lt;/code&gt;类型。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-4-shi-jian-lei-xing-shu-ju-de-chu-li"&gt;#&lt;/a&gt;6.4. 时间类型数据的处理&lt;/h2&gt;     &lt;p&gt;Debezium底层的binlog解析用的是       &lt;a href="https://github.com/shyiko/mysql-binlog-connector-java" rel="noopener" target="_blank"&gt;shyiko/mysql-binlog-connector-java&lt;/a&gt;。这中间做了很多转换：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;th&gt;mysql(Asia/Shanghai)&lt;/th&gt;         &lt;th&gt;binlog-connector&lt;/th&gt;         &lt;th&gt;debezium&lt;/th&gt;         &lt;th&gt;debezium schema&lt;/th&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;date (2021-01-28)&lt;/td&gt;         &lt;td&gt;LocalDate (2021-01-28)&lt;/td&gt;         &lt;td&gt;Integer (18655)&lt;/td&gt;         &lt;td&gt;io.debezium.time.Date&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;time (17:29:04)&lt;/td&gt;         &lt;td&gt;Duration (PT17H29M4S)&lt;/td&gt;         &lt;td&gt;Long (62944000000)&lt;/td&gt;         &lt;td&gt;io.debezium.time.MicroTime&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;timestamp (2021-01-28 17:29:04)&lt;/td&gt;         &lt;td&gt;ZonedDateTime (2021-01-28T09:29:04Z)&lt;/td&gt;         &lt;td&gt;String (2021-01-28T09:29:04Z)&lt;/td&gt;         &lt;td&gt;io.debezium.time.ZonedTimestamp&lt;/td&gt;&lt;/tr&gt;       &lt;tr&gt;         &lt;td&gt;datetime (2021-01-28 17:29:04)&lt;/td&gt;         &lt;td&gt;LocalDateTime (2021-01-28T17:29:04)&lt;/td&gt;         &lt;td&gt;Long (1611854944000)&lt;/td&gt;         &lt;td&gt;io.debezium.time.Timestamp&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;       &lt;code&gt;date&lt;/code&gt;类型，最后在Debezium中会调用       &lt;code&gt;LocalDate.toEpochDay&lt;/code&gt;转成了基于1970年的天数。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;time&lt;/code&gt;类型，在binlog解析库中，被转成了Duration，在Debezium中最后被转成了毫秒值。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;timestamp&lt;/code&gt;类型，最后在Debezium中被转成了一个ISO格式的字符串，但是时区默认是UTC时区。&lt;/p&gt;     &lt;p&gt;       &lt;code&gt;datetime&lt;/code&gt;类型，最后在Debezium中被转成了一个long类型，时区是写死的UTC时区。&lt;/p&gt;     &lt;blockquote&gt;       &lt;p&gt;         &lt;a href="https://debezium.io/documentation/reference/connectors/mysql.html#mysql-temporal-types" rel="noopener" target="_blank"&gt;文档里&lt;/a&gt;有MySQL时间类型与存入Kafka类型的映射表&lt;/p&gt;&lt;/blockquote&gt;     &lt;p&gt;总之，Debezium时间的处理混乱不堪。所以我为Debezium写了一个       &lt;a href="https://github.com/holmofy/debezium-datetime-converter" rel="noopener" target="_blank"&gt;         &lt;code&gt;datetime-converter&lt;/code&gt;的补丁&lt;/a&gt;可以将这四种类型转成字符串。配置如下：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;converters=datetime             &lt;br /&gt;datetime.type=com.darcytech.debezium.converter.MySqlDateTimeConverter             &lt;br /&gt;datetime.format.date=yyyy-MM-dd             &lt;br /&gt;datetime.format.time=HH:mm:ss             &lt;br /&gt;datetime.format.datetime=yyyy-MM-dd HH:mm:ss             &lt;br /&gt;datetime.format.timestamp=yyyy-MM-dd HH:mm:ss             &lt;br /&gt;datetime.format.timestamp.zone=UTC+8             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-5-mu-bei-shi-jian"&gt;#&lt;/a&gt;6.5. 墓碑事件&lt;/h2&gt;     &lt;p&gt;Debezium会生成5种事件：&lt;/p&gt;     &lt;ul&gt;       &lt;li&gt;         &lt;p&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-create-events" rel="noopener" target="_blank"&gt;             &lt;em&gt;create&lt;/em&gt;events&lt;/a&gt;：对应MySQL种的INSERT语句。&lt;/p&gt;         &lt;table&gt;           &lt;tr&gt;             &lt;td&gt;               &lt;pre&gt;1                 &lt;br /&gt;2                 &lt;br /&gt;3                 &lt;br /&gt;4                 &lt;br /&gt;5                 &lt;br /&gt;6                 &lt;br /&gt;7                 &lt;br /&gt;8                 &lt;br /&gt;9                 &lt;br /&gt;10                 &lt;br /&gt;11                 &lt;br /&gt;12                 &lt;br /&gt;13                 &lt;br /&gt;14                 &lt;br /&gt;15                 &lt;br /&gt;16                 &lt;br /&gt;17                 &lt;br /&gt;18                 &lt;br /&gt;19                 &lt;br /&gt;20                 &lt;br /&gt;21                 &lt;br /&gt;22                 &lt;br /&gt;23                 &lt;br /&gt;24                 &lt;br /&gt;25                 &lt;br /&gt;26                 &lt;br /&gt;27                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;             &lt;td&gt;               &lt;pre&gt;{                 &lt;br /&gt;&amp;quot;op&amp;quot;:&amp;quot;c&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:1465491411815,                 &lt;br /&gt;&amp;quot;before&amp;quot;:null,                 &lt;br /&gt;&amp;quot;after&amp;quot;: {                 &lt;br /&gt;&amp;quot;id&amp;quot;:1004,                 &lt;br /&gt;&amp;quot;first_name&amp;quot;:&amp;quot;Anne&amp;quot;,                 &lt;br /&gt;&amp;quot;last_name&amp;quot;:&amp;quot;Kretchmar&amp;quot;,                 &lt;br /&gt;&amp;quot;email&amp;quot;:&amp;quot;annek@noanswer.org&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;source&amp;quot;: {                 &lt;br /&gt;&amp;quot;version&amp;quot;:&amp;quot;1.4.2.Final&amp;quot;,                 &lt;br /&gt;&amp;quot;connector&amp;quot;:&amp;quot;mysql&amp;quot;,                 &lt;br /&gt;&amp;quot;name&amp;quot;:&amp;quot;mysql-server-1&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:0,                 &lt;br /&gt;&amp;quot;snapshot&amp;quot;:false,                 &lt;br /&gt;&amp;quot;db&amp;quot;:&amp;quot;inventory&amp;quot;,                 &lt;br /&gt;&amp;quot;table&amp;quot;:&amp;quot;customers&amp;quot;,                 &lt;br /&gt;&amp;quot;server_id&amp;quot;:0,                 &lt;br /&gt;&amp;quot;gtid&amp;quot;:null,                 &lt;br /&gt;&amp;quot;file&amp;quot;:&amp;quot;mysql-bin.000003&amp;quot;,                 &lt;br /&gt;&amp;quot;pos&amp;quot;:154,                 &lt;br /&gt;&amp;quot;row&amp;quot;:0,                 &lt;br /&gt;&amp;quot;thread&amp;quot;:7,                 &lt;br /&gt;&amp;quot;query&amp;quot;:&amp;quot;INSERT INTO customers (first_name, last_name, email) VALUES (&amp;apos;Anne&amp;apos;, &amp;apos;Kretchmar&amp;apos;, &amp;apos;annek@noanswer.org&amp;apos;)&amp;quot;                 &lt;br /&gt;}                 &lt;br /&gt;}                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;         &lt;p&gt;此时payload种的before字段为null，after字段为新增的记录值。&lt;/p&gt;&lt;/li&gt;       &lt;li&gt;         &lt;p&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-update-events" rel="noopener" target="_blank"&gt;             &lt;em&gt;update&lt;/em&gt;events&lt;/a&gt;：对应MySQL种的UPDATE语句。&lt;/p&gt;         &lt;table&gt;           &lt;tr&gt;             &lt;td&gt;               &lt;pre&gt;1                 &lt;br /&gt;2                 &lt;br /&gt;3                 &lt;br /&gt;4                 &lt;br /&gt;5                 &lt;br /&gt;6                 &lt;br /&gt;7                 &lt;br /&gt;8                 &lt;br /&gt;9                 &lt;br /&gt;10                 &lt;br /&gt;11                 &lt;br /&gt;12                 &lt;br /&gt;13                 &lt;br /&gt;14                 &lt;br /&gt;15                 &lt;br /&gt;16                 &lt;br /&gt;17                 &lt;br /&gt;18                 &lt;br /&gt;19                 &lt;br /&gt;20                 &lt;br /&gt;21                 &lt;br /&gt;22                 &lt;br /&gt;23                 &lt;br /&gt;24                 &lt;br /&gt;25                 &lt;br /&gt;26                 &lt;br /&gt;27                 &lt;br /&gt;28                 &lt;br /&gt;29                 &lt;br /&gt;30                 &lt;br /&gt;31                 &lt;br /&gt;32                 &lt;br /&gt;33                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;             &lt;td&gt;               &lt;pre&gt;{                 &lt;br /&gt;&amp;quot;before&amp;quot;: {                 &lt;br /&gt;&amp;quot;id&amp;quot;:1004,                 &lt;br /&gt;&amp;quot;first_name&amp;quot;:&amp;quot;Anne&amp;quot;,                 &lt;br /&gt;&amp;quot;last_name&amp;quot;:&amp;quot;Kretchmar&amp;quot;,                 &lt;br /&gt;&amp;quot;email&amp;quot;:&amp;quot;annek@noanswer.org&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;after&amp;quot;: {                 &lt;br /&gt;&amp;quot;id&amp;quot;:1004,                 &lt;br /&gt;&amp;quot;first_name&amp;quot;:&amp;quot;Anne Marie&amp;quot;,                 &lt;br /&gt;&amp;quot;last_name&amp;quot;:&amp;quot;Kretchmar&amp;quot;,                 &lt;br /&gt;&amp;quot;email&amp;quot;:&amp;quot;annek@noanswer.org&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;source&amp;quot;: {                 &lt;br /&gt;&amp;quot;version&amp;quot;:&amp;quot;1.4.2.Final&amp;quot;,                 &lt;br /&gt;&amp;quot;name&amp;quot;:&amp;quot;mysql-server-1&amp;quot;,                 &lt;br /&gt;&amp;quot;connector&amp;quot;:&amp;quot;mysql&amp;quot;,                 &lt;br /&gt;&amp;quot;name&amp;quot;:&amp;quot;mysql-server-1&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:1465581029100,                 &lt;br /&gt;&amp;quot;snapshot&amp;quot;:false,                 &lt;br /&gt;&amp;quot;db&amp;quot;:&amp;quot;inventory&amp;quot;,                 &lt;br /&gt;&amp;quot;table&amp;quot;:&amp;quot;customers&amp;quot;,                 &lt;br /&gt;&amp;quot;server_id&amp;quot;:223344,                 &lt;br /&gt;&amp;quot;gtid&amp;quot;:null,                 &lt;br /&gt;&amp;quot;file&amp;quot;:&amp;quot;mysql-bin.000003&amp;quot;,                 &lt;br /&gt;&amp;quot;pos&amp;quot;:484,                 &lt;br /&gt;&amp;quot;row&amp;quot;:0,                 &lt;br /&gt;&amp;quot;thread&amp;quot;:7,                 &lt;br /&gt;&amp;quot;query&amp;quot;:&amp;quot;UPDATE customers SET first_name=&amp;apos;Anne Marie&amp;apos; WHERE id=1004&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;op&amp;quot;:&amp;quot;u&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:1465581029523                 &lt;br /&gt;}                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;         &lt;p&gt;此时payload中，before为更新前的数据，after为更新后的数据。&lt;/p&gt;&lt;/li&gt;       &lt;li&gt;         &lt;p&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-primary-key-updates" rel="noopener" target="_blank"&gt;Primary key updates&lt;/a&gt;：修改主键的操作，会生成一个           &lt;code&gt;DELETE&lt;/code&gt;事件和           &lt;code&gt;CREATE&lt;/code&gt;事件：&lt;/p&gt;         &lt;ul&gt;           &lt;li&gt;             &lt;code&gt;DELETE&lt;/code&gt;事件会有             &lt;code&gt;__debezium.newkey&lt;/code&gt;的消息头。这个值是更新后的新主键。&lt;/li&gt;           &lt;li&gt;             &lt;code&gt;CREATE&lt;/code&gt;事件会有             &lt;code&gt;__debezium.oldkey&lt;/code&gt;的消息头。这个值是更新前的老主键。&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;       &lt;li&gt;         &lt;p&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-delete-events" rel="noopener" target="_blank"&gt;             &lt;em&gt;delete&lt;/em&gt;events&lt;/a&gt;：对应MySQL的DELTE语句。&lt;/p&gt;         &lt;table&gt;           &lt;tr&gt;             &lt;td&gt;               &lt;pre&gt;1                 &lt;br /&gt;2                 &lt;br /&gt;3                 &lt;br /&gt;4                 &lt;br /&gt;5                 &lt;br /&gt;6                 &lt;br /&gt;7                 &lt;br /&gt;8                 &lt;br /&gt;9                 &lt;br /&gt;10                 &lt;br /&gt;11                 &lt;br /&gt;12                 &lt;br /&gt;13                 &lt;br /&gt;14                 &lt;br /&gt;15                 &lt;br /&gt;16                 &lt;br /&gt;17                 &lt;br /&gt;18                 &lt;br /&gt;19                 &lt;br /&gt;20                 &lt;br /&gt;21                 &lt;br /&gt;22                 &lt;br /&gt;23                 &lt;br /&gt;24                 &lt;br /&gt;25                 &lt;br /&gt;26                 &lt;br /&gt;27                 &lt;br /&gt;28                 &lt;br /&gt;29                 &lt;br /&gt;30                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;             &lt;td&gt;               &lt;pre&gt;{                 &lt;br /&gt;&amp;quot;schema&amp;quot;: { ... },                 &lt;br /&gt;&amp;quot;payload&amp;quot;: {                 &lt;br /&gt;&amp;quot;before&amp;quot;: {                 &lt;br /&gt;&amp;quot;id&amp;quot;:1004,                 &lt;br /&gt;&amp;quot;first_name&amp;quot;:&amp;quot;Anne Marie&amp;quot;,                 &lt;br /&gt;&amp;quot;last_name&amp;quot;:&amp;quot;Kretchmar&amp;quot;,                 &lt;br /&gt;&amp;quot;email&amp;quot;:&amp;quot;annek@noanswer.org&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;after&amp;quot;:null,                 &lt;br /&gt;&amp;quot;source&amp;quot;: {                 &lt;br /&gt;&amp;quot;version&amp;quot;:&amp;quot;1.5.0.Beta2&amp;quot;,                 &lt;br /&gt;&amp;quot;connector&amp;quot;:&amp;quot;mysql&amp;quot;,                 &lt;br /&gt;&amp;quot;name&amp;quot;:&amp;quot;mysql-server-1&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:1465581902300,                 &lt;br /&gt;&amp;quot;snapshot&amp;quot;:false,                 &lt;br /&gt;&amp;quot;db&amp;quot;:&amp;quot;inventory&amp;quot;,                 &lt;br /&gt;&amp;quot;table&amp;quot;:&amp;quot;customers&amp;quot;,                 &lt;br /&gt;&amp;quot;server_id&amp;quot;:223344,                 &lt;br /&gt;&amp;quot;gtid&amp;quot;:null,                 &lt;br /&gt;&amp;quot;file&amp;quot;:&amp;quot;mysql-bin.000003&amp;quot;,                 &lt;br /&gt;&amp;quot;pos&amp;quot;:805,                 &lt;br /&gt;&amp;quot;row&amp;quot;:0,                 &lt;br /&gt;&amp;quot;thread&amp;quot;:7,                 &lt;br /&gt;&amp;quot;query&amp;quot;:&amp;quot;DELETE FROM customers WHERE id=1004&amp;quot;                 &lt;br /&gt;},                 &lt;br /&gt;&amp;quot;op&amp;quot;:&amp;quot;d&amp;quot;,                 &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:1465581902461                 &lt;br /&gt;}                 &lt;br /&gt;}                 &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;         &lt;p&gt;此时payload中，before为删除前的数据，after为null。&lt;/p&gt;&lt;/li&gt;       &lt;li&gt;         &lt;p&gt;           &lt;a href="https://debezium.io/documentation/reference/1.4/connectors/mysql.html#mysql-tombstone-events" rel="noopener" target="_blank"&gt;Tombstone events&lt;/a&gt;：Debezium会为删除操作生成一条key与DELETE事件相同、value为null的空消息(墓碑事件)。&lt;/p&gt;         &lt;blockquote&gt;           &lt;p&gt;墓碑事件主要用于             &lt;a href="https://kafka.apache.org/documentation/#compaction" rel="noopener" target="_blank"&gt;Kafka的compact&lt;/a&gt;——Kafka会删除具有相同key的早期事件。但是要让Kafka删除所有具有相同key的消息，需要将消息指设置成null。&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;/ul&gt;     &lt;p&gt;需要特别注意，墓碑事件的消息value为null，需要为这个事件做特殊处理。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-6-jin-yong-kafka-connect-de-schema-pei-zhi"&gt;#&lt;/a&gt;6.6. 禁用Kafka-Connect的Schema配置&lt;/h2&gt;     &lt;p&gt;Kafka-Connect为了保证每条消息是可以自我描述的，所以都会带schema。如果我们使用了       &lt;a href="https://www.confluent.io/blog/kafka-connect-deep-dive-converters-serialization-explained/" rel="noopener" target="_blank"&gt;         &lt;code&gt;JsonConverter&lt;/code&gt;&lt;/a&gt;进行序列化，默认情况下，kafka中的消息格式是这样的：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;12             &lt;br /&gt;13             &lt;br /&gt;14             &lt;br /&gt;15             &lt;br /&gt;16             &lt;br /&gt;17             &lt;br /&gt;18             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{             &lt;br /&gt;&amp;quot;schema&amp;quot;: {/* ... */},             &lt;br /&gt;&amp;quot;payload&amp;quot;: {             &lt;br /&gt;&amp;quot;op&amp;quot;:&amp;quot;u&amp;quot;,             &lt;br /&gt;&amp;quot;source&amp;quot;: {             &lt;br /&gt;...             &lt;br /&gt;},             &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:&amp;quot;...&amp;quot;,             &lt;br /&gt;&amp;quot;before&amp;quot;: {             &lt;br /&gt;&amp;quot;field1&amp;quot;:&amp;quot;oldvalue1&amp;quot;,             &lt;br /&gt;&amp;quot;field2&amp;quot;:&amp;quot;oldvalue2&amp;quot;             &lt;br /&gt;},             &lt;br /&gt;&amp;quot;after&amp;quot;: {             &lt;br /&gt;&amp;quot;field1&amp;quot;:&amp;quot;newvalue1&amp;quot;,             &lt;br /&gt;&amp;quot;field2&amp;quot;:&amp;quot;newvalue2&amp;quot;             &lt;br /&gt;}             &lt;br /&gt;}             &lt;br /&gt;}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;这里面的schema会包含下面payload里每个字段的类型解释，会导致Kafka中存储的消息非常臃肿。可以在Kafka-Connect中将Key和Value的schema禁用掉：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;key.converter=org.apache.kafka.connect.json.JsonConverter             &lt;br /&gt;value.converter=org.apache.kafka.connect.json.JsonConverter             &lt;br /&gt;key.converter.schemas.enable=false             &lt;br /&gt;value.converter.schemas.enable=false             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;更好的解决方案是使用中心化的Schema Registry。Debezium也推荐使用这种方式。&lt;/p&gt;     &lt;p&gt;       &lt;img alt="schema registry" src="https://docs.confluent.io/platform/current/_images/schema-registry-and-kafka.png"&gt;&lt;/img&gt;&lt;/p&gt;     &lt;p&gt;在github搜索       &lt;a href="https://github.com/search?q=Schema+registry" rel="noopener" target="_blank"&gt;         &lt;code&gt;schema registry&lt;/code&gt;&lt;/a&gt;关键词查找相关项目。       &lt;a href="https://debezium.io/documentation/faq/#avro-converter" rel="noopener" target="_blank"&gt;Debezium在文档中&lt;/a&gt;推荐       &lt;a href="https://github.com/Apicurio/apicurio-registry" rel="noopener" target="_blank"&gt;Apicurio API and Schema Registry&lt;/a&gt;和       &lt;a href="https://github.com/confluentinc/schema-registry" rel="noopener" target="_blank"&gt;Confluent Schema Registry&lt;/a&gt;这两种SchemaRegistry。&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-7-dui-debezium-sheng-cheng-de-xiao-xi-jin-xing-chu-li"&gt;#&lt;/a&gt;6.7. 对Debezium生成的消息进行处理&lt;/h2&gt;     &lt;p&gt;没有shema的时候，Debezium默认生成的数据格式是这样的：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;12             &lt;br /&gt;13             &lt;br /&gt;14             &lt;br /&gt;15             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{             &lt;br /&gt;&amp;quot;op&amp;quot;:&amp;quot;u&amp;quot;,             &lt;br /&gt;&amp;quot;source&amp;quot;: {             &lt;br /&gt;...             &lt;br /&gt;},             &lt;br /&gt;&amp;quot;ts_ms&amp;quot;:&amp;quot;...&amp;quot;,             &lt;br /&gt;&amp;quot;before&amp;quot;: {             &lt;br /&gt;&amp;quot;field1&amp;quot;:&amp;quot;oldvalue1&amp;quot;,             &lt;br /&gt;&amp;quot;field2&amp;quot;:&amp;quot;oldvalue2&amp;quot;             &lt;br /&gt;},             &lt;br /&gt;&amp;quot;after&amp;quot;: {             &lt;br /&gt;&amp;quot;field1&amp;quot;:&amp;quot;newvalue1&amp;quot;,             &lt;br /&gt;&amp;quot;field2&amp;quot;:&amp;quot;newvalue2&amp;quot;             &lt;br /&gt;}             &lt;br /&gt;}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;消息体中       &lt;code&gt;before&lt;/code&gt;表示变更前的数据，       &lt;code&gt;after&lt;/code&gt;表示变更后的数据，       &lt;code&gt;source&lt;/code&gt;表示来源于哪个数据库、哪张表、哪个事务(GTID)。&lt;/p&gt;     &lt;p&gt;为了方便与其他Connector集成，比如让       &lt;a href="https://docs.confluent.io/kafka-connect-jdbc/current/index.html" rel="noopener" target="_blank"&gt;         &lt;code&gt;kafka-connect-jdbc&lt;/code&gt;&lt;/a&gt;把消息都写到另一个数据库中。那这个时候我们只想要       &lt;code&gt;after&lt;/code&gt;里面的数据了。&lt;/p&gt;     &lt;p&gt;Debezium提供了一个       &lt;a href="https://debezium.io/documentation/reference/1.4/configuration/event-flattening.html" rel="noopener" target="_blank"&gt;         &lt;code&gt;Event-Flat&lt;/code&gt;的SMT&lt;/a&gt;，我们只需要和上面的RegexRouter一样配置一下就可以了：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;transforms=unwrap,...             &lt;br /&gt;transforms.unwrap.type=io.debezium.transforms.ExtractNewRecordState             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;那如果是删除操作呢，删除操作会生成两个事件，一个delete事件有before没有after，还有一个和delete事件key相同的墓碑事件消息体为null。ExtractNewRecordState可以配置怎么处理       &lt;code&gt;delete&lt;/code&gt;记录：&lt;/p&gt;     &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;transforms=unwrap,...             &lt;br /&gt;transforms.unwrap.type=io.debezium.transforms.ExtractNewRecordState             &lt;br /&gt;transforms.unwrap.drop.tombstones=true             &lt;br /&gt;transforms.unwrap.delete.handling.mode=drop             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;     &lt;p&gt;       &lt;code&gt;delete.handling.mode&lt;/code&gt;指定delete记录的处理模式，默认为       &lt;code&gt;drop&lt;/code&gt;也就是delete记录将会被ExtractNewRecordState丢弃。       &lt;code&gt;drop.tombstones&lt;/code&gt;指定要不要丢弃墓碑事件。&lt;/p&gt;     &lt;p&gt;更多配置可以参考       &lt;a href="https://debezium.io/documentation/reference/configuration/event-flattening.html#configuration-options" rel="noopener" target="_blank"&gt;官方文档&lt;/a&gt;&lt;/p&gt;     &lt;h2&gt;       &lt;a href="https://blog.hufeifei.cn/2021/03/DB/mysql-binlog-parser/index.html#6-8-kafka-connect-de-keng"&gt;#&lt;/a&gt;6.8. kafka-connect的坑&lt;/h2&gt;     &lt;p&gt;kafka broker本身有个配置       &lt;a href="https://kafka.apache.org/documentation/#brokerconfigs_auto.create.topics.enable" rel="noopener" target="_blank"&gt;auto.create.topics.enable&lt;/a&gt;默认为true——当发送消息到一个不存在的topic时，kafka会自动创建这个topic，这些自动创建的topic会使用       &lt;a href="https://kafka.apache.org/documentation.html#brokerconfigs_num.partitions" rel="noopener" target="_blank"&gt;num.partitions&lt;/a&gt;和       &lt;a href="https://kafka.apache.org/documentation.html#brokerconfigs_default.replication.factor" rel="noopener" target="_blank"&gt;default.replication.factor&lt;/a&gt;指定的partition数和replicas数创建topic。生产环境一般是不建议使用kafka broker中的自动创建主题的，因为这可能会带来很大的维护成本，我们希望不同情况使用不同的主题配置。&lt;/p&gt;     &lt;p&gt;另外，kafka-connect启动时默认会创建三个       &lt;a href="https://docs.confluent.io/home/connect/userguide.html#kconnect-internal-topics" rel="noopener" target="_blank"&gt;connect内部使用的topic&lt;/a&gt;，这三个topic名字由       &lt;code&gt;config.storage.topic&lt;/code&gt;、       &lt;code&gt;offset.storage.topic&lt;/code&gt;、       &lt;code&gt;status.storage.topic&lt;/code&gt;三个配置指定，它们分别存储connector的配置和offset以及当前的状态。&lt;/p&gt;     &lt;p&gt;如果想要对这三个自动创建的topic进行一些配置，可以参考       &lt;a href="https://docs.confluent.io/home/connect/userguide.html#using-ak-broker-default-topic-settings" rel="noopener" target="_blank"&gt;connect的文档&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;如果你是手动创建需要注意：&lt;/p&gt;     &lt;p&gt;       &lt;a href="https://docs.confluent.io/platform/current/connect/references/allconfigs.html#distributed-worker-configuration" rel="noopener" target="_blank"&gt;config的partition必须为1&lt;/a&gt;；&lt;/p&gt;     &lt;p&gt;offset和kafka内建的       &lt;code&gt;__consumer_offsets&lt;/code&gt;类似，如果要支持更大的kafka-connect集群，可以把partition设大一点。&lt;/p&gt;     &lt;p&gt;这三个topic的       &lt;code&gt;cleanup.policy&lt;/code&gt;都必须设置成compacted模式。&lt;/p&gt;     &lt;p&gt;如果是source connector内部要自动创建topic，可以使用connector的一些配置，具体可以参考：&lt;/p&gt;     &lt;p&gt;       &lt;a href="https://docs.confluent.io/home/connect/userguide.html#configuring-auto-topic-creation-for-source-connectors" rel="noopener" target="_blank"&gt;Configuring Auto Topic Creation for Source Connectors&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;       &lt;a href="https://debezium.io/documentation/reference/configuration/topic-auto-create-config.html" rel="noopener" target="_blank"&gt;Customization of Kafka Connect automatic topic creation&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;Refs:&lt;/p&gt;     &lt;p&gt;^ Debezium Document:       &lt;a href="https://debezium.io/documentation/reference/1.4/" rel="noopener" target="_blank"&gt;https://debezium.io/documentation/reference/1.4/&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;^ Debezium FAQ:       &lt;a href="https://debezium.io/documentation/faq/" rel="noopener" target="_blank"&gt;https://debezium.io/documentation/faq/&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;^ Confluent Document:       &lt;a href="https://docs.confluent.io/platform/current/overview.html" rel="noopener" target="_blank"&gt;https://docs.confluent.io/platform/current/overview.html&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;^ Aliyun DTS服务原理:       &lt;a href="https://www.alibabacloud.com/help/zh/doc-detail/176085.htm" rel="noopener" target="_blank"&gt;https://www.alibabacloud.com/help/zh/doc-detail/176085.htm&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;^ Aliyun DTS应用场景:       &lt;a href="https://www.alibabacloud.com/help/zh/doc-detail/176086.htm" rel="noopener" target="_blank"&gt;https://www.alibabacloud.com/help/zh/doc-detail/176086.htm&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61859-binlog-%E5%AE%9E%E6%97%B6-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Sat, 30 Oct 2021 23:19:38 CST</pubDate>
    </item>
    <item>
      <title>rsync+inotify-tools实现数据实时同步方案_Ljohn的技术博客_51CTO博客</title>
      <link>https://itindex.net/detail/61595-rsync-inotify-tools</link>
      <description>&lt;h3&gt;rsync数据同步优缺点&lt;/h3&gt; &lt;blockquote&gt;  &lt;p&gt;与传统的cp、tar备份方式相比，rsync具有安全性高、备份迅速、支持增量备份等优点，通过rsync可以解决对实时性要求不高的数据备份需求，例如定期的备份文件服务器数据到远端服务器，对本地磁盘定期做数据镜像等。&lt;/p&gt;&lt;/blockquote&gt; &lt;blockquote&gt;  &lt;p&gt;随着应用系统规模的不断扩大，对数据的安全性和可靠性也提出的更好的要求，rsync在高端业务系统中也逐渐暴露出了很多不足。首先，rsync同步数据时，需要扫描所有文件后进行比对，进行差量传输。如果文件数量达到了百万甚至千万量级，扫描所有文件将是非常耗时的。而且正在发生变化的往往是其中很少的一部分，这是非常低效的方式。其次，rsync不能实时的去监测、同步数据，虽然它可以通过linux守护进程的方式进行触发同步，但是两次触发动作一定会有时间差，这样就导致了服务端和客户端数据可能出现不一致，无法在应用故障时完全的恢复数据。基于以上原因，rsync+inotify组合出现了！&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;inotify&lt;/h3&gt; &lt;blockquote&gt;  &lt;p&gt;inotify是一种强大的、细粒度的、异步的文件系统事件监控机制，linux内核从2.6.13起，加入了inotify支持，通过inotify可以监控文件系统中添加、删除，修改、移动等各种细微事件，利用这个内核接口，第三方软件就可以监控文件系统下文件的各种变化情况，而inotify-tools就是这样的一个第三方软件。&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;  &lt;img alt="" src="https://s4.51cto.com/images/blog/201712/04/3efc7eef751c79ae0f416c0aa0de3d2c.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk="&gt;&lt;/img&gt;&lt;/p&gt; &lt;h3&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;一、环境准备&lt;/h3&gt; &lt;p&gt;操作系统：CentOS release 6.8 (Final) x86_64&lt;/p&gt; &lt;p&gt;服务器IP：&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;rsync_server（数据源）192.168.0.44
rsync_client（目标端）192.168.0.45
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;同步目录：&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;rsync_server       /app/rsync_server
rsync_client       /app/rsync_client 
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;二、安装及配置rsync&lt;/h3&gt; &lt;h4&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;客户端配置（目标端）&lt;/h4&gt; &lt;p&gt;1、安装rsync&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# yum -y install rsync xinetd
# cp /etc/xinetd.d/rsync{,.bak}
# vim /etc/xinetd.d/rsync
service rsync
{
        disable = no            #修改为no
        flags           = IPv6
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/bin/rsync
        server_args     = --daemon
        log_on_failure  += USERID
}
# /etc/init.d/xinetd start 
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;      &lt;li&gt;15.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;2、配置rsync&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# vim /etc/rsyncd.conf    #创建配置文件

logfile = /var/log/rsyncd.log    #日志文件位置，启动rsync后自动产生这个文件，无需提前创建
pidfile = /var/run/rsyncd.pid    #pid文件的存放位置
lockfile = /var/run/rsync.lock   #支持max connections参数的锁文件
secretsfile = /etc/rsync.pass    #用户认证配置文件，里面保存用户名称和密码，后面会创建这个文件
motdfile = /etc/rsyncd.Motd    #rsync启动时欢迎信息页面文件位置（文件内容自定义）
[app_rsync_client]   #自定义名称
path = /app/rsync_client/    #rsync服务端数据目录路径
comment = app_rsync_client    #模块名称与[app_rsync_client]自定义名称相同
uid = root    #设置rsync运行权限为root
gid = root    #设置rsync运行权限为root
port =873
use chroot = no    #默认为true，修改为no，增加对目录文件软连接的备份
read only = no    设置rsync服务端文件为读写权限
list = no    #不显示rsync服务端资源列表
mac connections = 200
timeout = 600
auth users = rsync    #执行数据同步的用户名，可以设置多个，用英文状态下逗号隔开
hosts allow = 192.168.0.45   #允许进行数据同步的客户端IP地址，可以设置多个，用英文状态下逗号隔开
hosts deny = 192.168.0.46,192.168.0.47    #禁止数据同步的客户端IP地址，可以设置多个，用英文状态下逗号隔开,先允许后拒绝
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;      &lt;li&gt;15.&lt;/li&gt;      &lt;li&gt;16.&lt;/li&gt;      &lt;li&gt;17.&lt;/li&gt;      &lt;li&gt;18.&lt;/li&gt;      &lt;li&gt;19.&lt;/li&gt;      &lt;li&gt;20.&lt;/li&gt;      &lt;li&gt;21.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;3、配置rsync同步的账户密码&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# vim /etc/rsync.pass    #配置文件，添加以下内容
rsync:123456    #格式，用户名:密码，可以设置多个，每行一个用户名:密码
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;4、赋权启动rsync&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# chmod 600 /etc/rsyncd.conf 
# chmod 600 /etc/rsync.pass 
# /etc/init.d/xinetd restart
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;h4&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;服务端配置（数据源）&lt;/h4&gt; &lt;p&gt;1、安装rsync&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# yum install rsync xinetd
# vim /etc/xinetd.d/rsync
service rsync
{
        disable = no    #修改为no
        flags           = IPv6
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/bin/rsync
        server_args     = --daemon
        log_on_failure  += USERID
}

&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;2、配置rsync同步的账户密码&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# vim /etc/passwd.txt
123456

# chmod 600 /etc/passwd.txt

&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;3、测试手动同步&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# mkdir -pv /app/rsync_server &amp;amp;&amp;amp; touch /app/rsync_server/test.txt
在rsync_server的/app/rsync_server目录下创建文件test.txt，在rsync_server端运行同步命令同步数据：

rsync -avH --port=873 --progress --delete  /app/rsync_client/ rsync@192.168.0.45::app_rsync_client --password-file=/etc/passwd.txt

注释：
/app/rsync_server/             #数据源的目录
-password-file=/etc/passwd.txt #数据源的密码文件
rsync@10.15.43.228::app_rsync_client #rsync目标端rsync服务端配置的用户名，app_rsync_client目标端rsync服务端配置的模块名称

检查客户端rsync_client目录

# ls /app/rsync_client/
test.txt
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;三、安装Inotify-tools实时触发rsync进行同步&lt;/h3&gt; &lt;blockquote&gt;  &lt;p&gt;这里可以参考github上的官方wiki文档（包含安装及配置使用示例）   &lt;br /&gt;   &lt;a href="https://github.com/rvoicilas/inotify-tools/wiki" target="_blank"&gt;https://github.com/rvoicilas/inotify-tools/wiki&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;1、下载安装Inotify-tools&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# uname -r        #Linux下支持inotify的内核最小为2.6.13
2.6.32-642.el6.x86_64

# 安装前要先下载epel源
# yum install inotify-tools -y

查看其程序是否安装成功
# rpm -qa inotify-tools
inotify-tools-3.14-1.el6.x86_64

查看程序包含的文件
#rpm -ql inotify-tools
/usr/bin/inotifywait
/usr/bin/inotifywatch
/usr/lib64/libinotifytools.so.0
/usr/lib64/libinotifytools.so.0.4.1
/usr/share/doc/inotify-tools-3.14
/usr/share/doc/inotify-tools-3.14/AUTHORS
/usr/share/doc/inotify-tools-3.14/COPYING
/usr/share/doc/inotify-tools-3.14/ChangeLog
/usr/share/doc/inotify-tools-3.14/NEWS
/usr/share/doc/inotify-tools-3.14/README
/usr/share/man/man1/inotifywait.1.gz
/usr/share/man/man1/inotifywatch.1.gz

&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;      &lt;li&gt;15.&lt;/li&gt;      &lt;li&gt;16.&lt;/li&gt;      &lt;li&gt;17.&lt;/li&gt;      &lt;li&gt;18.&lt;/li&gt;      &lt;li&gt;19.&lt;/li&gt;      &lt;li&gt;20.&lt;/li&gt;      &lt;li&gt;21.&lt;/li&gt;      &lt;li&gt;22.&lt;/li&gt;      &lt;li&gt;23.&lt;/li&gt;      &lt;li&gt;24.&lt;/li&gt;      &lt;li&gt;25.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;2、配置inotify-tools&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# sysctl -a|egrep -i &amp;quot;max_queued_events|max_user_watches|max_user_instances&amp;quot;    #修改inotify默认参数（inotify默认内核参数值太小）
fs.inotify.max_user_instances = 128
fs.inotify.max_user_watches = 8192
fs.inotify.max_queued_events = 16384
fs.epoll.max_user_watches = 201420

# vim /etc/sysctl.conf 添加
fs.inotify.max_queued_events = 99999999
fs.inotify.max_user_watches = 99999999
fs.inotify.max_user_instances = 65535

#sysctl  -p   参数立即生效

# cat /proc/sys/fs/inotify/{max_user_instances,max_user_watches,max_queued_events}  #检查参数是否生效
65535
99999999
99999999


注释：
    max_queued_events：inotify队列最大长度，如果值太小，会出现&amp;quot;** Event Queue Overflow **&amp;quot;错误，导致监控文件不准确
    max_user_watches：要同步的文件包含多少目录，可以用：find /app/rsync_server/ -type d | wc -l 统计，必须保证max_user_watches值大于统计结果（这里/app/rsync_server/为同步文件目录）
    max_user_instances：每个用户创建inotify实例最大值
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;      &lt;li&gt;15.&lt;/li&gt;      &lt;li&gt;16.&lt;/li&gt;      &lt;li&gt;17.&lt;/li&gt;      &lt;li&gt;18.&lt;/li&gt;      &lt;li&gt;19.&lt;/li&gt;      &lt;li&gt;20.&lt;/li&gt;      &lt;li&gt;21.&lt;/li&gt;      &lt;li&gt;22.&lt;/li&gt;      &lt;li&gt;23.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;3、创建实时同步脚本&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;# vim  /usr/local/inotify/rsync.sh
#!/bin/bash
src_dir=&amp;quot;/app/rsync_server/&amp;quot;
dst_dir=&amp;quot;app_rsync_client&amp;quot;
exclude_dir=&amp;quot;/usr/local/inotify/exclude.list&amp;quot;
rsync_user=&amp;quot;rsync&amp;quot;
rsync_passwd=&amp;quot;/etc/passwd.txt&amp;quot;
dst_ip=&amp;quot;192.168.0.45&amp;quot;
rsync_command(){
                  rsync -avH --port=873 --progress --delete --exclude-from=$exclude_dir $src_dir $rsync_user@$ip::$dst_dir --password-file=$rsync_passwd
}
for ip in $dst_ip;do
     rsync_command
done
    /usr/bin/inotifywait -mrq --timefmt &amp;apos;%d/%m/%y %H:%M&amp;apos; --format &amp;apos;%T %w%f%e&amp;apos; -e close_write,modify,delete,create,attrib,move $src_dir \
| while read file;do
   for ip in $dst_ip;do
       rsync_command
       echo &amp;quot;${file} was rsynced&amp;quot; &amp;gt;&amp;gt; /tmp/rsync.log 2&amp;gt;&amp;amp;1
   done
 done 

注释：
    src_dir=&amp;quot;/app/rsync_server/&amp;quot;    #源服务器同步目录
    dst_dir=&amp;quot;app_rsync_client&amp;quot;    #目标服务器rsync同步目录模块名称
    exclude_dir=&amp;quot;/usr/local/inotify/exclude.list&amp;quot;    #不需要同步的目录，如果有多个，每一行写一个目录，使用相对于同步模块的路径；
    例如：不需要同步/app/rsync_server/&amp;quot;目录下的a目录和b目录下面的b1目录，exclude.list文件可以这样写
    a/
    b/b1/
    
    rsync_user=&amp;quot;rsync&amp;quot;    #目标服务器rsync同步用户名
    rsync_passwd=&amp;quot;/etc/passwd.txt&amp;quot;    #目标服务器rsync同步用户的密码在源服务器的存放路径
    dst_ip=&amp;quot;192.168.0.45&amp;quot;    #目标服务器ip，多个ip用空格分开
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;      &lt;li&gt;12.&lt;/li&gt;      &lt;li&gt;13.&lt;/li&gt;      &lt;li&gt;14.&lt;/li&gt;      &lt;li&gt;15.&lt;/li&gt;      &lt;li&gt;16.&lt;/li&gt;      &lt;li&gt;17.&lt;/li&gt;      &lt;li&gt;18.&lt;/li&gt;      &lt;li&gt;19.&lt;/li&gt;      &lt;li&gt;20.&lt;/li&gt;      &lt;li&gt;21.&lt;/li&gt;      &lt;li&gt;22.&lt;/li&gt;      &lt;li&gt;23.&lt;/li&gt;      &lt;li&gt;24.&lt;/li&gt;      &lt;li&gt;25.&lt;/li&gt;      &lt;li&gt;26.&lt;/li&gt;      &lt;li&gt;27.&lt;/li&gt;      &lt;li&gt;28.&lt;/li&gt;      &lt;li&gt;29.&lt;/li&gt;      &lt;li&gt;30.&lt;/li&gt;      &lt;li&gt;31.&lt;/li&gt;      &lt;li&gt;32.&lt;/li&gt;      &lt;li&gt;33.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt; 
##赋权，添加开机启动

# chmod +x /usr/local/inotify/rsync.sh
# touch /usr/local/inotify/exclude.list
# vim /etc/rc.d/rc.local
nohup /bin/sh /usr/local/inotify/rsync.sh &amp;amp;
# nohup /bin/sh /usr/local/inotify/rsync.sh &amp;amp;

&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;4、测试&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;pre&gt;     &lt;code&gt;在rsync_server（数据源）192.168.0.44的/app/rsync_server创建文件
# cd /app/rsync_server
# touch test{1..9}
# touch test{a..j}
# ls
test1  test2  test3  test4  test5  test6  test7  test8  test9  testa  testb  testc  testd  teste  testf  testg  testh  testi  testj

在rsync_client（目标端）192.168.0.45上查看已经同步
# cd /app/rsync_client
# ls
test1  test2  test3  test4  test5  test6  test7  test8  test9  testa  testb  testc  testd  teste  testf  testg  testh  testi  testj
&lt;/code&gt;     &lt;ul&gt;      &lt;li&gt;1.&lt;/li&gt;      &lt;li&gt;2.&lt;/li&gt;      &lt;li&gt;3.&lt;/li&gt;      &lt;li&gt;4.&lt;/li&gt;      &lt;li&gt;5.&lt;/li&gt;      &lt;li&gt;6.&lt;/li&gt;      &lt;li&gt;7.&lt;/li&gt;      &lt;li&gt;8.&lt;/li&gt;      &lt;li&gt;9.&lt;/li&gt;      &lt;li&gt;10.&lt;/li&gt;      &lt;li&gt;11.&lt;/li&gt;&lt;/ul&gt;&lt;/pre&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;/div&gt; &lt;blockquote&gt;  &lt;p&gt;如果以上测试都通过，说明inotify实时触发rsync同步脚本运行正常。   &lt;br /&gt;至此，Linux下Rsync+Inotify-tools实现数据实时同步完成。如果要双向同步可以把以上反过来部署一次。&lt;/p&gt;&lt;/blockquote&gt; &lt;h4&gt;  &lt;a target="_blank"&gt;&lt;/a&gt;FAQ&lt;/h4&gt; &lt;p&gt;Q1:  &lt;br /&gt;#rsync -avH --port=873 --progress --delete /app/rsync_client/   &lt;a href="mailto:rsync@192.168.0.45" target="_blank"&gt;rsync@192.168.0.45&lt;/a&gt;::app_rsync_client --password-file=/etc/passwd.txt&lt;/p&gt; &lt;p&gt;@ERROR: auth failed on module app_rsync_client  &lt;br /&gt;rsync error: error starting client-server protocol (code 5) at main.c(1503) [sender=3.0.6]&lt;/p&gt; &lt;p&gt;A：如果出现这个错误，请详细检查配置文件是否有误，建议删掉无用的注释&lt;/p&gt; &lt;p&gt;Q2:  &lt;br /&gt;#rsync -avH --port=873 --progress --delete /app/rsync_client   &lt;a href="mailto:rsync@192.168.0.45" target="_blank"&gt;rsync@192.168.0.45&lt;/a&gt;::app_rsync_client --password-file=/etc/passwd.txt&lt;/p&gt; &lt;p&gt;sending incremental file list  &lt;br /&gt;rsync: link_stat “/app/rsync_client” failed: No such file or directory (2)&lt;/p&gt; &lt;p&gt;A:检查客户端及服务端文件夹是否存在，这里应该还有一个坑，就是这里是在服务端(数据源)同步，目录应该指向“/app/rsync_client”&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;因此，如果是同步应用程序目录，建议这里的源目录，与目标目录设置为同一个。&lt;/p&gt;&lt;/blockquote&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61595-rsync-inotify-tools</guid>
      <pubDate>Wed, 07 Jul 2021 15:30:10 CST</pubDate>
    </item>
    <item>
      <title>mysql主从同步设置的重要参数log_slave_updates_ITPUB博客</title>
      <link>https://itindex.net/detail/61566-mysql-%E5%90%8C%E6%AD%A5-%E5%8F%82%E6%95%B0</link>
      <description>&lt;p&gt;说明：最近部署了mysql的集群环境，详细如下M01和M02为主主复制，M01和R01为主从复制；在测试的过程中发现了以下问题：&lt;/p&gt; &lt;p&gt;1、M01和M02的主主复制是没有问题的（从M01写入数据能同步到M02，从M02写入数据能够同步到M01);&lt;/p&gt; &lt;p&gt;2、主从同步的时候，当从M01写入的时候，数据可以写入到R01；&lt;/p&gt; &lt;p&gt;3、当从M02写入的时候，数据就不能写入到R01；&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;问题的原因：log_slave_updates参数的状态为NO&lt;/p&gt; &lt;p&gt;mysql的官网说明如下：&lt;/p&gt; &lt;div&gt;  &lt;table border="0"&gt;   &lt;tr&gt;    &lt;td valign="middle"&gt;     &lt;p&gt;Normally, a slave does not log to its own binary log any updates that are received from a master server. This option tells the slave to log the updates performed by its SQL thread to its own binary log. For this option to have any effect, the slave must also be started with the --log-bin option to enable binary logging. Prior to MySQL 5.5, the server would not start when using the --log-slave-updates option without also starting the server with the --log-bin option, and would fail with an error; in MySQL 5.5, only a warning is generated. (Bug #44663) --log-slave-updates is used when you want to chain replication servers. For example, you might want to set up replication servers using this arrangement:&lt;/p&gt;     &lt;p&gt;A -&amp;gt; B -&amp;gt; C&lt;/p&gt;     &lt;p&gt;   &lt;/p&gt;     &lt;p&gt;Here, A serves as the master for the slave B, and B serves as the master for the slave C. For this to work, B must be both a master and a slave. You must start both A and B with --log-bin to enable binary logging, and B with the --log-slave-updates option so that updates received from A are logged by B to its binary log. &lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt; &lt;p&gt;a) M01同步从M02同步数据过来的时候，log_slave_updates参数用来控制M01是否把所有的操作写入到binary log，默认的情况下mysql是关闭的;&lt;/p&gt; &lt;p&gt;b) R01数据的更新需要通过读取到M01的binary log才能进行更新，这个时候M01是没有写binary log的，所以当数据从M02写入的时候，R01也就没有更新了。。&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;问题的解决方法：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="http://img.blog.itpub.net/blog/attachment/201411/3/12679300_14150174872iDG.png"&gt;&lt;/img&gt; &lt;/p&gt; &lt;p&gt;log_slave_updates：默认值为OFF;&lt;/p&gt; &lt;p&gt;Dynamic Variable：NO&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;处理方法：修改/etc/my.cnf，增加一行log_slave_updates=1，重启数据库后就可以了；&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;总结：设置完该参数后，数据库的架构就可以设置成M01和M02为主主同步，R01通过M01进行主从同步；&lt;/p&gt; &lt;p&gt;应用的写操作中M02上面进行，读操作中R01上面进行（如果读操作很多的话，可以在M01上面架设多台只读数据库），当M02发生故障后，系统的写操作自动迁移到M01上面。这种架构基本可以保证大部分公司的应用需求；&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61566-mysql-%E5%90%8C%E6%AD%A5-%E5%8F%82%E6%95%B0</guid>
      <pubDate>Sat, 26 Jun 2021 14:39:24 CST</pubDate>
    </item>
    <item>
      <title>MySQL 数据库双向同步复制 - mindwind - 博客园</title>
      <link>https://itindex.net/detail/61564-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;    &lt;p&gt;      &lt;img alt="" src="https://images2015.cnblogs.com/blog/815275/201511/815275-20151125093542202-1771243901.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;MySQL 复制问题的最后一篇，关于双向同步复制架构设计的一些设计要点与制约。&lt;/p&gt;    &lt;h2&gt;问题和制约&lt;/h2&gt;    &lt;p&gt;数据库的双主双写并双向同步场景，主要考虑数据完整性、一致性和避免冲突。对于同一个库，同一张表，同一个记录中的同一字段的两地变更，会引发数据一致性判断冲突，尽可能通过业务场景设计规避。双主双写并同步复制可能引发主键冲突，需避免使用数据库自增类主键方案。另外，双向同步潜在可能引发循环同步的问题，需要做回环控制。&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://images2015.cnblogs.com/blog/815275/201511/815275-20151125093606452-731951970.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;如上图所示，复制程序写入时也会产生 binlog，如何识别由复制程序产生的 binlog 并将其过滤掉是避免循环复制的关键。&lt;/p&gt;    &lt;h2&gt;原生 Dual Master 方案&lt;/h2&gt;    &lt;p&gt;MySQL 自身支持双主配置，但并没有去解决潜在的主键和双写带来的数据一致性冲突。对于双向同步潜在的循环复制问题，MySQL 在 binlog 中记录了当前 MySQL 的 server-id。一旦有了 server-id 的值之后，MySQL 就很容易判断某个变更是从哪一个 Server 最初产生的，所以就很容易避免出现循环复制的情况。而且，还可以配置不打开记录 slave 的 binlog 选项（--log-slave-update），MySQL 就不会记录复制过程中的变更到 binlog 中，就更不用担心可能会出现循环复制的情形了。&lt;/p&gt;    &lt;p&gt;从 MySQL 自身的方案中可以找到切入点，就是如果能在 binlog 中打上标记，就有办法判断哪些 binlog 是复制产生的，并将其过滤。使用 MySQL 的方案则过于耦合 MySQL 的配置，在大规模部署的线上生产系统中容易因为 MySQL 配置错误导致问题。&lt;/p&gt;    &lt;h2&gt;自定义标记 SQL 方案&lt;/h2&gt;    &lt;p&gt;为了和 MySQL 配置解耦合，可以考虑一种通用的标记 SQL 方案。简单来说，就是在同步复制入库时插入特殊的标记 SQL 语句来标记这是来自复制程序的变更，这个标记 SQL 会进入 binlog 中。而在复制程序读取时，通过识别这个标记 SQL 来过滤判断。&lt;/p&gt;    &lt;p&gt;binlog 中存储了对数据产生变更影响的的 SQL 语句，这些 SQL 语句组成了一段一段的事务，如下图所示：&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://images2015.cnblogs.com/blog/815275/201511/815275-20151125093638077-1748567220.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;绿色区是业务运行产生的正常事务，红色区是复制程序写入产生的事务，其中蓝色块是标记 SQL。标记 SQL 分别在事务开始后与事务结束前，标记 SQL 更新一张预定义的区别于业务表的标记表。那么每次复制程序去批量读取 binlog 内容时，可能存在下面 5 种情况，如下图所示：&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://images2015.cnblogs.com/blog/815275/201511/815275-20151125093654343-545960852.png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;ol&gt;      &lt;li&gt;批量读取范围全落在绿色区内。&lt;/li&gt;      &lt;li&gt;批量读取范围起点落在绿色区，终点落在红色区。&lt;/li&gt;      &lt;li&gt;批量读取范围起点落在红色区，终点落在绿色区。&lt;/li&gt;      &lt;li&gt;批量读取范围起点和终点都在绿色区，但中间涵盖了一段红色区。&lt;/li&gt;      &lt;li&gt;批量读取范围全落在红色区。&lt;/li&gt;&lt;/ol&gt;    &lt;p&gt;如上只有第 5 种情况，一个事务被拆成 3 段来同步。中间一段因为没有事务头和尾的标记，复制程序读取时将无法判断，导致循环同步，需要避免。通过把复制程序的批量读取范围固定设置为至少大于或等于写入的事务长度范围，避免了第 5 种情况。复制程序批量读取 binlog 日志事件时，通过标记 SQL 来过滤，避免了循环复制，实现了回环控制。&lt;/p&gt;    &lt;h2&gt;总结&lt;/h2&gt;    &lt;p&gt;本文考虑了在 MySQL 双主写入场景下双向同步复制的一些设计要点和制约。以原生实现为参考，给出了一种自定义实现方式的设计要点分析。而对于同库同表同记录同字段的同时两地变更，则必然引发数据一致性冲突，在复制同步层面无法区分哪边的更新为准。通常会考虑以最后时间戳来恢复到一致状态，但时间戳实际也会产生误差，此类场景不多见最好还是尽可能还是在业务场景设计上来规避。&lt;/p&gt;    &lt;h2&gt;参考&lt;/h2&gt;    &lt;p&gt;[1] MySQL Internals Manual.      &lt;a href="https://dev.mysql.com/doc/internals/en/replication.html" target="_blank"&gt;Replication&lt;/a&gt;.      &lt;br /&gt;[2] MySQL Internals Manual.      &lt;a href="https://dev.mysql.com/doc/internals/en/binary-log.html" target="_blank"&gt;The Binary Log&lt;/a&gt;.  [3] in355hz.      &lt;a href="http://in355hz.iteye.com/blog/2029963" target="_blank"&gt;数据库 ACID 的实现&lt;/a&gt;.      &lt;br /&gt;[4] jb51.      &lt;a href="http://www.jb51.net/article/27556.htm" target="_blank"&gt;MySQL 对 binlog 的处理说明&lt;/a&gt;.      &lt;br /&gt;[5] repls.      &lt;a href="http://www.2cto.com/database/201306/221413.html" target="_blank"&gt;浅析 innodb_support_xa 与 innodb_flush_log_at_trx_commit&lt;/a&gt;.      &lt;br /&gt;[6] 68idc.      &lt;a href="http://www.68idc.cn/help/mysqldata/mysql/20150127191299.html" target="_blank"&gt;MySQL 5.6 之 DBA 与开发者指南&lt;/a&gt;.      &lt;br /&gt;[7] csdn.      &lt;a href="http://blog.csdn.net/hguisu/article/details/7325124" target="_blank"&gt;高性能 MySQL 主从架构的复制原理及配置详解&lt;/a&gt;.      &lt;br /&gt;[8] agapple.      &lt;a href="https://github.com/alibaba/otter/wiki/Otter%E5%8F%8C%E5%90%91%E5%9B%9E%E7%8E%AF%E6%8E%A7%E5%88%B6" target="_blank"&gt;Otter 双向回环控制&lt;/a&gt;.&lt;/p&gt;    &lt;hr&gt;&lt;/hr&gt;    &lt;p&gt;下面是我的微信公众号      &lt;a href="https://mp.weixin.qq.com/s?__biz=MzAxMTEyOTQ5OQ==&amp;mid=401032225&amp;idx=1&amp;sn=5e2e82e9102371e8f241e50c562208b2#rd" target="_blank"&gt;「瞬息之间」&lt;/a&gt;，除了写技术的文章、还有产品、行业和人生的思考，希望能和更多走在这条路上同行者交流。      &lt;br /&gt;      &lt;img alt="" src="https://images2015.cnblogs.com/blog/815275/201510/815275-20151013203630882-390531373.jpg"&gt;&lt;/img&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61564-mysql-%E6%95%B0%E6%8D%AE%E5%BA%93-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Sat, 26 Jun 2021 12:52:56 CST</pubDate>
    </item>
    <item>
      <title>使用logstash同步至ES的几个坑 - 一位帅气的网友的个人空间 - OSCHINA - 中文开源技术交流社区</title>
      <link>https://itindex.net/detail/61370-logstash-%E5%90%8C%E6%AD%A5-es</link>
      <description>&lt;div&gt;    &lt;h1&gt;1.前言&lt;/h1&gt;    &lt;p&gt;记录使用logstash从sqlserver同步数据到ES中遇到的几点问题。使用的版本是es6.8.3+logstash6.8.3&lt;/p&gt;    &lt;h1&gt;2.logstash配置文件&lt;/h1&gt;    &lt;h2&gt;2.1input&lt;/h2&gt;    &lt;pre&gt;      &lt;code&gt;input {
    jdbc {
        jdbc_driver_library =&amp;gt; &amp;quot;/usr/local/logstash-6.8.3/logstashconfs/sqljdbc4.jar&amp;quot;#sqlserver的驱动jar包jdbc_driver_class =&amp;gt; &amp;quot;com.microsoft.sqlserver.jdbc.SQLServerDriver&amp;quot;
        jdbc_connection_string =&amp;gt; &amp;quot;jdbc:sqlserver://192.168.1.101:1433;databaseName=test;&amp;quot;
        jdbc_user =&amp;gt; &amp;quot;sa&amp;quot;
        jdbc_password =&amp;gt; &amp;quot;123456&amp;quot;
        jdbc_default_timezone =&amp;gt; &amp;quot;Asia/Shanghai&amp;quot;
		jdbc_paging_enabled =&amp;gt; &amp;quot;true&amp;quot;#分页record_last_run =&amp;gt; true#记录上一次运行的值use_column_value =&amp;gt; true#使用数据库中的字段追踪tracking_column =&amp;gt; &amp;quot;update_time&amp;quot;#追踪的字段名称tracking_column_type =&amp;gt; &amp;quot;timestamp&amp;quot;#追踪的字段类型last_run_metadata_path =&amp;gt; &amp;quot;/usr/local/logstash-6.8.3/logstashconfs/sync-logs/consumer_statistics_update_time&amp;quot;#上一次运行的值存储的文件地址clean_run =&amp;gt; false#使用数据库中的字段追踪statement =&amp;gt; &amp;quot;SELECT * FROM v_test WHERE update_time&amp;gt;:sql_last_value and update_time&amp;lt;GETDATE() &amp;quot;#sql语句schedule =&amp;gt; &amp;quot;*/5 * * * * *&amp;quot;#每5s执行一次}
}&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;statement&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;由于要查的数据是表关联的数据,一开始想的是建立多个jdbc,把数据存到es的不同的索引中,利用父子文档进行关联查询,&lt;/p&gt;    &lt;p&gt;后来发现这种办法效率差,而且影响ES的性能,所以解决办法就是在sqlserver中建立好多表联查好的视图,&lt;/p&gt;    &lt;p&gt;这里的      &lt;code&gt;statement&lt;/code&gt;中的v_test就是创建好的视图.&lt;/p&gt;    &lt;p&gt;由于设置了Logstash 增量更新, 必须要使用      &lt;code&gt;update_time&amp;gt;:sql_last_value and update_time&amp;lt;GETDATE()&lt;/code&gt;这种限制条件,这样才可以保证数据不丢失也不重复&lt;/p&gt;    &lt;p&gt;具体原因见:      &lt;a href="https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fjuejin.im%2Fpost%2F5d1886d4e51d45775b419c22" rel="nofollow" target="_blank"&gt;如何使用 Logstash 实现关系型数据库与 ElasticSearch 之间的数据同步&lt;/a&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;schedule&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;网上的很多教程都说最小间隔是1min,实际上是可以做到秒级的.&lt;/p&gt;    &lt;p&gt;      &lt;code&gt;schedule =&amp;gt; &amp;quot;*/5 * * * * *&amp;quot;&lt;/code&gt;只要在前面再加一个* 单位就是秒,这里就是每5s执行一次&lt;/p&gt;    &lt;h2&gt;2.2filter&lt;/h2&gt;    &lt;pre&gt;      &lt;code&gt;filter {
	if ![test]{ruby{code =&amp;gt;&amp;apos;event.set(&amp;quot;test&amp;quot;,&amp;quot;&amp;quot;)&amp;apos;}}	
	mutate{
		convert =&amp;gt; { &amp;quot;id&amp;quot; =&amp;gt; &amp;quot;integer&amp;quot; }
		remove_field =&amp;gt; [&amp;quot;@timestamp&amp;quot;]
		remove_field =&amp;gt; [&amp;quot;@version&amp;quot;]
	}
}&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;这里主要是对从sqlserver数据库查出来的数据进行一些处理,我这里删去了大多数的内容,仅保留一些代表性的.&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;if ![test]{ruby{code =&amp;gt;&amp;apos;event.set(&amp;quot;test&amp;quot;,&amp;quot;&amp;quot;)&amp;apos;}}&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;这个的意思是 test字段为null时,使用ruby这个语言进行处理,      &lt;code&gt;code =&amp;gt;&amp;apos;&amp;apos;&lt;/code&gt;这里面就是写代码的&lt;/p&gt;    &lt;p&gt;      &lt;code&gt;event.set(&amp;quot;test&amp;quot;,&amp;quot;&amp;quot;)&lt;/code&gt;意思就是 设置test字段的内容为&amp;quot;&amp;quot;&lt;/p&gt;    &lt;p&gt;当然我们也可以先      &lt;code&gt;event.get(&amp;quot;test&amp;quot;)&lt;/code&gt;,获取test字段的内容,然后在进行一系列处理后,再      &lt;code&gt;event.set&lt;/code&gt;,这样就可以保存处理后的字段的值&lt;/p&gt;    &lt;p&gt;ruby语言的具体语法可以参考这个:      &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fwww.runoob.com%2Fruby%2Fruby-tutorial.html" rel="nofollow" target="_blank"&gt;Ruby教程&lt;/a&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;convert =&amp;gt; { &amp;quot;id&amp;quot; =&amp;gt; &amp;quot;integer&amp;quot; }&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;这个的意思就是将id字段的类型转化为integer,如果某个字段是时间类型可以转化为timestamp类型&lt;/p&gt;    &lt;h2&gt;2.3output&lt;/h2&gt;    &lt;pre&gt;      &lt;code&gt;output {
		elasticsearch {
			hosts =&amp;gt; [&amp;quot;htkj101:9200&amp;quot;,&amp;quot;htkj102:9200&amp;quot;,&amp;quot;htkj103:9200&amp;quot;]
			index =&amp;gt; &amp;quot;consumer_statistics&amp;quot;#索引名称document_id =&amp;gt; &amp;quot;%{id}&amp;quot;#索引的iddocument_type =&amp;gt; &amp;quot;consumer_statistics&amp;quot;#索引的type,这个在6.x版本以后就已经被废弃,可以忽略这个template_name =&amp;gt; &amp;quot;consumer_statistics&amp;quot;#索引模板的名称}
}&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;document_id =&amp;gt; &amp;quot;%{id}&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;文档的id就是导入数据的id,这样设置可以实现幂等性&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;template_name =&amp;gt; &amp;quot;consumer_statistics&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;索引模板的名称      &lt;code&gt;consumer_statistics&lt;/code&gt;,ES会调用模板名称为      &lt;code&gt;consumer_statistics&lt;/code&gt;创建索引.&lt;/p&gt;    &lt;p&gt;当然前提是你得先创建好这个模板&lt;/p&gt;    &lt;h1&gt;3.索引模板的创建&lt;/h1&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;p&gt;指令&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;curl -H &amp;quot;Content-Type: application/json&amp;quot; -XPUT http://htkj101:9200/_template/consumer_statistics -d &amp;apos;在这里输入你创建的模板&amp;apos;&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;p&gt;模板&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;{
	&amp;quot;template&amp;quot;: &amp;quot;consumer_statistics&amp;quot;,
	&amp;quot;order&amp;quot;: 2,
	&amp;quot;version&amp;quot;: 60001,
	&amp;quot;index_patterns&amp;quot;: [&amp;quot;consumer_statistics&amp;quot;],
	&amp;quot;settings&amp;quot;: {
		&amp;quot;index&amp;quot;: {
			&amp;quot;refresh_interval&amp;quot;: &amp;quot;5s&amp;quot;,
			&amp;quot;max_result_window&amp;quot;: &amp;quot;2147483647&amp;quot;#设置from+size的最大值}
	},
	&amp;quot;mappings&amp;quot;: {
		&amp;quot;_default_&amp;quot;: {
			&amp;quot;dynamic_templates&amp;quot;: [{
				&amp;quot;message_field&amp;quot;: {
					&amp;quot;path_match&amp;quot;: &amp;quot;message&amp;quot;,
					&amp;quot;mapping&amp;quot;: {
						&amp;quot;norms&amp;quot;: false,
						&amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;
					},
					&amp;quot;match_mapping_type&amp;quot;: &amp;quot;string&amp;quot;
				}
			}, {
				&amp;quot;string_fields&amp;quot;: {
					&amp;quot;mapping&amp;quot;: {
						&amp;quot;norms&amp;quot;: false,
						&amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;,
						&amp;quot;fields&amp;quot;: {
							&amp;quot;keyword&amp;quot;: {
								&amp;quot;ignore_above&amp;quot;: 1024,#设置不被索引的字段长度&amp;quot;type&amp;quot;: &amp;quot;keyword&amp;quot;
							}
						}
					},
					&amp;quot;match_mapping_type&amp;quot;: &amp;quot;string&amp;quot;,
					&amp;quot;match&amp;quot;: &amp;quot;*&amp;quot;
				}
			}],
			&amp;quot;properties&amp;quot;: {
				&amp;quot;@timestamp&amp;quot;: {
					&amp;quot;type&amp;quot;: &amp;quot;date&amp;quot;
				},
				&amp;quot;geoip&amp;quot;: {
					&amp;quot;dynamic&amp;quot;: true,
					&amp;quot;properties&amp;quot;: {
						&amp;quot;ip&amp;quot;: {
							&amp;quot;type&amp;quot;: &amp;quot;ip&amp;quot;
						},
						&amp;quot;latitude&amp;quot;: {
							&amp;quot;type&amp;quot;: &amp;quot;half_float&amp;quot;
						},
						&amp;quot;location&amp;quot;: {
							&amp;quot;type&amp;quot;: &amp;quot;geo_point&amp;quot;
						},
						&amp;quot;longitude&amp;quot;: {
							&amp;quot;type&amp;quot;: &amp;quot;half_float&amp;quot;
						}
					}
				},
				&amp;quot;@version&amp;quot;: {
					&amp;quot;type&amp;quot;: &amp;quot;keyword&amp;quot;
				}
			}
		}
	},
	&amp;quot;aliases&amp;quot;: {}
}&lt;/code&gt;&lt;/pre&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;&amp;quot;max_result_window&amp;quot;: &amp;quot;2147483647&amp;quot;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;在业务处理的过程中往往需要分页,ES的JAVA-API是通过from,size来设置分页数量和每页的数量,&lt;/p&gt;    &lt;p&gt;在默认的情况下from+size必须要小于10000,但是如果实际需求大于10000,则必须在这里设置&lt;/p&gt;    &lt;p&gt;我这里设置的是      &lt;code&gt;max_result_window&lt;/code&gt;的最大值,实际情况中不需要设置如此之大,&lt;/p&gt;    &lt;p&gt;因为ES会在内存中进行排序,如果一次返回的结果过大,可能会导致服务宕机.&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;        &lt;code&gt;&amp;quot;ignore_above&amp;quot;: 1024&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;这里默认是256,意思是如果某一个字段的内容超过256字节的话,那么将不会被索引.&lt;/p&gt;    &lt;p&gt;也就是说从ES中是能够看到这条数据的存在,但是如果你指定查询条件,是查不出来的.&lt;/p&gt;    &lt;p&gt;举个例子,现在ES中有id,test两个字段,一共100条数据&lt;/p&gt;    &lt;p&gt;test字段中只有一条数据超过了256字节,现在我查询test字段中包含&amp;quot;1&amp;quot;的数据,&lt;/p&gt;    &lt;p&gt;即使这个超过256字节的数据含有1,但是也不会被查询到.&lt;/p&gt;    &lt;p&gt;为了能够让他被索引到,这里将256改成1024,即只有超过1024字节才会不被索引.&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;完整命令&lt;/li&gt;&lt;/ul&gt;    &lt;pre&gt;      &lt;code&gt;curl -H &amp;quot;Content-Type: application/json&amp;quot; -XPUT http://htkj101:9200/_template/consumer_statistics -d &amp;apos;
{&amp;quot;template&amp;quot;:&amp;quot;consumer_statistics&amp;quot;,&amp;quot;order&amp;quot;:2,&amp;quot;version&amp;quot;:60001,&amp;quot;index_patterns&amp;quot;:[&amp;quot;consumer_statistics&amp;quot;],&amp;quot;settings&amp;quot;:{&amp;quot;index&amp;quot;:{&amp;quot;refresh_interval&amp;quot;:&amp;quot;5s&amp;quot;,&amp;quot;max_result_window&amp;quot;:&amp;quot;2147483647&amp;quot;}},&amp;quot;mappings&amp;quot;:{&amp;quot;_default_&amp;quot;:{&amp;quot;dynamic_templates&amp;quot;:[{&amp;quot;message_field&amp;quot;:{&amp;quot;path_match&amp;quot;:&amp;quot;message&amp;quot;,&amp;quot;mapping&amp;quot;:{&amp;quot;norms&amp;quot;:false,&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;},&amp;quot;match_mapping_type&amp;quot;:&amp;quot;string&amp;quot;}},{&amp;quot;string_fields&amp;quot;:{&amp;quot;mapping&amp;quot;:{&amp;quot;norms&amp;quot;:false,&amp;quot;type&amp;quot;:&amp;quot;text&amp;quot;,&amp;quot;fields&amp;quot;:{&amp;quot;keyword&amp;quot;:{&amp;quot;ignore_above&amp;quot;:1024,&amp;quot;type&amp;quot;:&amp;quot;keyword&amp;quot;}}},&amp;quot;match_mapping_type&amp;quot;:&amp;quot;string&amp;quot;,&amp;quot;match&amp;quot;:&amp;quot;*&amp;quot;}}],&amp;quot;properties&amp;quot;:{&amp;quot;@timestamp&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;date&amp;quot;},&amp;quot;geoip&amp;quot;:{&amp;quot;dynamic&amp;quot;:true,&amp;quot;properties&amp;quot;:{&amp;quot;ip&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;ip&amp;quot;},&amp;quot;latitude&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;half_float&amp;quot;},&amp;quot;location&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;geo_point&amp;quot;},&amp;quot;longitude&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;half_float&amp;quot;}}},&amp;quot;@version&amp;quot;:{&amp;quot;type&amp;quot;:&amp;quot;keyword&amp;quot;}}}},&amp;quot;aliases&amp;quot;:{}}&amp;apos;&lt;/code&gt;&lt;/pre&gt;    &lt;p&gt;在创建模板的过程中,发现总是创建失败,后来发现弄成这样的两行,就不会出错了.&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61370-logstash-%E5%90%8C%E6%AD%A5-es</guid>
      <pubDate>Mon, 26 Apr 2021 15:52:26 CST</pubDate>
    </item>
    <item>
      <title>数据同步工具 Elasticsearch-datatran v6.2.9 发布</title>
      <link>https://itindex.net/detail/61356-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;
                                                                    
                                                          &lt;p&gt;数据同步工具 Elasticsearch-datatran 6.2.9 发布，   &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fesdoc.bbossgroups.com%2F%23%2Fdb-es-tool" target="_blank"&gt;Elasticsearch-datatran&lt;/a&gt; 由    &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fesdoc.bbossgroups.com%2F%23%2FREADME" target="_blank"&gt;bboss &lt;/a&gt;开源的数据同步迁移工具，支持在Elasticsearch、关系数据库(mysql,oracle,db2,sqlserver、达梦等)、Mongodb、HBase、Hive、Kafka、文本文件、SFTP/FTP多种数据源之间进行海量数据同步；支持日志文件实时增量采集到kafka/elasticsearch/database。&lt;/p&gt; 
  &lt;p&gt;   &lt;strong&gt;Elasticsearch版本兼容性：支持&lt;/strong&gt;各种Elasticsearch版本（1.x,2.x,5.x,6.x,7.x,+）之间相互数据迁移&lt;/p&gt; 
  &lt;p&gt;   &lt;img alt="" height="572" src="https://esdoc.bbossgroups.com/images/datasyn.png" width="781"&gt;&lt;/img&gt;&lt;/p&gt; 
  &lt;p&gt;   &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fesdoc.bbossgroups.com%2F%23%2Fchangelog%3Fid%3Dv629-%25e5%258a%259f%25e8%2583%25bd%25e6%2594%25b9%25e8%25bf%259b" target="_blank"&gt;v6.2.9 功能改进&lt;/a&gt;&lt;/p&gt; 
  &lt;ol&gt; 
    &lt;li&gt;     &lt;p&gt;数据同步改进：完善ip2region和geoip数据库热加载机制&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;Restclient改进：升级httpcliet组件版本到最新的官方版本4.5.13&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;Restclient改进：升级fastxml jackson databind版本2.9.10.8&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;Restclient改进：增加对elasticsearch pit机制的支持，参考用例：&lt;/p&gt;     &lt;p&gt;testPitId方法&lt;/p&gt;     &lt;p&gt;     &lt;a href="https://gitee.com/bboss/eshelloword-spring-boot-starter/blob/master/src/test/java/org/bboss/elasticsearchtest/springboot/SimpleBBossESStarterTestCase.java" target="_blank"&gt;https://gitee.com/bboss/eshelloword-spring-boot-starter/blob/master/src/test/java/org/bboss/elasticsearchtest/springboot/SimpleBBossESStarterTestCase.java&lt;/a&gt;&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;数据同步工具扩展：增加日志文件采集插件，支持全量和增量采集两种模式，实时采集日志文件数据到kafka/elasticsearch/database&lt;/p&gt;     &lt;p&gt;使用文档：     &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fesdoc.bbossgroups.com%2F%23%2Ffilelog-guide" target="_blank"&gt;https://esdoc.bbossgroups.com/#/filelog-guide&lt;/a&gt;&lt;/p&gt;     &lt;p&gt;日志文件采集插件使用案例：&lt;/p&gt; 
      &lt;ol&gt; 
        &lt;li&gt;      &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbbossgroups%2Ffilelog-elasticsearch%2Fblob%2Fmain%2Fsrc%2Fmain%2Fjava%2Forg%2Fframeworkset%2Felasticsearch%2Fimp%2FFileLog2DBDemo.java" target="_blank"&gt;采集日志数据并写入数据库&lt;/a&gt;&lt;/li&gt; 
        &lt;li&gt;      &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbbossgroups%2Ffilelog-elasticsearch%2Fblob%2Fmain%2Fsrc%2Fmain%2Fjava%2Forg%2Fframeworkset%2Felasticsearch%2Fimp%2FFileLog2ESDemo.java" target="_blank"&gt;采集日志数据并写入Elasticsearch&lt;/a&gt;&lt;/li&gt; 
        &lt;li&gt;      &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fgithub.com%2Fbbossgroups%2Fkafka2x-elasticsearch%2Fblob%2Fmaster%2Fsrc%2Fmain%2Fjava%2Forg%2Fframeworkset%2Felasticsearch%2Fimp%2FFilelog2KafkaDemo.java" target="_blank"&gt;采集日志数据并发送到Kafka&lt;/a&gt;&lt;/li&gt; 
  &lt;/ol&gt;     &lt;p&gt;之前版本升级6.2.9注意事项，需手动修改增量同步状态表结构，增加下面三个字段：&lt;/p&gt;     &lt;pre&gt;     &lt;code&gt;status number(1) ,  //数据采集完成状态：0-采集中（默认值）  1-完成  适用于文件日志采集 默认值 0
filePath varchar(500)  //日志文件路径，默认值&amp;quot;&amp;quot;
fileId varchar(500)  //日志文件indoe标识，默认值&amp;quot;&amp;quot;&lt;/code&gt;&lt;/pre&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;Restclient改进：设每个elasticsearch数据源默认版本兼容性为7，为了处理启动时无法连接es的情况，可以根据连接的es来配置和调整每个elasticsearch数据源的配置，示例如下： elasticsearch.version=7.12.0&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;调整gradle构建脚本语法，保持与gradle 7的兼容性&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;Restclient改进：elasticsearch节点自动发现和故障节点健康检查后台线程模型调整为daemon模式&lt;/p&gt; &lt;/li&gt; 
    &lt;li&gt;     &lt;p&gt;http-proxy改进：http-proxy节点自动发现和故障节点健康检查后台线程模型调整为daemon模式&lt;/p&gt; &lt;/li&gt; 
&lt;/ol&gt;
                                        &lt;/div&gt;
                                    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61356-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Mon, 19 Apr 2021 22:33:41 CST</pubDate>
    </item>
    <item>
      <title>超3亿活跃用户的多活架构，数据同步与流量调度怎么做？ - 架构 - dbaplus社群：围绕Data、Blockchain、AiOps的企业级专业社群。技术大咖、原创干货，每天精品原创文章推送，每周线上技术分享，每月线下技术沙龙。</title>
      <link>https://itindex.net/detail/61298-%E7%94%A8%E6%88%B7-%E6%9E%B6%E6%9E%84-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;p&gt;    &lt;strong&gt;一、多活业务架构&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;1、OPPO多活架构原则   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第一，主线多活。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;多活成本比较高的，双活是两倍，三活可能成本会低一些，但三活的难度更大。因此没有办法对所有业务进行多活，只能对主线做多活。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第二，是保障多数用户。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;举个例子，系统有个充值的功能，充值功能本身是强一致的，完全不能允许任何的延迟或者是副本的读。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;但是多活切换之后，只有少数用户在切换的前几分钟有充值的，这部分用户余额可能没有通过过去，只需要对这部分用户进行服务降级，其他绝大多数用户是可以使用完整的服务的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第三，数据分类，应用不同的CAP模型。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;CAP定理不是针对的业务功能，比如说账号、支付、登录，CAP定理是对数据的要求。一个功能可能用到多个数据，数据本身的一致性、可用性、延迟的容忍是不一样的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;所以需要对业务功能用到的数据进行分类，比如余额数据、流水数据、日志数据、个人资料数据……我们对每个数据进行一致性、可用性的需求分析，一致性要求很强，这个数据就选用同城高可用的数据库服务。这个数据一致性要求不高、允许延迟，就可以选择异地高可用的数据库服务。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;所以这个业务来说不是整体使用一个CAP模型，在业务内部，因为不同的数据分类，使用了不同的模型，因此业务有时候存在部分降级的情况。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第四，平台业务SDK化。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;OPPO的业务比较多，比如浏览器、软件商店、广告务、音乐、视频等非常多的业务，这些业务都用了平台化的服务，比如评论系统、消息系统，还有账号鉴权的系统等等。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;OPPO公司的机房比较多，主要的就有好几个机房，我们的上层业务是分布在不同的机房里面去，这对平台业务来说就比较麻烦，上层业务可能只需要做双活就行了，而平台业务可能就要做七活、甚至八活，而且七、八个机房都要有读和写，难度就非常大。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;为解决这个问题，我们提出平台业务进行SDK化思路，把这种平台型业务，拆分成独立的域名，从SDK开始拆分，这样我们平台业务只需要单独做多活就行了，不需要在每个机房都提供读写的能力。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第五，数据最终一致。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第六，我们的记录日志、流水，避免修改、计数操作。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;2、同城多活业务架构   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095350816.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是典型同城多活的业务架构，应用层是完全无状态的，随便打流量。四层采用DPDK技术开发，七层包括Nginx和API网关两个组件，Nginx只用来做SSL卸载、WAF防火墙，其他功能都是API网关来提供。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;数据层以主备为主，写流量只会写到Master节点，但是读的流量可以访问slave节点，但是也不一定，看业务本身数据一致性要求，如果要求非常强一致的，我们的读也只会指向Master节点。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;需要注意，我们把Nginx和API网关都放到同一个容器中，两只之间采用进程间通信。这样的好处是，我们扩容的时候，我们可以将整个七层同步去扩容，而不会存在某一层组件容量不足的情况。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;另外就是注册中心，我们没有使用 k8s本身的一个注册功能，而是自己基于数据库，实现了AP模型的注册中心，保证注册中心的跨机房高可用。同时注册中心兼容Consul协议，从而更好的融入开源生态。多个k8s集群的实例，都会注册到统一的注册中心里面去。这个注册的动作，是由发布平台完成的，好处是应用发布的时候，发布平台可以提前摘掉流量，避免重启影响服务的成功率。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;3、异地多活业务架构——单元化   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095400868.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;异地多活，比较典型的架构是单元化，就是将用户进行分片，将不同的用户分片放到不同的机房里面去，这样可以做到一个完全的扩展，随着用户规模的增加，我们可以很容易去扩展机房的数量，这些都可以持续的去增加的，包括每个机房的容量也可能不一样。比如有的机房大，有的机房小，我们可以调整每个机房存放的单元数量。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这里确实实现了多活，每个机房都有流量，每个机房也是读写，是完全的多活，但是单元高可用的问题如何解决，单元的归属机房故障了，如果把这个单元转移另一个机房继续提供服务。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;4、异地双活业务架构   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095426496.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是我们使用较多的异地双活架构，首先我们将用户按照地域维度进行了一个单元划分，比如说按照地域将用户划分为七个大区单元。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;注意这个单元划分是用户首次访问服务的时候进行的，然后客户端就保存了单元号，就不会产生变化了，所以用户出差，换到因为另外一个地域里面去，它所属的单元号我们是不会变化的，还是访问单元归属的机房，这个时候可能就不是访问最优的机房。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这样的好处是，当一个用户移动的时候，数据访问就不会在两个机房之间跳来跳去，避免双向同步的数据冲突问题，很容易实施。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;数据层在两个机房，都是完全全量的，两个机房间数据是做双向同步，没有谁是主谁是从的区分，是完全对等的架构。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;用户流量调度按单元进行，这样可以保证一个用户，他只会访问其中一个机房，不会在南北两个机房之间跳来跳去，就算是用户出差也是如此，按照首次访问服务时的地域来划分的单元。只要我们的调度规则没有变更的情况下，一个用户他永远只会在其中一个机房读写，这样的好处就是，第一个可以避免我们的同步的冲突，第二个好处就是容忍了数据延迟的情况，比如说一个用户他永远是看到北方机房，南北之间数据同步的延迟日常情况下其实是感知不到的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这个架构是非常简单，只需要在客户端网络库里面做一些封装，对用户进行单元划分，按单元进行流量调度就可以了，双向同步比较好实施，延迟、冲突，这些问题都可以避免。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;除了地域之外，也可以按照账号或者设备来划分单元。按账号或者设备划分单元的好处是，如果按照地域划分单元，在用户删除手机APP这种情况下，APP里面保存的单元号就没有了，下次访问服务的时候就需要重新分配单元号，因为地域可能和之前不同了，就可能分配到不同的单元号，按账号或者设备划分就没有这个问题，重新分配还是原来的单元号。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;前面说到，南北机房的数据层都是全量的，一般情况下，按地域的划分单元的模式，就算重新分配了单元号，也不影响数据的读写访问。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;5、异地双活——评论系统案例   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095447395.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是平台型业务-评论系统异地生活的案例，评论系统从SDK开始，就进行了域名拆分，避免了在业务域名所在机房内部去做跨机房的评论服务调用，影响服务的可用性和性能。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;如上图所示，我们只对MySQL原始数据层做了南北双机房同步，第二层的评论元数据表，还有第三层的一个Cache，这两层实际上没做同步的。两个机房分别基于MySQL数据独自去重建第二层的元数据表，第三层的Cache，以及重建其他的数据源。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这样好处就是，我们只有一个数据源做了南北机房的同步，就可以避免双数据源同步的时候，两个数据源之间会存在同步的进度不一致，从而两个数据源之间的依赖关系出现问题。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;举个例子，我们上面的评论表、点赞表这一层，最上面这一层做了同步以后，我们中间第二层如果也做了同步，然后第二层同步以后，两个数据可能存在差异，比如说第一层同步快一点，第二层同步得慢一点，同样是南方的用户，他们看到这个数据之间的存在不匹配的问题。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;因为用户流量调度是按单元进行的，两个机房的数据虽然有差异，有延迟，但是用户感知不到的。一个用户要么看到南方机房，要么看到北方机房，我们评论数量两个机房有差异，点赞数量有差异，回复数都有差异，但是无所谓，用户是感知不到差异的。需要注意的一点，就是当多活切换的时候，用户能感知到一个差异，但日常情况下用户感知不到这个差异。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;6、异地N活业务架构   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095455738.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是比较复杂异地N活业务架构。它基本的思路就是对用户进行两级的划分。第一级按照设备和账号划分单元，其中单元里面既有登录的用户，也有未登录的用户。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;在第二级划分的单元内部，我们再应用异地双活的模式，或者是同城多活的模式，比如说左边单元1，按照地域做第二级的划分，把它划分成南北两个副本，既然是副本，肯定数据是全量的，是异地双活模式，两个副本数据做双向同步，这种模式适用非强一致的业务。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;那么强一致的业务怎么办呢？比如右边的单元4，跨同城的两个机房，单元内部采用同城多活的模式，就是共享跨机房高可用的数据层，是主备的的。这种模式适合强一致的业务。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;前面说了单元内部主要两种模式，第一种是异地双活，双向同步，主主模式，读写在本机房，然后做双向同步；第二种是同城多活，主备的模式，跨机房共享主备切换的数据层。除此之外，单元内部还可以选择主从，冷备等模式。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;7、服务部署   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095512583.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是服务部署架构。服务部署分为几大部分。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第一部分是中心域。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;中心域主要是部署一些运营管理后台，还有一些爬虫，还有一些非常长尾的应用，但这些业务可能不太重要，也不需要做一个多活。中心域的读写都是在中心机房，然后把数据单向同步到其他单元机房。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第二部分是全局域。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;全局域主要存放非单元分片维度的数据，比如评论、消息等。这些数据不能按统一维度进行拆分，需要全量的访问，放到全局域的数据都是全量的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第三部分是单元域。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;存放按单元拆分的数据，比如用户订单、收藏、下载记录等。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;8、服务路由   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095527276.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;用户会先请求到API网关，API网关根据请求的单元号参数，判断是是否访问错了机房，如果访问错了，就做重定向，或者跨机房转发，用户自己选择的其中一种模式。转发的模式比较依赖于两个机房之间的专线的带宽和稳定性，重定向模式机房之间的带宽要求会低一些，客户端重新发起请求，这两个机房之间的网络专线要求低一些。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;前面说到用户首次请求的时候，会给客户端分配一个单元号，这个单元号将会存储起来，以后每次业务请求都会带上这个单元号。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;请求到了单元内部，单元号会做一个全链路的传递，全链路传递是通过调用链来实现的，调用链可以把一些参数做全链路的传递。应用实例打上了单元号的标签，微服务调用方通过单元号对实例进行筛选，防止请求打到其他单元。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;数据访问层要做一个兜底的操作，可能由于服务路由还是其他的一些原因，不小心访问错了单元，这个数据层有可能访问错，所以数据访问层要做一个兜底，根据传过来单元号，做拒绝或者转发。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;9、用单元化解决业务扩展性问题   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095536642.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;单元化不只是可以用来解决多活的问题，也可以用来解决业务扩展性问题。在一个机房内部，如果服务1000万用户，他可能需要10个数据库，服务1亿个用户，需要100个数据库，如果100个数据库让每个应用实例都连上的话，连接数就太多了。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;可以在一个机房内部也拆分多个单元，每个单元保证1000万、2000万左右的用户，随着用户的增长，我们再将单元数量进行增加就行了，这样就可以保证每一个单元内部的服务规模受控。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;二、多活数据同步&lt;/strong&gt;&lt;/p&gt;  &lt;h1&gt; &lt;/h1&gt;1、MySQL同城多活   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095552377.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是MySQL同城多活架构，MySQL对外看上去是一个集群，只有一个IP。我们需要解决的问题是：怎么让跨机房的集群看到的是同一个IP？这里就用到了Anycast技术，IP的作用可以理解为域名，我们把一个 VIP用Anycast技术，将它路由到两个机房，或者是三个机房。我们是路由到三个机房，然后就到了机房内部，再通过 ECMP协议将流量再分到多个四层负载均衡节点。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;通过Anycast第一层路由到不同的机房，第二层的ECMP再路由到基于DPDK技术开发的四层负载均衡节点。这样我们整个的数据库对外看到的VIP就是同一个了，所有机房看到VIP都是同一个。利用Anycast和ECMP两个技术，实现跨AZ共享VIP。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;然后是数据层，数据层我们现在是一主三从，然后需要2个以上slave同步成功，才能完成最终的成功。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;MySQL版本需要5.7以上，操作系统内核需要打一个 toa补丁，这样经过四层负载均衡之后，MySQL Server才能拿到真正来源IP。因为我们这边要做一个IP白名单的授权，如果不打补丁，拿到的来源IP就是四层负载均衡的IP，就没法做IP白名单授权了。当然top补丁有一个缺陷，就是只能支持ipv4，这在内网使用问题不大。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;底层采用了开源的MySQL拓扑管理组件，通过检测我们数据库节点的情况，然后做重新选组做切换，然后通知SLB改变后端指向，流量打到新的master节点，&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Anycast不是必须的，也可以用域名代替，但是域名有个问题，需要重新接连的时候才会发起解析，所以域名切换的时候可能会切不干净。Anycast做切换是立即生效的，因为这是路由协议的一个变更，马上就能切过去，不存在解析不干净和生效不一致的问题。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;Anycast除了内网之外，外网也用的比较多，比如说谷歌上负载均衡器，它发布的IP就是Anycast的IP，在公网环境下，在不同的地区路由到不同的一个真实地址，包括我们 DNS Server也是用Anycast去发布的，在不同的区域，路由到就近的IDC，所以Anycast技术应用还比较广泛。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;2、MySQL异地多活   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095559718.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;上图是MySQL的异地多活架构，重点在于提升同步的性能，从源库订阅到数据以后，不是直接写目标库，而是先存起来，在目标机房部署中继日志模块。这样的好处是，我们可以在网络上快速的传输过去，中继日志并行去写目标库。&lt;/h2&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这个设计性能提升非常大，OPPO实际业务场景下，这个模式比订阅后直接写目标库提升了几倍。因为引入了中继日志，就存在两阶段提交的问题。比如中继日志写成功，但是中继日志写目标库没有成功。这就存在数据一致性问题，需要用到两阶段提交。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;还有就是数据压缩和加密，对数据的安全和同步性能也非常重要。&lt;/p&gt;  &lt;p&gt;然后是多消费者支持，订阅模块会保存数据，每个订阅方可以维持自己的消费位点，彼此之间没有干扰，从而减少多订阅方同步对 Source DB的压力。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;3、MySQL订阅——数据最终一致   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095606170.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;以前面提到的评论系统为例，数据同步只同步MySQL那一层，而其他的数据源Cache、MQ、ES、排序服务等，分别订阅MySQL binlog重新构建。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;原则上，我们尽量只同步底层的一份MySQL数据，其他数据源订阅MySQL重建。前面说到，MySQL只需要订阅一次，Jins程序自己存储了一份数据到本地文件队列，然后分别重放到Cache、MQ、ES等其他数据源，也可以多次重放数据。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;如果多数据源分别进行同步的话，多个数据源同步的进度是没法保证协调一致的，必然有的数据源快，有的数据源慢，这有可能导致两个数据源之间的关联关系出现一些程序错误。所以我们尽量只同步一个数据源，再基于MySQL重建其他的数据源，避免进度不一致的问题。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;4、MySQL数据对比&amp;amp;修复   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095623339.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;OPPO的业务场景，很多地方都非常依赖底层的 MySQL数据同步，两个机房之间之间到底有没有差异，是蛮重要的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;因此我们设计了一个独立的MySQL比对修复工具，就执行上图这样一个SQL语句，通过这个SQL语句，对一段时间之内的所有数据算一个异或的值，通过异或值去比对两个机房之间数据差异，如果比对有差异，我们再缩小比对范围，逐步逼近到差异的记录行，这个语句的执行效率还是蛮高的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;但是这个方案有个不足，要求我们数据库里面有一个时间戳的字段，程序会对比前一个周期内的所有记录的异或值，判断两个机房之间数据是否有差异。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;另外一重要场景就是数据修复，因为业务可能配置错了数据库、应用实例配置生效不一致，再比如A单元数据写到B单元，这个时候需要修复数据，通过这个工具，把两个数据库不一致的数据行整理出来，然后人工做识别或者批量修复。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;5、Redis多活   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095632389.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;Redis同城多活的架构如上图所示，我们在Redis Server上面做了一层代理，下层Redis Server没有使用Redis cluster技术，代理将流量进行分片，分发到了不同的Redis Group里面去，每个Group里面就是普通的Redis主从。&lt;/h2&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;主从之间采用binlog的同步，因为Redis本身没有binlog，我们把 AOF做了改造，把让它变成binlog的这种格式，这里改造的工作量不大。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;然后代理也支持两种模式，一种是重定向模式，一种是转发模式。转发模式就是写主读从，它只会把写流量转到了主机房里面去，但是从机房是能读的。重定向模式就不一样，重定向模式是非常更强一致的，读写都只能在主机房。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;前面反复提到，CAP是针对数据的，是指数据本身的延迟或者差异的容忍度，所以这两种模式都需要支持，有的数据它就是要强一致，一定要到主库里面的去读，但有的数据它允许从库读，允许延迟。&lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095719807.png"&gt;&lt;/img&gt; &lt;/p&gt;  &lt;p&gt;异地多活也很简单，异地多活两个机房各部署一个组件去订阅同机房的Redis，订阅Redis的binlog，订阅的数据写到MQ里面去，两个机房分别重放binlog，实现起来并不复杂。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;最后简单说一下binlog的格式，里面包括了命令、数据产生的机房、递增的序号，还有一个时间戳。还需要注意的一点，Redis持久化RDB也要改造一下，RDB需要包含一个 binlog offset，binlog读取偏移量，需要把它记下来，因为主从颠倒的时候，我们订阅程序要重新从offset开始继续订阅下面的命令。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;三、GSLB流量调度&lt;/strong&gt;&lt;/p&gt;  &lt;h1&gt; &lt;/h1&gt;1、Http DNS   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095754485.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;最后讲我们的GSLB流量调度，首先是为什么要使用Http DNS。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第一个是防劫持。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;DNS劫持，DNS是多级缓存，部分环节存在解析劫持的情况。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;DNS黑洞，这个大家可能遇到比较少，什么叫DNS黑洞呢？就是运营商监控到某个域名有恶意的请求，封杀他的时候不小心扩大了封杀的范围，我们已经出现过几次这种情况，有时候某个地区甚至可以把整个cn顶级域名全封杀，这种封杀的范围很大，称之为DNS黑洞。整个2020年已经发生过多次这种情况了，某个地域整个顶级域名都给你封杀掉，大家都解决不了。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第二个是快速生效。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;首先是DNS本身的多级缓存，这个时间不受控制，但它可能不是主要问题，更主要的问题是客户端长连接。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;我们还没上Http DNS之前，业务使用了客户端长连接，需要20分钟甚至一个小时才能大部分流量调度走。主要的原因就在客户端长连接，DNS做了变更以后，只有客户端重新发起连接的时候，它才会发重新发起解析，才拿到新的IP，如果连接没断开，就一直不会转移，所以这部分长连接用户根本就切不走。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;如果是机房入口网络故障还好，连接天然会断开，如果是因为业务自己的问题，需要把流量切走，这种情况下就会发现根本切不走，所以客户端长连接是比较重要的问题。所以客户端网络库需要处理一下，解析变更的时候，需要主动去关闭连接，但是传统DNS，没有解析变更的通知机制，不发起解析就不知道解析变更了，这里就进入了循环了，需要仔细的思考一下流程。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第三个是精准调度。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;传统的DNS解析只能获取到IP这一个参数，首先IP信息不准确，包括运营商归属、地域归属，都不是很准确，国外运营商特别多，情况更严重。现在IPv6也在快速的推广，信息不准确的情况更为严重。其次传统DNS无法做到用户维度、设备维度的解析。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;最后是生效一致性。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;单元一旦发生调度以后，在单元内的所有用户要同时调走，不能说一部分先调走，一部分后调走，这样数据写入就乱了，需要保证全体用户生效的一致性。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;2、单元调度   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095808523.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;下面讲单元调度的主要流程&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第一步: 划分用户单元&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;划分用户单元主要有三种模式：&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;p&gt;按设备划分单元；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;按账号划分单元；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;按地域划分单元。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这里有个地方需要注意，我们为了划分单元，客户端肯定要传一些参数，如果按账号划分单元，需要传账号ID；按设备划分单元，需要传设备的IMEI,或者国内Android厂商推行的OpenID；按地域划分单元很简单，直接从IP里面可以获取，不用客户端传递参数。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;因为隐私合规要求，比如说海外业务，直接传用户的ID或设备信息，是违规的，因为我们这个调度的域名它是一个独立的域名，它不是业务本身的，这个域名很难跟用户解释，即使跟用户签了协议，因为业务主体的不同，可能也不一定包含了这个域名，所以我们做了一个匿名化处理，设计了两个新参数，一个叫ADG（匿名设备分组），一个叫AUG（匿名用户分组）。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;我们将账号ID和10万取模的值定义为AUG,设备ID与10万取模的值定义为ADG。通过这种方式，把设备和账号分成10万个桶，然后对桶分单元，比如说1~5000桶是单元1，5000~1万桶是单元2。这样我们就不用传真实的设备ID和真实账号。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第二步：客户端获取单元号。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;客户端首次访问业务的时候要分一个单元号，这样就算按地域划分单元，基本上也不会出现变更，只要用户的APP不被删除，我们OPPO手机的好处就是，我们的APP是不怕被删除的，我们的数据不会被清掉；但如果是一个外发的APP，可能就存在APP删除，这个可以考虑用设备或者账号分单元。获取到单元号之后，就永久保存在客户端。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第三步：客户端解析域名IP。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;域名解析的时候会带上单元号的参数，获取这个单元对应的IP列表，然后客户端缓存IP列表。需要注意的一点是缓存机制，建议根据网络环境进行缓存，比如WiFi名称，或者运营商的名称，底层的缓存数据结构就是域名加上网络环境的名称。这样的好处就是，用户网络切换的时候，比如说家里面是WiFi，我们拿的是IP1，我们一出门，网络环境变了，我们取出的缓存IP就是IP2，在每个网络环境都是缓存最优的IP。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;另外一点需要注意是：我们为每个单元还分配了一个单元域名，这是一个传统DNS域名，主要是降级的时候使用。可以设想一下，如果我们没有为每个单元单独分配一个传统DNS域名，一旦降级的时候就会走到业务的主域名，而传统DNS是不能携带任何参数的，无法做到按单元进行解析，用户流量就全都乱了。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;所以每个单元分配一个域名的好处就是，降级的时候只要降级到我们这个单元的域名，这样大多数用户解析结果还是准确的，不准确的一部分通过API网关重定向或者内部转发，只要很少用户需要走这个路径，绝大多数用户还是最佳的路径。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;第四步：客户端重定向。&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;因为调度过程当中还有一部分用户在访问旧的IP，我们是通过API网关，把新机房IP直接告诉客户端，客户端立即用新IP重试，并且异步去刷新解析，如果只是反馈一个状态码，告诉客户端需要重新刷新解析，客户端的总请求时间就会拉得比较长，这就是重定向模式。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;但除了重定向模式，还有转发模式，但是转发模式比较依赖机房之间的专线带宽和稳定性，如果公司规模不是很大的话，机房之间的专线带宽和稳定性可能赶不上公网，重定向模式可能更适合一些。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;3、单元调度注意事项   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095820520.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;数据层联动，举一个用户余额充值的例子，这是非常强一致的，我们可以维护一个数据不一致用户清单，比如说有用户刚刚进行了充值，这个数据还没在各个机房达成一致，机房调度的时候，只是这一部分用户需要进行服务降级，其他用户还可以继续提供完整的服务。&lt;/h2&gt;  &lt;p&gt; &lt;/p&gt;4、域名解析刷新时机   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095831681.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;接下来讲域名解析刷新时机。因为HttpDNS是直连解析的，不像传统DNS有多级的缓存，如果我们还沿用传统DNS的 TTL方式来刷新解析，这个TTL就不能设置的太短，太短了HttpDNS Server的压力非常大。TTL设置过长又不能满足业务快速恢复的要求。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;所以域名解析的及时刷新依赖另外两种途径，第一种途径是失败。我们请求一个服务，要么连接错误，要么响应内容出现错误，比如说我们响应了500，或者其他我们认可的一个响应值（客户端可以自己定一个规则），我们访问失败的时候，就需要立即去刷新一下域名解析，因为请求失败的时候可能需要做一个机房调度，不管是业务后端出现了问题，或者是连接不上，这种情况都需要做机房调度，需要客户端刷新解析。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;第二种途径是指令，如果是因为我们带宽不足，做活动，或者其他原因的，需要把流量切走，这时我们可以通过API网关下发指令，下发指令也是随着API网关的正常的业务请求，响应Header带下去，不是单独的通道，也不是通过Push推送。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;这样我们就可以兜底，要么会请求失败，会立即刷新解析。要么请求成功，响应header就会携带指令。所以用户一定能走到失败和指令其中一条路径。因此我们做了调度变更以后，用户一定会刷新，不再依赖TTL了，过期时间可以设置非常长，这样我们绝大多数请求，都不会发生真正的解析请求。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;通常情况下，传统DNS有2%~3%的解析失败率，还是挺高的。通过这种方式，我们就可以把解析成功率做到99.5%以上，日常情况下甚至能做到接近100%。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;5、调度生效一致性   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095857727.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;下面讲一讲调度生效的一致性，当我们的客户端降级到传统DNS的时候，就会解析到错误的机房，在调用生效过程当中，也会访问到旧的机房，所以我们在API网关会做一个拦截，因为每个请求都带上了单元号，API网关就可以判断这个请求是否请求到了正确的机房，如果请求错了机房，API网关把请求定向单元当前归属的机房。&lt;/h2&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;定向用户请求有两种模式，一种是转发模式，API网关直接转发到新机房的业务后端实例。另一种是重定向模式，API网关在响应header携带了重定向指令，以及新机房的IP（避免客户端多一次的请求），客户端立即重试新IP。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;转发模式需要消耗较多的机房专线带宽，重定向模式的总体时长更高，业务可以自由选择两种模式。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;解析刷新采用并行跑马的模式，客户端会并行请求两个HttpDNS Server和一个传统DNS，三个请求同时发出去。如果HttpDNS Server请求成功，哪个先到就用哪个，如果两个HttpDNS Server请求都失败，就使用传统DNS解析结果。因为每个单元都分配一个传统域名，所以传统DNS解析结果和HttpDNS解析结果也基本是一致，只有极少数用户会解析错误，API网关重定向一次以后也能纠正过来。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;6、调度决策大脑   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095910522.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;h2&gt;调度决策大脑会收集很多路的原始监控数据，比如客户端调用链的数据、外网拨测平台的数据、机房网络监控的数据等等，多路数据汇总到决策大脑里，进行比对分析，得出故障的结论。&lt;/h2&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;调度决策大脑一定要依赖多路监控数据源，因为单路数据的质量无法保证，比如可能会出现拨测用例配置错误、网络监控数据丢失等，所以单路数据都是不可信的，需要多路数据源做交叉的比对，过滤抖动、防止误判。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;调度决策大脑最终会输出一个指令，指令只会告诉你故障类型，比如：机房故障、运营商线路故障、机房之间网络（DCI)故障，或者是容量不足、业务自身出现了问题等。业务自身出现问题，比如业务的数据库故障，也需要切到另外的机房去。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;决策指令同时发到两个地方，既要发给接入层，也要发给数据层，为什么需要这样呢？&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;假设我们同城两个机房之间，专线出现了故障，两个机房的数据库肯定达不成一致，同步不过去了。这个情况下，假设我们的数据库选主B机房，而接入层保留A机房， A机房的数据库完全写不进去，即使写进去也是错误的，这里我们要保证数据层和接入层两边选择的机房要一致。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;所以这种专线故障情况下，我们是调度决策大脑来通知，做统一的决策，同时通知接入层、数据层做联动，选择同一机房，这个主机房的选择是事先配置好的，它不是由我们刚刚说的Raft组件来解决的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;7、调度效果   &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095921186.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;上图是我们9月份做过的一次机房调度的效果，基本上做到分钟级（实际上是秒级的）的生效，是很陡的一个曲线。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;四、总结&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;最后，给大家总结一下今天分享的内容：&lt;/p&gt;  &lt;p&gt;    &lt;img src="https://dbaplus.cn/uploadfile/2021/0330/20210330095939905.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;strong&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/strong&gt;  &lt;p&gt;    &lt;strong&gt;Q&amp;amp;A&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q1：Http DNS也有缓存的吧？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A1 ：&lt;/strong&gt;对，刚刚提到我们Http DNS缓存时间非常长，缓存了一周的时间，而且缓存的时候是根据环境来缓存的，就是按照 WiFi名称、运营商的名称来做缓存，这样网络切换的时候可以拿到最优的IP。&lt;/p&gt;  &lt;p&gt;缓存的时间非常长，是因为域名解析的刷新，是不依赖缓存过期的，如果能请求成功，API网关在响应Header就会带上调度指令，如果请求失败客户端也会主动去刷新解析。因此解析的刷新，是不依赖缓存过期时间的。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q2：同城多活网络是怎么配置的？两个机房使用相同的ip地址，还是不同的？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A2 ：&lt;/strong&gt;对于跨机房高可用的数据库来说，用户看到的是同一个IP，第一层使用Anycast路由到机房，第二层使用ECMP路由到多个四层负载均衡节点，单个四层负载均衡的流量扛不住，四层负载均衡是一个集群，通过ECMP实现流量分发。&lt;/p&gt;  &lt;p&gt;多余入口流量来说，前面架构图可以看到，接入层在两个机房从四层、七层都是独立的，接入层有2组出口IP，如果其中一个机房运营商线路出现问题，根据调度决策系统的指令，自动停止该运营商线路的IP解析。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q3：老师能介绍一下多活带来什么业务收益吗？是什么契机促使 OPPO开始做异地多活？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A3 ：&lt;/strong&gt;OPPO业务多活的三个核心诉求是成本、扩展、容灾。&lt;/p&gt;  &lt;p&gt;成本是指业务总体技术运营成本，包括基础设施的资源成本、研发成本，还包括业务中断的成本、品牌和口碑的成本；&lt;/p&gt;  &lt;p&gt;扩展是因为业务规模过大，一个服务需要调用数百个三方实例、一个数据库被数百个实例连接、一个服务需要连接几十个数据库，这就需要对用户进行分片，缩小业务规模，自然演进到单元化多活的架构；&lt;/p&gt;  &lt;p&gt;容灾一方面是极端情况下用户数据可靠性保障的需求，另一方面还是业务过于复杂、处理的链路很长，总有一些意想不到情况的发生，频率还挺高，问题定位到恢复的时间达不到公司RTO的要求。机房内部共享了运营商线路、DNS、SNAT防火墙、负载均衡、K8S集群、注册中心、监控等等资源，而机房之间是相对隔离的环境，同时出问题的概率大为降低。在业务出现无法自动恢复的故障时，先切换机房恢复业务，然后再从容定位问题根因。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q4：随着业务发展启用多个订阅时，如何减少对数据库的压力？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A4 ：&lt;/strong&gt;我们从数据库源库订阅出来以后，先落地到本地文件队列，然后多个订阅方可以维持自己的同步位点，所以对于源数据库来说，只会有一次订阅。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q5：请问同城双活方案MHA manager部署在哪个数据中心？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A5 ：&lt;/strong&gt;我们这里不是MHA，我们用的是一个开源的Raft组件，部署在同城的3个机房，通过Raft组件检测数据库的状态、触发切换。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q6：Http DNS和Local DNS的区别是什么？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A6 ：&lt;/strong&gt;Http DNS走的是HTTP协议，客户端直连解析，没有运营商的多级缓存。Local DNS就是运营商的DNS，成功率低，还有劫持、黑洞等问题，而且这两年黑洞频率是越来越高了，前几年基本上很少出现黑洞情况。传统DNS劫持情况现在好一些了，像移动端的接口劫持相对来说会少一些，H5的劫持多一点。&lt;/p&gt;  &lt;p&gt;Http DNS就是依赖HTTP协议做解析，但这个压力会比较大，因为Http DNS没有多级缓存，所有请求都到我们的机房，所以刷新机制的设计就非常关键，前面一个章节详细介绍解析刷新的时机。&lt;/p&gt;  &lt;p&gt;HttpDNS还有一个好处，因为是自定义的协议，可以传递其他参数，比如设备信息、账号信息，这样才能够实现按用户单元进行解析、调度。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q7：能否制定统一的用户单元划分规则？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A7 ：&lt;/strong&gt;这个问题比较好，我们最开始也是想这样子的，我们有云服务、广告、信息流、音乐、视频等业务，起初也想整个公司使用一套单元划分规则，这样业务之间可以做到单元内封闭调用，避免跨机房的调用。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;最终的方案，业务之间没有使用同一套单元划分规则。主要原因是：比如说有个业务他经常会做活动，做活动的时候他需要将一部分用户调度走，如果全公司用一套规则的话，所有业务都要跟着调度走，其他业务是不同意的。所以我们是每个业务自己制定单元划分规则。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;那这里怎样解决业务之间跨机房调用呢？前面说到了平台型业务SDK化，上层业务之间本身没有强依赖，音乐、软件商店、视频之间本身没有强依赖，他们主要是依赖平台型服务，如账号、评论服务、消息中心、推送服务等。这些平台型业务我们最开始也是提供机房内部API去给其他业务器调用，这就导致我们的平台型服务在每一个机房都要去部署，每个机房都要提供读写功能。所以我们将平台型域名拆分出来，从SDK就开始就和业务域名分开，平台型自己做多活。当然平台型业务无法做到100%的SDK化拆分，平台型服务的部分数据也需要单向同步到各机房，提供本地查询的服务。&lt;/p&gt;  &lt;p&gt; &lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;      &lt;strong&gt;Q8：Redis日志是哪个开源组件做到的来的？&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;A8 ：&lt;/strong&gt;Redis binlog是OPPO自己修改的，基于AOF修改，简单说一下binlog的格式，    &lt;br /&gt;    &lt;br /&gt;  &lt;br /&gt;&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61298-%E7%94%A8%E6%88%B7-%E6%9E%B6%E6%9E%84-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Thu, 01 Apr 2021 14:05:21 CST</pubDate>
    </item>
    <item>
      <title>使用logstash同步mysql 多表数据到ElasticSearch实践 - 三度 - 博客园</title>
      <link>https://itindex.net/detail/61170-logstash-%E5%90%8C%E6%AD%A5-mysql</link>
      <description>&lt;div&gt;    &lt;p&gt;参考样式即可，具体使用配置参数根据实际情况而定&lt;/p&gt;    &lt;pre&gt;      &lt;code&gt;input {  
    jdbc {  
      jdbc_connection_string =&amp;gt; &amp;quot;jdbc:mysql://localhost/数据库名&amp;quot;  
      jdbc_user =&amp;gt; &amp;quot;root&amp;quot;  
      jdbc_password =&amp;gt; &amp;quot;password&amp;quot;  
      jdbc_driver_library =&amp;gt; &amp;quot;mysql-connector-java-5.1.45-bin.jar所在位置&amp;quot;  
      jdbc_driver_class =&amp;gt; &amp;quot;com.mysql.jdbc.Driver&amp;quot;
      codec =&amp;gt; plain {charset =&amp;gt; &amp;quot;UTF-8&amp;quot;}
      record_last_run =&amp;gt; true
      jdbc_paging_enabled =&amp;gt; &amp;quot;true&amp;quot;  
      jdbc_page_size =&amp;gt; &amp;quot;1000&amp;quot;  
      statement =&amp;gt; &amp;quot;sql statement&amp;quot;   
      schedule =&amp;gt; &amp;quot;* * * * *&amp;quot;  
      type =&amp;gt; &amp;quot;数据库表名1&amp;quot;  
      tags =&amp;gt; &amp;quot;数据库表名1&amp;quot;
    }
    jdbc {  
      jdbc_connection_string =&amp;gt; &amp;quot;jdbc:mysql://localhost/数据库名&amp;quot;  
      jdbc_user =&amp;gt; &amp;quot;root&amp;quot;  
      jdbc_password =&amp;gt; &amp;quot;password&amp;quot;  
      jdbc_driver_library =&amp;gt; &amp;quot;mysql-connector-java-5.1.45-bin.jar所在位置&amp;quot;  
      jdbc_driver_class =&amp;gt; &amp;quot;com.mysql.jdbc.Driver&amp;quot;
      codec =&amp;gt; plain {charset =&amp;gt; &amp;quot;UTF-8&amp;quot;}
      record_last_run =&amp;gt; true
      jdbc_paging_enabled =&amp;gt; &amp;quot;true&amp;quot;  
      jdbc_page_size =&amp;gt; &amp;quot;1000&amp;quot;  
      statement =&amp;gt; &amp;quot;sql statement&amp;quot;   
      schedule =&amp;gt; &amp;quot;* * * * *&amp;quot;  
      type =&amp;gt; &amp;quot;数据库表名2&amp;quot;
      tags =&amp;gt; &amp;quot;数据库表名2&amp;quot;
    }
}  

filter {  
    json {  
        source =&amp;gt; &amp;quot;message&amp;quot;  
        remove_field =&amp;gt; [&amp;quot;message&amp;quot;]  
    }  
}  

output {  
    if [type] == &amp;quot;数据库表名1&amp;quot;{
        elasticsearch {
            hosts =&amp;gt; [&amp;quot;els的host地址&amp;quot;]  
            index =&amp;gt; &amp;quot;数据库表名1对应的els的index&amp;quot;  
            document_id =&amp;gt; &amp;quot;%{唯一id}&amp;quot;
        }
    }
    if [type] == &amp;quot;数据库表名2&amp;quot;{
        elasticsearch {
            hosts =&amp;gt; [&amp;quot;els的host地址&amp;quot;]  
            index =&amp;gt; &amp;quot;数据库表名2对应的els的index&amp;quot;  
            document_id =&amp;gt; &amp;quot;%{唯一id}&amp;quot;
        }
    }
    stdout {   
        codec =&amp;gt; json_lines  
    }  
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61170-logstash-%E5%90%8C%E6%AD%A5-mysql</guid>
      <pubDate>Wed, 13 Jan 2021 09:26:52 CST</pubDate>
    </item>
    <item>
      <title>MySQL如何实时同步数据到ES？试试这款阿里开源的神器！</title>
      <link>https://itindex.net/detail/61015-mysql-%E5%AE%9E%E6%97%B6-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;  &lt;blockquote&gt;
   &lt;p&gt;SpringBoot实战电商项目mall（40k+star）地址：    &lt;a href="https://github.com/macrozheng/mall" rel="nofollow noopener noreferrer" target="_blank"&gt;github.com/macrozheng/…&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
  &lt;h2&gt;摘要&lt;/h2&gt;
  &lt;p&gt;   &lt;code&gt;mall&lt;/code&gt;项目中的商品搜索功能，一直都没有做实时数据同步。最近发现阿里巴巴开源的   &lt;code&gt;canal&lt;/code&gt;可以把MySQL中的数据实时同步到Elasticsearch中，能很好地解决数据同步问题。今天我们来讲讲   &lt;code&gt;canal&lt;/code&gt;的使用，希望对大家有所帮助！&lt;/p&gt;
  &lt;h2&gt;canal简介&lt;/h2&gt;
  &lt;p&gt;canal主要用途是对MySQL数据库增量日志进行解析，提供增量数据的订阅和消费，简单说就是可以对MySQL的增量数据进行实时同步，支持同步到MySQL、Elasticsearch、HBase等数据存储中去。&lt;/p&gt;
  &lt;h2&gt;canal工作原理&lt;/h2&gt;
  &lt;p&gt;canal会模拟MySQL主库和从库的交互协议，从而伪装成MySQL的从库，然后向MySQL主库发送dump协议，MySQL主库收到dump请求会向canal推送binlog，canal通过解析binlog将数据同步到其他存储中去。&lt;/p&gt;
  &lt;p&gt;   &lt;img alt="canal&amp;#24037;&amp;#20316;&amp;#21407;&amp;#29702;&amp;#22270;" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6c82adcafd684f398a9a04e0dc315bec~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;h2&gt;canal使用&lt;/h2&gt;
  &lt;blockquote&gt;
   &lt;p&gt;接下来我们来学习下canal的使用，以MySQL实时同步数据到Elasticsearch为例。&lt;/p&gt;
&lt;/blockquote&gt;
  &lt;h3&gt;组件下载&lt;/h3&gt;
  &lt;ul&gt;
   &lt;li&gt;首先我们需要下载canal的各个组件    &lt;code&gt;canal-server&lt;/code&gt;、    &lt;code&gt;canal-adapter&lt;/code&gt;、    &lt;code&gt;canal-admin&lt;/code&gt;，下载地址：https://github.com/alibaba/canal/releases&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3b4bc47974834eb389fbf5cfe15387ef~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;
    &lt;p&gt;canal的各个组件的用途各不相同，下面分别介绍下：&lt;/p&gt;
    &lt;ul&gt;
     &lt;li&gt;canal-server（canal-deploy）：可以直接监听MySQL的binlog，把自己伪装成MySQL的从库，只负责接收数据，并不做处理。&lt;/li&gt;
     &lt;li&gt;canal-adapter：相当于canal的客户端，会从canal-server中获取数据，然后对数据进行同步，可以同步到MySQL、Elasticsearch和HBase等存储中去。&lt;/li&gt;
     &lt;li&gt;canal-admin：为canal提供整体配置管理、节点运维等面向运维的功能，提供相对友好的WebUI操作界面，方便更多用户快速和安全的操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
   &lt;li&gt;
    &lt;p&gt;由于不同版本的MySQL、Elasticsearch和canal会有兼容性问题，所以我们先对其使用版本做个约定。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;








































  &lt;table&gt;   &lt;tr&gt;    &lt;th&gt;应用&lt;/th&gt;    &lt;th&gt;端口&lt;/th&gt;    &lt;th&gt;版本&lt;/th&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;MySQL&lt;/td&gt;    &lt;td&gt;3306&lt;/td&gt;    &lt;td&gt;5.7&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;Elasticsearch&lt;/td&gt;    &lt;td&gt;9200&lt;/td&gt;    &lt;td&gt;7.6.2&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;Kibanba&lt;/td&gt;    &lt;td&gt;5601&lt;/td&gt;    &lt;td&gt;7.6.2&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;canal-server&lt;/td&gt;    &lt;td&gt;11111&lt;/td&gt;    &lt;td&gt;1.1.15&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;canal-adapter&lt;/td&gt;    &lt;td&gt;8081&lt;/td&gt;    &lt;td&gt;1.1.15&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;canal-admin&lt;/td&gt;    &lt;td&gt;8089&lt;/td&gt;    &lt;td&gt;1.1.15&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
  &lt;h3&gt;MySQL配置&lt;/h3&gt;
  &lt;ul&gt;
   &lt;li&gt;由于canal是通过订阅MySQL的binlog来实现数据同步的，所以我们需要开启MySQL的binlog写入功能，并设置    &lt;code&gt;binlog-format&lt;/code&gt;为ROW模式，我的配置文件为    &lt;code&gt;/mydata/mysql/conf/my.cnf&lt;/code&gt;，改为如下内容即可；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;[mysqld]
## 设置server_id，同一局域网中需要唯一
server_id=101 
## 指定不需要同步的数据库名称
binlog-ignore-db=mysql  
## 开启二进制日志功能
log-bin=mall-mysql-bin  
## 设置二进制日志使用内存大小（事务）
binlog_cache_size=1M  
## 设置使用的二进制日志格式（mixed,statement,row）
binlog_format=row  
## 二进制日志过期清理时间。默认值为0，表示不自动清理。
expire_logs_days=7  
## 跳过主从复制中遇到的所有错误或指定类型的错误，避免slave端复制中断。
## 如：1062错误是指一些主键重复，1032错误是因为主从数据库数据不一致
slave_skip_errors=1062  
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;配置完成后需要重新启动MySQL，重启成功后通过如下命令查看binlog是否启用；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;show variables like &amp;apos;%log_bin%&amp;apos;
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;+---------------------------------+-------------------------------------+
| Variable_name                   | Value                               |
+---------------------------------+-------------------------------------+
| log_bin                         | ON                                  |
| log_bin_basename                | /var/lib/mysql/mall-mysql-bin       |
| log_bin_index                   | /var/lib/mysql/mall-mysql-bin.index |
| log_bin_trust_function_creators | OFF                                 |
| log_bin_use_v1_row_events       | OFF                                 |
| sql_log_bin                     | ON                                  |
+---------------------------------+-------------------------------------+
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;再查看下MySQL的binlog模式；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;show variables like &amp;apos;binlog_format%&amp;apos;;  
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;接下来需要创建一个拥有从库权限的账号，用于订阅binlog，这里创建的账号为    &lt;code&gt;canal:canal&lt;/code&gt;；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;CREATE USER canal IDENTIFIED BY &amp;apos;canal&amp;apos;;  
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO &amp;apos;canal&amp;apos;@&amp;apos;%&amp;apos;;
FLUSH PRIVILEGES;
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;创建好测试用的数据库    &lt;code&gt;canal-test&lt;/code&gt;，之后创建一张商品表    &lt;code&gt;product&lt;/code&gt;，建表语句如下。&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;CREATE TABLE `product`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sub_title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` decimal(10, 2) NULL DEFAULT NULL,
  `pic` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;h3&gt;canal-server使用&lt;/h3&gt;
  &lt;ul&gt;
   &lt;li&gt;将我们下载好的压缩包    &lt;code&gt;canal.deployer-1.1.5-SNAPSHOT.tar.gz&lt;/code&gt;上传到Linux服务器，然后解压到指定目录    &lt;code&gt;/mydata/canal-server&lt;/code&gt;，可使用如下命令解压；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;tar -zxvf canal.deployer-1.1.5-SNAPSHOT.tar.gz
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;解压完成后目录结构如下；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;├── bin
│   ├── restart.sh
│   ├── startup.bat
│   ├── startup.sh
│   └── stop.sh
├── conf
│   ├── canal_local.properties
│   ├── canal.properties
│   └── example
│       └── instance.properties
├── lib
├── logs
│   ├── canal
│   │   └── canal.log
│   └── example
│       ├── example.log
│       └── example.log
└── plugin
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;修改配置文件    &lt;code&gt;conf/example/instance.properties&lt;/code&gt;，按如下配置即可，主要是修改数据库相关配置；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;# 需要同步数据的MySQL地址
canal.instance.master.address=127.0.0.1:3306
canal.instance.master.journal.name=
canal.instance.master.position=
canal.instance.master.timestamp=
canal.instance.master.gtid=
# 用于同步数据的数据库账号
canal.instance.dbUsername=canal
# 用于同步数据的数据库密码
canal.instance.dbPassword=canal
# 数据库连接编码
canal.instance.connectionCharset = UTF-8
# 需要订阅binlog的表过滤正则表达式
canal.instance.filter.regex=.*\\..*
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;使用    &lt;code&gt;startup.sh&lt;/code&gt;脚本启动    &lt;code&gt;canal-server&lt;/code&gt;服务；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;sh bin/startup.sh
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;启动成功后可使用如下命令查看服务日志信息；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;tail -f logs/canal/canal.log
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;2020-10-26 16:18:13.354 [main] INFO  com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[172.17.0.1(172.17.0.1):11111]
2020-10-26 16:18:19.978 [main] INFO  com.alibaba.otter.canal.deployer.CanalStarter - ## the canal server is running now ......
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;启动成功后可使用如下命令查看instance日志信息；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;tail -f logs/example/example.log 
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;2020-10-26 16:18:16.056 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [canal.properties]
2020-10-26 16:18:16.061 [main] INFO  c.a.o.c.i.spring.support.PropertyPlaceholderConfigurer - Loading properties file from class path resource [example/instance.properties]
2020-10-26 16:18:18.259 [main] INFO  c.a.otter.canal.instance.spring.CanalInstanceWithSpring - start CannalInstance for 1-example 
2020-10-26 16:18:18.282 [main] WARN  c.a.o.canal.parse.inbound.mysql.dbsync.LogEventConvert - --&amp;gt; init table filter : ^.*\..*$
2020-10-26 16:18:18.282 [main] WARN  c.a.o.canal.parse.inbound.mysql.dbsync.LogEventConvert - --&amp;gt; init table black filter : ^mysql\.slave_.*$
2020-10-26 16:18:19.543 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN  c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---&amp;gt; begin to find start position, it will be long time for reset or first position
2020-10-26 16:18:19.578 [main] INFO  c.a.otter.canal.instance.core.AbstractCanalInstance - start successful....
2020-10-26 16:18:19.912 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN  c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - prepare to find start position just last position
 {&amp;quot;identity&amp;quot;:{&amp;quot;slaveId&amp;quot;:-1,&amp;quot;sourceAddress&amp;quot;:{&amp;quot;address&amp;quot;:&amp;quot;localhost&amp;quot;,&amp;quot;port&amp;quot;:3306}},&amp;quot;postion&amp;quot;:{&amp;quot;gtid&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;included&amp;quot;:false,&amp;quot;journalName&amp;quot;:&amp;quot;mall-mysql-bin.000006&amp;quot;,&amp;quot;position&amp;quot;:2271,&amp;quot;serverId&amp;quot;:101,&amp;quot;timestamp&amp;quot;:1603682664000}}
2020-10-26 16:18:22.435 [destination = example , address = /127.0.0.1:3306 , EventParser] WARN  c.a.o.c.p.inbound.mysql.rds.RdsBinlogEventParserProxy - ---&amp;gt; find start position successfully, EntryPosition[included=false,journalName=mall-mysql-bin.000006,position=2271,serverId=101,gtid=,timestamp=1603682664000] cost : 2768ms , the next step is binlog dump
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;如果想要停止    &lt;code&gt;canal-server&lt;/code&gt;服务可以使用如下命令。&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;sh bin/stop.sh
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;h3&gt;canal-adapter使用&lt;/h3&gt;
  &lt;ul&gt;
   &lt;li&gt;将我们下载好的压缩包    &lt;code&gt;canal.adapter-1.1.5-SNAPSHOT.tar.gz&lt;/code&gt;上传到Linux服务器，然后解压到指定目录    &lt;code&gt;/mydata/canal-adpter&lt;/code&gt;，解压完成后目录结构如下；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;├── bin
│   ├── adapter.pid
│   ├── restart.sh
│   ├── startup.bat
│   ├── startup.sh
│   └── stop.sh
├── conf
│   ├── application.yml
│   ├── es6
│   ├── es7
│   │   ├── biz_order.yml
│   │   ├── customer.yml
│   │   └── product.yml
│   ├── hbase
│   ├── kudu
│   ├── logback.xml
│   ├── META-INF
│   │   └── spring.factories
│   └── rdb
├── lib
├── logs
│   └── adapter
│       └── adapter.log
└── plugin
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;修改配置文件    &lt;code&gt;conf/application.yml&lt;/code&gt;，按如下配置即可，主要是修改canal-server配置、数据源配置和客户端适配器配置；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;canal.conf:
  mode: tcp # 客户端的模式，可选tcp kafka rocketMQ
  flatMessage: true # 扁平message开关, 是否以json字符串形式投递数据, 仅在kafka/rocketMQ模式下有效
  zookeeperHosts:    # 对应集群模式下的zk地址
  syncBatchSize: 1000 # 每次同步的批数量
  retries: 0 # 重试次数, -1为无限重试
  timeout: # 同步超时时间, 单位毫秒
  accessKey:
  secretKey:
  consumerProperties:
    # canal tcp consumer
    canal.tcp.server.host: 127.0.0.1:11111 #设置canal-server的地址
    canal.tcp.zookeeper.hosts:
    canal.tcp.batch.size: 500
    canal.tcp.username:
    canal.tcp.password:

  srcDataSources: # 源数据库配置
    defaultDS:
      url: jdbc:mysql://127.0.0.1:3306/canal_test?useUnicode=true
      username: canal
      password: canal
  canalAdapters: # 适配器列表
  - instance: example # canal实例名或者MQ topic名
    groups: # 分组列表
    - groupId: g1 # 分组id, 如果是MQ模式将用到该值
      outerAdapters:
      - name: logger # 日志打印适配器
      - name: es7 # ES同步适配器
        hosts: 127.0.0.1:9200 # ES连接地址
        properties:
          mode: rest # 模式可选transport(9300) 或者 rest(9200)
          # security.auth: test:123456 #  only used for rest mode
          cluster.name: elasticsearch # ES集群名称
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;添加配置文件    &lt;code&gt;canal-adapter/conf/es7/product.yml&lt;/code&gt;，用于配置MySQL中的表与Elasticsearch中索引的映射关系；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;dataSourceKey: defaultDS # 源数据源的key, 对应上面配置的srcDataSources中的值
destination: example  # canal的instance或者MQ的topic
groupId: g1 # 对应MQ模式下的groupId, 只会同步对应groupId的数据
esMapping:
  _index: canal_product # es 的索引名称
  _id: _id  # es 的_id, 如果不配置该项必须配置下面的pk项_id则会由es自动分配
  sql: &amp;quot;SELECT
        p.id AS _id,
        p.title,
        p.sub_title,
        p.price,
        p.pic
        FROM
        product p&amp;quot;        # sql映射
  etlCondition: &amp;quot;where a.c_time&amp;gt;={}&amp;quot;   #etl的条件参数
  commitBatch: 3000   # 提交批大小
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;使用    &lt;code&gt;startup.sh&lt;/code&gt;脚本启动    &lt;code&gt;canal-adapter&lt;/code&gt;服务；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;sh bin/startup.sh
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;启动成功后可使用如下命令查看服务日志信息；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;tail -f logs/adapter/adapter.log
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;20-10-26 16:52:55.148 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Load canal adapter: logger succeed
2020-10-26 16:52:57.005 [main] INFO  c.a.o.c.client.adapter.es.core.config.ESSyncConfigLoader - ## Start loading es mapping config ... 
2020-10-26 16:52:57.376 [main] INFO  c.a.o.c.client.adapter.es.core.config.ESSyncConfigLoader - ## ES mapping config loaded
2020-10-26 16:52:58.615 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Load canal adapter: es7 succeed
2020-10-26 16:52:58.651 [main] INFO  c.alibaba.otter.canal.connector.core.spi.ExtensionLoader - extension classpath dir: /mydata/canal-adapter/plugin
2020-10-26 16:52:59.043 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterLoader - Start adapter for canal-client mq topic: example-g1 succeed
2020-10-26 16:52:59.044 [main] INFO  c.a.o.canal.adapter.launcher.loader.CanalAdapterService - ## the canal client adapters are running now ......
2020-10-26 16:52:59.057 [Thread-4] INFO  c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - =============&amp;gt; Start to connect destination: example &amp;lt;=============
2020-10-26 16:52:59.100 [main] INFO  org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler [&amp;quot;http-nio-8081&amp;quot;]
2020-10-26 16:52:59.153 [main] INFO  org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
2020-10-26 16:52:59.590 [main] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8081 (http) with context path &amp;apos;&amp;apos;
2020-10-26 16:52:59.626 [main] INFO  c.a.otter.canal.adapter.launcher.CanalAdapterApplication - Started CanalAdapterApplication in 31.278 seconds (JVM running for 33.99)
2020-10-26 16:52:59.930 [Thread-4] INFO  c.a.otter.canal.adapter.launcher.loader.AdapterProcessor - =============&amp;gt; Subscribe destination: example succeed &amp;lt;=============
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;如果需要停止    &lt;code&gt;canal-adapter&lt;/code&gt;服务可以使用如下命令。&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;sh bin/stop.sh
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;h3&gt;数据同步演示&lt;/h3&gt;
  &lt;blockquote&gt;
   &lt;p&gt;经过上面的一系列步骤，canal的数据同步功能已经基本可以使用了，下面我们来演示下数据同步功能。&lt;/p&gt;
&lt;/blockquote&gt;
  &lt;ul&gt;
   &lt;li&gt;首先我们需要在Elasticsearch中创建索引，和MySQL中的product表相对应，直接在Kibana的    &lt;code&gt;Dev Tools&lt;/code&gt;中使用如下命令创建即可；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;PUT canal_product
{
  &amp;quot;mappings&amp;quot;: {
    &amp;quot;properties&amp;quot;: {
      &amp;quot;title&amp;quot;: {
        &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;
      },
      &amp;quot;sub_title&amp;quot;: {
        &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;
      },
      &amp;quot;pic&amp;quot;: {
        &amp;quot;type&amp;quot;: &amp;quot;text&amp;quot;
      },
      &amp;quot;price&amp;quot;: {
        &amp;quot;type&amp;quot;: &amp;quot;double&amp;quot;
      }
    }
  }
}
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c6ecce9d2f4481db5063d511d60f525~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;创建完成后可以查看下索引的结构；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;GET canal_product/_mapping
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b835ccca0e884509aee41afd5a5160d4~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;之后使用如下SQL语句在数据库中创建一条记录；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;INSERT INTO product ( id, title, sub_title, price, pic ) VALUES ( 5, &amp;apos;小米8&amp;apos;, &amp;apos; 全面屏游戏智能手机 6GB+64GB&amp;apos;, 1999.00, NULL );
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;创建成功后，在Elasticsearch中搜索下，发现数据已经同步了；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;GET canal_product/_search
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/88a8b351588d456da6aa494af35f496e~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;再使用如下SQL对数据进行修改；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;UPDATE product SET title=&amp;apos;小米10&amp;apos; WHERE id=5
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;修改成功后，在Elasticsearch中搜索下，发现数据已经修改了；&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab467296b11140059d3cd5544655dd75~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;再使用如下SQL对数据进行删除操作；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;DELETE FROM product WHERE id=5
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;删除成功后，在Elasticsearch中搜索下，发现数据已经删除了，至此MySQL同步到Elasticsearch的功能完成了！&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aac8c55432784391821c28d7645a4290~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;h3&gt;canal-admin使用&lt;/h3&gt;
  &lt;ul&gt;
   &lt;li&gt;将我们下载好的压缩包    &lt;code&gt;canal.admin-1.1.5-SNAPSHOT.tar.gz&lt;/code&gt;上传到Linux服务器，然后解压到指定目录    &lt;code&gt;/mydata/canal-admin&lt;/code&gt;，解压完成后目录结构如下；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;├── bin
│   ├── restart.sh
│   ├── startup.bat
│   ├── startup.sh
│   └── stop.sh
├── conf
│   ├── application.yml
│   ├── canal_manager.sql
│   ├── canal-template.properties
│   ├── instance-template.properties
│   ├── logback.xml
│   └── public
│       ├── avatar.gif
│       ├── index.html
│       ├── logo.png
│       └── static
├── lib
└── logs
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;创建canal-admin需要使用的数据库    &lt;code&gt;canal_manager&lt;/code&gt;，创建SQL脚本为    &lt;code&gt;/mydata/canal-admin/conf/canal_manager.sql&lt;/code&gt;，会创建如下表；&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a3ea831b5d48472f8f33cf3af9294abe~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;修改配置文件    &lt;code&gt;conf/application.yml&lt;/code&gt;，按如下配置即可，主要是修改数据源配置和    &lt;code&gt;canal-admin&lt;/code&gt;的管理账号配置，注意需要用一个有读写权限的数据库账号，比如管理账号    &lt;code&gt;root:root&lt;/code&gt;；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;server:
  port: 8089
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

spring.datasource:
  address: 127.0.0.1:3306
  database: canal_manager
  username: root
  password: root
  driver-class-name: com.mysql.jdbc.Driver
  url: jdbc:mysql://${spring.datasource.address}/${spring.datasource.database}?useUnicode=true&amp;amp;characterEncoding=UTF-8&amp;amp;useSSL=false
  hikari:
    maximum-pool-size: 30
    minimum-idle: 1

canal:
  adminUser: admin
  adminPasswd: admin
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;接下来对之前搭建的    &lt;code&gt;canal-server&lt;/code&gt;的    &lt;code&gt;conf/canal_local.properties&lt;/code&gt;文件进行配置，主要是修改    &lt;code&gt;canal-admin&lt;/code&gt;的配置，修改完成后使用    &lt;code&gt;sh bin/startup.sh local&lt;/code&gt;重启    &lt;code&gt;canal-server&lt;/code&gt;：&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;# register ip
canal.register.ip =

# canal admin config
canal.admin.manager = 127.0.0.1:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
# admin auto register
canal.admin.register.auto = true
canal.admin.register.cluster = 
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;使用    &lt;code&gt;startup.sh&lt;/code&gt;脚本启动    &lt;code&gt;canal-admin&lt;/code&gt;服务；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;sh bin/startup.sh
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;启动成功后可使用如下命令查看服务日志信息；&lt;/li&gt;
&lt;/ul&gt;
  &lt;pre&gt;   &lt;code&gt;tail -f logs/admin.log
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;pre&gt;   &lt;code&gt;020-10-27 10:15:04.210 [main] INFO  org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler [&amp;quot;http-nio-8089&amp;quot;]
2020-10-27 10:15:04.308 [main] INFO  org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
2020-10-27 10:15:04.534 [main] INFO  o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8089 (http) with context path &amp;apos;&amp;apos;
2020-10-27 10:15:04.573 [main] INFO  com.alibaba.otter.canal.admin.CanalAdminApplication - Started CanalAdminApplication in 31.203 seconds (JVM running for 34.865)
复制代码&lt;/code&gt;&lt;/pre&gt;
  &lt;ul&gt;
   &lt;li&gt;访问canal-admin的Web界面，输入账号密码    &lt;code&gt;admin:123456&lt;/code&gt;即可登录，访问地址：http://192.168.3.101:8089&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8fc667a4d0a34110b75746620516f5ff~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;ul&gt;
   &lt;li&gt;登录成功后即可使用Web界面操作canal-server。&lt;/li&gt;
&lt;/ul&gt;
  &lt;p&gt;   &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6a2c42520a54cd38fb1703034b1ecea~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
  &lt;h2&gt;参考资料&lt;/h2&gt;
  &lt;p&gt;canal官方文档：https://github.com/alibaba/canal/wiki&lt;/p&gt;
  &lt;h2&gt;配置文件地址&lt;/h2&gt;
  &lt;p&gt;   &lt;a href="https://github.com/macrozheng/mall-learning/tree/master/document/canal-config" rel="nofollow noopener noreferrer" target="_blank"&gt;github.com/macrozheng/…&lt;/a&gt;&lt;/p&gt;
  &lt;blockquote&gt;
   &lt;p&gt;本文 GitHub     &lt;a href="https://github.com/macrozheng/mall-learning" rel="nofollow noopener noreferrer" target="_blank"&gt;github.com/macrozheng/…&lt;/a&gt; 已经收录，欢迎大家Star！&lt;/p&gt;
&lt;/blockquote&gt;&lt;/div&gt;  &lt;div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61015-mysql-%E5%AE%9E%E6%97%B6-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Thu, 05 Nov 2020 00:50:41 CST</pubDate>
    </item>
    <item>
      <title>基于 Flink SQL CDC 的实时数据同步方案 (developer.aliyun.com)</title>
      <link>https://itindex.net/detail/61000-flink-sql-cdc</link>
      <description>&lt;div&gt;  &lt;p&gt;整理：陈政羽（Flink 社区志愿者）&lt;/p&gt;  &lt;p&gt;Flink 1.11 引入了 Flink SQL CDC，CDC 能给我们数据和业务间能带来什么变化？本文由 Apache Flink PMC，阿里巴巴技术专家伍翀 (云邪）分享，内容将从传统的数据同步方案，基于 Flink CDC 同步的解决方案以及更多的应用场景和 CDC 未来开发规划等方面进行介绍和演示。&lt;/p&gt;  &lt;p&gt;1、传统数据同步方案   &lt;br /&gt;2、基于 Flink SQL CDC 的数据同步方案（Demo）   &lt;br /&gt;3、Flink SQL CDC 的更多应用场景   &lt;br /&gt;4、Flink SQL CDC 的未来规划   &lt;br /&gt;直播回顾：   &lt;br /&gt;   &lt;a href="https://www.bilibili.com/video/BV1zt4y1D7kt/"&gt;https://www.bilibili.com/video/BV1zt4y1D7kt/&lt;/a&gt;&lt;/p&gt;  &lt;h2&gt;传统的数据同步方案与 Flink SQL CDC 解决方案&lt;/h2&gt;  &lt;p&gt;业务系统经常会遇到需要更新数据到多个存储的需求。例如：一个订单系统刚刚开始只需要写入数据库即可完成业务使用。某天 BI 团队期望对数据库做全文索引，于是我们同时要写多一份数据到 ES 中，改造后一段时间，又有需求需要写入到 Redis 缓存中。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="1.png" src="https://ucc.alicdn.com/pic/developer-ecology/af3289bba8e84e8487339cdd4b56fd86.png" title="1.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;很明显这种模式是不可持续发展的，这种双写到各个数据存储系统中可能导致不可维护和扩展，数据一致性问题等，需要引入分布式事务，成本和复杂度也随之增加。我们可以通过 CDC（Change Data Capture）工具进行解除耦合，同步到下游需要同步的存储系统。通过这种方式提高系统的稳健性，也方便后续的维护。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="2.png" src="https://ucc.alicdn.com/pic/developer-ecology/10391f919ed74c259e398e0746f403df.png" title="2.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h3&gt;Flink SQL CDC 数据同步与原理解析&lt;/h3&gt;  &lt;p&gt;CDC 全称是 Change Data Capture ，它是一个比较广义的概念，只要能捕获变更的数据，我们都可以称为 CDC 。业界主要有基于查询的 CDC 和基于日志的 CDC ，可以从下面表格对比他们功能和差异点。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="&amp;#34920;&amp;#26684;.jpg" src="https://ucc.alicdn.com/pic/developer-ecology/7806a56be96b4731892494775f393a39.jpg" title="&amp;#34920;&amp;#26684;.jpg"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;经过以上对比，我们可以发现基于日志 CDC 有以下这几种优势：&lt;/p&gt;  &lt;p&gt;· 能够捕获所有数据的变化，捕获完整的变更记录。在异地容灾，数据备份等场景中得到广泛应用，如果是基于查询的 CDC 有可能导致两次查询的中间一部分数据丢失   &lt;br /&gt;· 每次 DML 操作均有记录无需像查询 CDC 这样发起全表扫描进行过滤，拥有更高的效率和性能，具有低延迟，不增加数据库负载的优势   &lt;br /&gt;· 无需入侵业务，业务解耦，无需更改业务模型   &lt;br /&gt;· 捕获删除事件和捕获旧记录的状态，在查询 CDC 中，周期的查询无法感知中间数据是否删除&lt;/p&gt;  &lt;p&gt;   &lt;img alt="3.png" src="https://ucc.alicdn.com/pic/developer-ecology/1f5bd7fa81cd4a9ca6e21b13d1739a5a.png" title="3.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h3&gt;基于日志的 CDC 方案介绍&lt;/h3&gt;  &lt;p&gt;从 ETL 的角度进行分析，一般采集的都是业务库数据，这里使用 MySQL 作为需要采集的数据库，通过 Debezium 把 MySQL Binlog 进行采集后发送至 Kafka 消息队列，然后对接一些实时计算引擎或者 APP 进行消费后把数据传输入 OLAP 系统或者其他存储介质。&lt;/p&gt;  &lt;p&gt;Flink 希望打通更多数据源，发挥完整的计算能力。我们生产中主要来源于业务日志和数据库日志，Flink 在业务日志的支持上已经非常完善，但是在数据库日志支持方面在 Flink 1.11 前还属于一片空白，这就是为什么要集成 CDC 的原因之一。&lt;/p&gt;  &lt;p&gt;Flink SQL 内部支持了完整的 changelog 机制，所以 Flink 对接 CDC 数据只需要把CDC 数据转换成 Flink 认识的数据，所以在 Flink 1.11 里面重构了 TableSource 接口，以便更好支持和集成 CDC。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="4.png" src="https://ucc.alicdn.com/pic/developer-ecology/5d5c18c7805c4a9c89796d6591171c04.png" title="4.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img alt="5.png" src="https://ucc.alicdn.com/pic/developer-ecology/c3c6a01ee8604339907bacf133fab4e3.png" title="5.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;重构后的 TableSource 输出的都是 RowData 数据结构，代表了一行的数据。在RowData 上面会有一个元数据的信息，我们称为 RowKind 。RowKind 里面包括了插入、更新前、更新后、删除，这样和数据库里面的 binlog 概念十分类似。通过 Debezium 采集的 JSON 格式，包含了旧数据和新数据行以及原数据信息，op 的 u表示是 update 更新操作标识符，ts_ms 表示同步的时间戳。因此，对接 Debezium JSON 的数据，其实就是将这种原始的 JSON 数据转换成 Flink 认识的 RowData。&lt;/p&gt;  &lt;h3&gt;选择 Flink 作为 ETL 工具&lt;/h3&gt;  &lt;p&gt;当选择 Flink 作为 ETL 工具时，在数据同步场景，如下图同步结构：&lt;/p&gt;  &lt;p&gt;   &lt;img alt="6.png" src="https://ucc.alicdn.com/pic/developer-ecology/768d5052ec0c4ced8d0e79f71843c869.png" title="6.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;通过 Debezium 订阅业务库 MySQL 的 Binlog 传输至 Kafka ，Flink 通过创建 Kafka 表指定 format 格式为 debezium-json ，然后通过 Flink 进行计算后或者直接插入到其他外部数据存储系统，例如图中的 Elasticsearch 和 PostgreSQL。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="7.png" src="https://ucc.alicdn.com/pic/developer-ecology/ea195acd250d491ca9e55e67be428006.png" title="7.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;但是这个架构有个缺点，我们可以看到采集端组件过多导致维护繁杂，这时候就会想是否可以用 Flink SQL 直接对接 MySQL 的 binlog 数据呢，有没可以替代的方案呢？&lt;/p&gt;  &lt;p&gt;答案是有的！经过改进后结构如下图：&lt;/p&gt;  &lt;p&gt;   &lt;img alt="8.png" src="https://ucc.alicdn.com/pic/developer-ecology/6231a71c5b78493783bc0f3c024d9e65.png" title="8.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;社区开发了 flink-cdc-connectors 组件，这是一个可以直接从 MySQL、PostgreSQL 等数据库直接读取全量数据和增量变更数据的 source 组件。目前也已开源，开源地址：&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;    &lt;a href="https://github.com/ververica/flink-cdc-connectors"&gt;https://github.com/ververica/flink-cdc-connectors&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;flink-cdc-connectors 可以用来替换 Debezium+Kafka 的数据采集模块，从而实现 Flink SQL 采集+计算+传输（ETL）一体化，这样做的优点有以下：&lt;/p&gt;  &lt;p&gt;· 开箱即用，简单易上手   &lt;br /&gt;· 减少维护的组件，简化实时链路，减轻部署成本   &lt;br /&gt;· 减小端到端延迟   &lt;br /&gt;· Flink 自身支持 Exactly Once 的读取和计算   &lt;br /&gt;· 数据不落地，减少存储成本   &lt;br /&gt;· 支持全量和增量流式读取   &lt;br /&gt;· binlog 采集位点可回溯*&lt;/p&gt;  &lt;h2&gt;基于 Flink SQL CDC 的数据同步方案实践&lt;/h2&gt;  &lt;p&gt;下面给大家带来 3 个关于 Flink SQL + CDC 在实际场景中使用较多的案例。在完成实验时候，你需要 Docker、MySQL、Elasticsearch 等组件，具体请参考每个案例参考文档。&lt;/p&gt;  &lt;h3&gt;案例 1 :  Flink SQL CDC  + JDBC Connector&lt;/h3&gt;  &lt;p&gt;这个案例通过订阅我们订单表（事实表）数据，通过 Debezium 将 MySQL Binlog 发送至 Kafka，通过维表 Join 和 ETL 操作把结果输出至下游的 PG 数据库。具体可以参考 Flink 公众号文章：《Flink JDBC Connector：Flink 与数据库集成最佳实践》案例进行实践操作。&lt;/p&gt;  &lt;p&gt;   &lt;a href="https://www.bilibili.com/video/BV1bp4y1q78d"&gt;https://www.bilibili.com/video/BV1bp4y1q78d&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img alt="9.png" src="https://ucc.alicdn.com/pic/developer-ecology/9fbb325b6cfc46d0b6ea3414fb0a473f.png" title="9.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h3&gt;案例 2 :  CDC Streaming ETL&lt;/h3&gt;  &lt;p&gt;模拟电商公司的订单表和物流表，需要对订单数据进行统计分析，对于不同的信息需要进行关联后续形成订单的大宽表后，交给下游的业务方使用 ES 做数据分析，这个案例演示了如何只依赖 Flink 不依赖其他组件，借助 Flink 强大的计算能力实时把 Binlog 的数据流关联一次并同步至 ES 。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="10.png" src="https://ucc.alicdn.com/pic/developer-ecology/fe46d5094ddc4f778aa81380ba4a4866.png" title="10.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;例如如下的这段 Flink SQL 代码就能完成实时同步 MySQL 中 orders 表的全量+增量数据的目的。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;CREATE TABLE orders (
  order_id INT,
  order_date TIMESTAMP(0),
  customer_name STRING,
  price DECIMAL(10, 5),
  product_id INT,
  order_status BOOLEAN
) WITH (
  &amp;apos;connector&amp;apos; = &amp;apos;mysql-cdc&amp;apos;,
  &amp;apos;hostname&amp;apos; = &amp;apos;localhost&amp;apos;,
  &amp;apos;port&amp;apos; = &amp;apos;3306&amp;apos;,
  &amp;apos;username&amp;apos; = &amp;apos;root&amp;apos;,
  &amp;apos;password&amp;apos; = &amp;apos;123456&amp;apos;,
  &amp;apos;database-name&amp;apos; = &amp;apos;mydb&amp;apos;,
  &amp;apos;table-name&amp;apos; = &amp;apos;orders&amp;apos;
);

SELECT * FROM orders&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;为了让读者更好地上手和理解，我们还提供了 docker-compose 的测试环境，更详细的案例教程请参考下文的视频链接和文档链接。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;视频链接：    &lt;br /&gt;    &lt;a href="https://www.bilibili.com/video/BV1zt4y1D7kt"&gt;https://www.bilibili.com/video/BV1zt4y1D7kt&lt;/a&gt;    &lt;br /&gt;文档教程：    &lt;br /&gt;    &lt;a href="https://github.com/ververica/flink-cdc-connectors/wiki/"&gt;https://github.com/ververica/flink-cdc-connectors/wiki/&lt;/a&gt;中文教程&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;案例 3 : Streaming Changes to Kafka&lt;/h3&gt;  &lt;p&gt;下面案例就是对 GMV 进行天级别的全站统计。包含插入/更新/删除，只有付款的订单才能计算进入 GMV ，观察 GMV 值的变化。&lt;/p&gt;  &lt;p&gt;   &lt;img alt="11.png" src="https://ucc.alicdn.com/pic/developer-ecology/708256232bc64896abc327a7cf0a01ae.png" title="11.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;视频链接：    &lt;br /&gt;    &lt;a href="https://www.bilibili.com/video/BV1zt4y1D7kt"&gt;https://www.bilibili.com/video/BV1zt4y1D7kt&lt;/a&gt;    &lt;br /&gt;文档教程：    &lt;br /&gt;    &lt;a href="https://github.com/ververica/flink-cdc-connectors/wiki/"&gt;https://github.com/ververica/flink-cdc-connectors/wiki/&lt;/a&gt;中文教程&lt;/p&gt;&lt;/blockquote&gt;  &lt;h2&gt;Flink SQL CDC 的更多应用场景&lt;/h2&gt;  &lt;p&gt;Flink SQL CDC 不仅可以灵活地应用于实时数据同步场景中，还可以打通更多的场景提供给用户选择。&lt;/p&gt;  &lt;h3&gt;Flink 在数据同步场景中的灵活定位&lt;/h3&gt;  &lt;p&gt;· 如果你已经有 Debezium/Canal + Kafka 的采集层 (E)，可以使用 Flink 作为计算层 (T) 和传输层 (L)   &lt;br /&gt;· 也可以用 Flink 替代 Debezium/Canal ，由 Flink 直接同步变更数据到 Kafka，Flink 统一 ETL 流程   &lt;br /&gt;· 如果不需要 Kafka 数据缓存，可以由 Flink 直接同步变更数据到目的地，Flink 统一 ETL 流程&lt;/p&gt;  &lt;h3&gt;Flink SQL CDC : 打通更多场景&lt;/h3&gt;  &lt;p&gt;· 实时数据同步，数据备份，数据迁移，数仓构建   &lt;br /&gt;优势：丰富的上下游（E &amp;amp; L），强大的计算（T），易用的 API（SQL），流式计算低延迟   &lt;br /&gt;· 数据库之上的实时物化视图、流式数据分析   &lt;br /&gt;· 索引构建和实时维护   &lt;br /&gt;· 业务 cache 刷新   &lt;br /&gt;· 审计跟踪   &lt;br /&gt;· 微服务的解耦，读写分离   &lt;br /&gt;· 基于 CDC 的维表关联&lt;/p&gt;  &lt;p&gt;下面介绍一下为何用 CDC 的维表关联会比基于查询的维表查询快。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;■ 基于查询的维表关联&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img alt="12.png" src="https://ucc.alicdn.com/pic/developer-ecology/48d3e43e60ae4c2cb6e810d26d24ac44.png" title="12.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;目前维表查询的方式主要是通过 Join 的方式，数据从消息队列进来后通过向数据库发起 IO 的请求，由数据库把结果返回后合并再输出到下游，但是这个过程无可避免的产生了 IO 和网络通信的消耗，导致吞吐量无法进一步提升，就算使用一些缓存机制，但是因为缓存更新不及时可能会导致精确性也没那么高。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;■ 基于 CDC 的维表关联&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img alt="13.png" src="https://ucc.alicdn.com/pic/developer-ecology/095230f5273d4dd2b92468f64eced044.png" title="13.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;我们可以通过 CDC 把维表的数据导入到维表 Join 的状态里面，在这个 State 里面因为它是一个分布式的 State ，里面保存了 Database 里面实时的数据库维表镜像，当消息队列数据过来时候无需再次查询远程的数据库了，直接查询本地磁盘的 State ，避免了 IO 操作，实现了低延迟、高吞吐，更精准。&lt;/p&gt;  &lt;p&gt;Tips：目前此功能在 1.12 版本的规划中，具体进度请关注 FLIP-132 。&lt;/p&gt;  &lt;h2&gt;未来规划&lt;/h2&gt;  &lt;p&gt;· FLIP-132 ：Temporal Table DDL（基于 CDC 的维表关联）   &lt;br /&gt;· Upsert 数据输出到 Kafka   &lt;br /&gt;· 更多的 CDC formats 支持（debezium-avro, OGG, Maxwell）   &lt;br /&gt;· 批模式支持处理 CDC 数据   &lt;br /&gt;· flink-cdc-connectors 支持更多数据库&lt;/p&gt;  &lt;h2&gt;总结&lt;/h2&gt;  &lt;p&gt;本文通过对比传统的数据同步方案与 Flink SQL CDC 方案分享了 Flink CDC 的优势，与此同时介绍了 CDC 分为日志型和查询型各自的实现原理。后续案例也演示了关于 Debezium 订阅 MySQL Binlog 的场景介绍，以及如何通过 flink-cdc-connectors 实现技术整合替代订阅组件。除此之外，还详细讲解了 Flink CDC 在数据同步、物化视图、多机房备份等的场景，并重点讲解了社区未来规划的基于 CDC 维表关联对比传统维表关联的优势以及 CDC 组件工作。&lt;/p&gt;  &lt;p&gt;希望通过这次分享，大家对 Flink SQL CDC 能有全新的认识和了解，在未来实际生产开发中，期望 Flink CDC 能带来更多开发的便捷和更丰富的使用场景。&lt;/p&gt;  &lt;h2&gt;Q &amp;amp; A&lt;/h2&gt;  &lt;p&gt;   &lt;strong&gt;1、GROUP BY 结果如何写到 Kafka ？&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;因为 group by 的结果是一个更新的结果，目前无法写入 append only 的消息队列中里面去。更新的结果写入 Kafka 中将在 1.12 版本中原生地支持。在 1.11 版本中，可以通过 flink-cdc-connectors 项目提供的 changelog-json format 来实现该功能，具体见文档。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;文档链接：    &lt;br /&gt;    &lt;a href="https://github.com/ververica/flink-cdc-connectors/wiki/Changelog-JSON-Format"&gt;https://github.com/ververica/flink-cdc-connectors/wiki/Changelog-JSON-Format&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;   &lt;strong&gt;2、CDC 是否需要保证顺序化消费？&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;是的，数据同步到 kafka ，首先需要 kafka 在分区中保证有序，同一个 key 的变更数据需要打入到同一个 kafka 的分区里面。这样 flink 读取的时候才能保证顺序。&lt;/p&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>jianshu</category>
      <guid isPermaLink="true">https://itindex.net/detail/61000-flink-sql-cdc</guid>
      <pubDate>Mon, 09 Nov 2020 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>MYSQL logstash 同步数据到es的几种方案对比以及每种方案数据丢失原因分析。</title>
      <link>https://itindex.net/detail/60911-mysql-logstash-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;h3&gt;MYSQL logstash 同步增量数据到ES&lt;/h3&gt;  &lt;p&gt;最近一段时间，在使用mysql通过logstash-jdbc同步数据到es,但是总是会有一定程度数据丢失。logstash-jdbc无非是通过sql遍历数据表的所有数据，然后同步到es。&lt;/p&gt;  &lt;p&gt;对于表里面的所有字段都需要查出来然后同步到es中去。本地测试数据表结构如下:&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;CREATE TABLE `user` (      &lt;br /&gt;  `id` bigint(20) NOT NULL AUTO_INCREMENT,      &lt;br /&gt;  `username` varchar(32) CHARACTER SET utf8 NOT NULL,      &lt;br /&gt;  `upnum` decimal(20,0) NOT NULL,      &lt;br /&gt;  `last_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,      &lt;br /&gt;  PRIMARY KEY (`id`),      &lt;br /&gt;  KEY `last_update_time` (`last_update_time`)      &lt;br /&gt;) ENGINE=InnoDB AUTO_INCREMENT=25094590 DEFAULT CHARSET=latin1;      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;数据同步脚本分为全量同步与增量同步。&lt;/p&gt;  &lt;p&gt;全量同步sql如下:&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;select id,username,upnum,last_update_time from user where id&amp;gt;{$MIN_ID} and id&amp;lt;{$MAX_ID}      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;通过shell的方式在每次执行之前替换脚本里面的最大最小ID,每批查询10万ID数据。&lt;/p&gt;  &lt;p&gt;增量同步sql:&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;select id,username,upnum,last_update_time from user where last_update_time&amp;gt;=:last_sql_value      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;增量同步，每次记录last_update_time最后时间，然后每次查询查询上一次时间之后的数据。最终结果，增量丢数据！！！&lt;/p&gt;  &lt;h4&gt;方案1&lt;/h4&gt;  &lt;p&gt;每次查询last_update_time 时间大于等于上一次数据时间的数据，分页查询。&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;select * from (select id,username,upnum,last_update_time from user where last_update_time&amp;gt;=:last_sql_val) as a limit 10000 offset 10000      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这种方案会涉及到几个问题:&lt;/p&gt;  &lt;p&gt;1)  相同last_update_time的数据导致数据最终查询出来的顺序有可能不确定。导致有可能在查第一页的时候，数据id=100001的数据在第二页，但是当分页到第二页的时候，id=100001的数据又在第一页了，从而导致这条数据不会被同步。&lt;/p&gt;  &lt;p&gt;2）last_update_time 使用的是ON UPDATE CURRENT_TIMESTAMP，时间是更新语句执行的时间。但是只有当更新语句事务提交的时候才会被查询到。这种情况，当一条数据在    &lt;code&gt;2020-09-30 00:00:00&lt;/code&gt;执行update语句，但是因为事务提交时间比较长，到    &lt;code&gt;2020-09-30 00:00:03&lt;/code&gt;的时候才提交。这个时候在    &lt;code&gt;2020-09-30 00:00:03&lt;/code&gt;的时候，同步sql有可能已经是翻页翻到时间为    &lt;code&gt;2020-09-30 00:00:02&lt;/code&gt;的数据了，因此导致数据丢失。&lt;/p&gt;  &lt;p&gt;3）主从同步导致数据分页查询不到。一般都用es了，都会有主从同步，而es数据同步也是在从库中读取数据同步的。因此主从同步的这个时间延时也会导致数据查询不正确。&lt;/p&gt;  &lt;h4&gt;方案2&lt;/h4&gt;  &lt;p&gt;不分页查询，避免分页造成的数据丢失。不分页查询的话，需要将数据查询的时间区间减少。调整logstatsh调度频率，每5秒钟执行一次，每次查询数据10万条。&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;select id,username,upnum,last_update_time from user where last_update_time&amp;gt;=:last_sql_val limit 10000      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个方案的问题:&lt;/p&gt;  &lt;p&gt;1) 查询条件是大于等于。因此，当在last_update_time 临界时间    &lt;code&gt;2020-09-30 00:00:00&lt;/code&gt;有10万条数据(比如添加字段初始化数据)，就会让整个数据更新脚本    &lt;strong&gt;原地踏步&lt;/strong&gt;    &lt;br /&gt;2) 主从同步，已经事务提交延迟的问题依然存在&lt;/p&gt;  &lt;h4&gt;方案3&lt;/h4&gt;  &lt;p&gt;使用两个脚本同步数据，新增一个5分钟延迟的同步脚本，减少数据丢失的概率&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;select id,username,upnum,last_update_time from user where last_update_time&amp;gt;:last_sql_val limit 10000      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;pre&gt;    &lt;code&gt;select id,username,upnum,last_update_time from user where last_update_time&amp;gt;:last_sql_val and last_update_time&amp;lt;DATE_SUB(NOW(),INTERVAL 5 MINUTE) limit 30000      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;这个方案的问题:    &lt;br /&gt;1) 不会出现原地踏步的情况，但是在同时间的数据可能会有数据丢失    &lt;br /&gt;2) 可以减少因为主从同步，事务提交延迟的数据丢失，但是没办法确保100%不丢失&lt;/p&gt;  &lt;h4&gt;方案4&lt;/h4&gt;  &lt;p&gt;使用binlog的方式同步到es。但是对于现有的历史数据，还是需要通过遍历数据表的方式进行同步。&lt;/p&gt;  &lt;h4&gt;结论&lt;/h4&gt;  &lt;p&gt;以上内容是目前在工作中mysql同步数据到es的几个方案。按目前网络中大部分文章，都是通过logstash进行数据同步。&lt;/p&gt;  &lt;p&gt;但是请注意，logstash的方案是有可能造成成数据丢失的。而这种数据丢失对于insert操作而言，还是可以确定是否有没有丢数据。毕竟对比一下两边的数据量就可以了。&lt;/p&gt;  &lt;p&gt;但是，对于update操作呢，怎么确定是否全部同步过去了呢。假如您有一个几千万用户单据信息，而且还是关于钱的，怎么样确定每次更新都正确无误的同步到了es呢？&lt;/p&gt;  &lt;p&gt;基于上面的分析，最靠谱的办法，还是通过binlog的方式同步es。&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60911-mysql-logstash-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Wed, 07 Oct 2020 15:59:50 CST</pubDate>
    </item>
    <item>
      <title>实时数据同步服务如何保证消息的顺序性</title>
      <link>https://itindex.net/detail/60822-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;  &lt;blockquote&gt;   &lt;p&gt;    &lt;strong&gt;     &lt;a href="https://mp.weixin.qq.com/s?__biz=MzAwMjk5NTY3Mw==&amp;mid=2247483897&amp;idx=1&amp;sn=1f3983e5ae389bafa449ce3c504f843e&amp;chksm=9ac0a54fadb72c597b2f4f36a09e1eabc89ba8c504355aa60f585562c88f902094e6852b1bf3&amp;token=1597623571&amp;lang=zh_CN#rd" target="_blank"&gt;上一篇&lt;/a&gt;&lt;/strong&gt; 介绍了移山(数据迁移平台)实时数据同步的整体架构；     &lt;br /&gt;本文主要介绍移山(数据迁移平台)实时数据同步是如何保证消息的顺序性。&lt;/p&gt;   &lt;p&gt;可以访问     &lt;strong&gt;     &lt;a href="https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&amp;album_id=1410124501450571776&amp;__biz=MzAwMjk5NTY3Mw==#wechat_redirect" target="_blank"&gt;这里&lt;/a&gt;&lt;/strong&gt; 查看更多关于大数据平台建设的原创文章。&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt; &lt;div&gt;  &lt;h3&gt;一. 什么是消息的顺序性？&lt;/h3&gt;  &lt;blockquote&gt;   &lt;ol&gt;    &lt;li&gt;     &lt;p&gt;消息生产端将消息发送给同一个MQ服务器的同一个分区，并且按顺序发送；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;消费消费端按照消息发送的顺序进行消费。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;/div&gt; &lt;div&gt;  &lt;h3&gt;二. 为什么要保证消息的顺序性？&lt;/h3&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;   &lt;p&gt;在某些业务功能场景下需要保证消息的发送和接收顺序是一致的，否则会影响数据的使用。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h4&gt;需要保证消息有序的场景&lt;/h4&gt;   &lt;blockquote&gt;    &lt;p&gt;移山的实时数据同步使用      &lt;code&gt;canal&lt;/code&gt; 组件订阅MySQL数据库的日志，并将其投递至 kafka 中（想了解移山实时同步服务架构设计的可以点      &lt;a href="https://mp.weixin.qq.com/s?__biz=MzAwMjk5NTY3Mw==&amp;mid=2247483897&amp;idx=1&amp;sn=1f3983e5ae389bafa449ce3c504f843e&amp;chksm=9ac0a54fadb72c597b2f4f36a09e1eabc89ba8c504355aa60f585562c88f902094e6852b1bf3&amp;token=1597623571&amp;lang=zh_CN#rd" target="_blank"&gt;这里&lt;/a&gt;）；      &lt;br /&gt;kafka 消费端再根据具体的数据使用场景去处理数据（存入 HBase、MySQL 或直接做实时分析）；      &lt;br /&gt;由于binlog 本身是有序的，因此写入到mq之后也需要保障顺序。&lt;/p&gt;&lt;/blockquote&gt;   &lt;ol&gt;    &lt;li&gt;     &lt;p&gt;假如现在移山创建了一个实时同步任务，然后订阅了一个业务数据库的订单表；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;上游业务，向订单表里插入了一个订单，然后对该订单又做了一个更新操作，则 binlog 里会自动写入插入操作和更新操作的数据，这些数据会被 canal server 投递至 kafka broker 里面；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;如果 kafka 消费端先消费到了更新日志，后消费到插入日志，则在往目标表里做操作时就会因为数据缺失导致发生异常。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h3&gt;三. 移山实时同步服务是怎么保证消息的顺序性&lt;/h3&gt;   &lt;p&gt;实时同步服务消息处理整体流程如下：&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;p&gt;    &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815224725680-751244705.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;p&gt;我们主要通过以下两个方面去保障保证消息的顺序性。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h4&gt;1. 将需要保证顺序的消息发送到同一个partition&lt;/h4&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;1.1 kafka的同一个partition内的消息是有序的&lt;/h5&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;kafka 的同一个 partition 用一个write ahead log组织， 是一个有序的队列，所以可以保证FIFO的顺序；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;因此生产者按照一定的顺序发送消息，broker 就会按照这个顺序把消息写入 partition，消费者也会按照相同的顺序去读取消息；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;kafka 的每一个 partition 不会同时被两个消费者实例消费，由此可以保证消息消费的顺序性。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;1.2 控制同一key分发到同一partition&lt;/h5&gt;   &lt;p&gt;要保证同一个订单的多次修改到达 kafka 里的顺序不能乱，可以在Producer 往 kafka 插入数据时，控制同一个key （可以采用订单主键key-hash算法来实现）发送到同一 partition，这样就能保证同一笔订单都会落到同一个 partition 内。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;1.3 canal 需要做的配置&lt;/h5&gt;   &lt;p&gt;canal 目前支持的mq有    &lt;code&gt;kafka/rocketmq&lt;/code&gt;，本质上都是基于本地文件的方式来支持了分区级的顺序消息的能力。我们只需在配置 instance 的时候开启如下配置即可：&lt;/p&gt;   &lt;p&gt;    &lt;strong&gt;1&amp;gt; canal.properties&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;pre&gt;# leader节点会等待所有同步中的副本确认之后再确认这条记录是否发送完成
canal.mq.acks = all&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;   &lt;p&gt;备注：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;这样只要至少有一个同步副本存在，记录就不会丢失。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;   &lt;p&gt;    &lt;strong&gt;2&amp;gt; instance.properties&lt;/strong&gt;&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;pre&gt;1 # 散列模式的分区数
2 canal.mq.partitionsNum=2
3 # 散列规则定义 库名.表名: 唯一主键，多个表之间用逗号分隔
4 canal.mq.partitionHash=test.lyf_canal_test:id&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;   &lt;p&gt;备注：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;同一条数据的增删改操作 产生的 binlog 数据都会写到同一个分区内；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;查看指定topic的指定分区的消息，可以使用如下命令：&lt;/p&gt;     &lt;div&gt;      &lt;div&gt;       &lt;pre&gt;bin/kafka-console-consumer.sh --bootstrap-server serverlist --topic topicname --from-beginning --partition 0&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h4&gt;2. 通过日志时间戳和日志偏移量进行乱序处理&lt;/h4&gt;   &lt;p&gt;将同一个订单数据通过指定key的方式发送到同一个 partition 可以解决大部分情况下的数据乱序问题。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;2.1 特殊场景&lt;/h5&gt;   &lt;p&gt;对于一个有着先后顺序的消息A、B，正常情况下应该是A先发送完成后再发送B。但是在异常情况下：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;A发送失败了，B发送成功，而A由于重试机制在B发送完成之后重试发送成功了；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;这时对于本身顺序为AB的消息顺序变成了BA。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;   &lt;p&gt;移山的实时同步服务会在将订阅到的数据存入HBase之前再加一层乱序处理 。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;2.2 binlog里的两个重要信息&lt;/h5&gt;   &lt;p&gt;使用     &lt;code&gt;mysqlbinlog&lt;/code&gt; 查看 binlog：&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;pre&gt;/usr/bin/mysqlbinlog --base64-output=decode-rows -v /var/lib/mysql/mysql-bin.000001&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;   &lt;p&gt;执行时间和偏移量：&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;p&gt;    &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225037527-1872580586.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;p&gt;备注：&lt;/p&gt;   &lt;ol&gt;    &lt;li&gt;     &lt;p&gt;每条数据都会有执行时间和偏移量这两个重要信息，      &lt;strong&gt;下边的校验逻辑核心正是借助了这两个值&lt;/strong&gt;；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;执行的sql 语句在 binlog 中是以base64编码格式存储的，如果想查看sql 语句，需要加上：      &lt;code&gt;--base64-output=decode-rows -v&lt;/code&gt; 参数来解码；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;偏移量：&lt;/p&gt;     &lt;ul&gt;      &lt;li&gt;       &lt;p&gt;Position 就代表 binlog 写到这个偏移量的地方，也就是写了这么多字节，即当前 binlog 文件的大小；&lt;/p&gt;&lt;/li&gt;      &lt;li&gt;       &lt;p&gt;也就是说后写入数据的 Position 肯定比先写入数据的 Position 大，        &lt;strong&gt;因此可以根据 Position 大小来判断消息的顺序。&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h4&gt;3.消息乱序处理演示&lt;/h4&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;3.1 在订阅表里插入一条数据，然后再做两次更新操作：&lt;/h5&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;     &lt;pre&gt;MariaDB [test]&amp;gt; insert into lyf_canal_test (name,status,content) values(&amp;apos;demo1&amp;apos;,1,&amp;apos;demo1 test&amp;apos;);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [test]&amp;gt; update lyf_canal_test set name = &amp;apos;demo update&amp;apos; where id = 13;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
 
MariaDB [test]&amp;gt; update lyf_canal_test set name = &amp;apos;demo update2&amp;apos;,content=&amp;apos;second update&amp;apos;,status=2 where id = 13;
Query OK, 1 row affected (0.00 sec)&lt;/pre&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;3.2 产生三条需要保证顺序的消息&lt;/h5&gt;   &lt;p&gt;把    &lt;strong&gt;插入，第一次更新，第二次更新&lt;/strong&gt;这三次操作产生的 binlog 被 canal server 推送至 kafka 中的消息分别称为：    &lt;strong&gt;消息A，消息B，消息C&lt;/strong&gt;。&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;消息A：       &lt;br /&gt;      &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225120790-629306877.png"&gt;&lt;/img&gt;      &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;消息B：       &lt;br /&gt;      &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225136075-487110997.png"&gt;&lt;/img&gt;      &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;消息C：       &lt;br /&gt;      &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225148641-157628035.png"&gt;&lt;/img&gt;      &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;3.3 网络原因造成消息乱序&lt;/h5&gt;   &lt;p&gt;假设由于不可知的网络原因：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;kafka broker收到的三条消息分别为：      &lt;strong&gt;消息A，消息C，消息B&lt;/strong&gt;；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;则 kafka 消费端消费到的这三条消息先后顺序就是：      &lt;strong&gt;消息A，消息C，消息B&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;这样就造成了消息的乱序，因此      &lt;strong&gt;订阅到的数据在存入目标表前必须得加乱序校验处理&lt;/strong&gt;。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;3.4 消息乱序处理逻辑&lt;/h5&gt;   &lt;p&gt;我们利用HBase的特性，将数据主键做为目标表的 rowkey。当 kafka 消费端消费到数据时，乱序处理主要流程（摘自禧云数芯大数据平台技术白皮书）如下：&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;p&gt;    &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225204362-2053933503.png"&gt;&lt;/img&gt;&lt;/p&gt;   &lt;p&gt;demo的三条消息处理流程如下：     &lt;br /&gt;1&amp;gt; 判断消息A 的主键id做为rowkey在hbase的目标表中不存在，则将消息A的数据直接插入HBase：     &lt;br /&gt;    &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225217724-1103973964.png"&gt;&lt;/img&gt;    &lt;br /&gt;&lt;/p&gt;   &lt;p&gt;2&amp;gt; 消息C 的主键id做为rowkey，已经在目标表中存在，则这时需要拿消息C 的执行时间和表中存储的执行时间去判断：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;如果消息C 中的执行时间小于表中存储的执行时间，则证明消息C 是重复消息或乱序的消息，直接丢弃；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;消息C 中的执行时间大于表中存储的执行时间，则直接更新表数据（本demo即符合该种场景）：       &lt;br /&gt;      &lt;img alt="" src="https://img2020.cnblogs.com/blog/414002/202008/414002-20200815225229229-506694159.png"&gt;&lt;/img&gt;      &lt;br /&gt;&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;消息C 中的执行时间等于表中存储的执行时间，则这时需要拿消息C 的偏移量和表中存储的偏移量去判断：&lt;/p&gt;     &lt;ul&gt;      &lt;li&gt;       &lt;p&gt;消息C 中的偏移量小于表中存储的偏移量，则证明消息C 是重复消息，直接丢弃；&lt;/p&gt;&lt;/li&gt;      &lt;li&gt;       &lt;p&gt;消息C 中的偏移量大于等于表中存储的偏移量，则直接更新表数据。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;   &lt;p&gt;3&amp;gt; 消息B 的主键id做为rowkey，已经在目标表中存在，则这时需要拿消息B 的执行时间和表中存储的执行时间去判断：&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;由于消息B中的执行时间小于表中存储的执行时间（即消息C 的执行时间），因此消息B 直接丢弃。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h5&gt;3.5 主要代码&lt;/h5&gt;   &lt;p&gt;kafka 消费端将消费到的消息进行格式化处理和组装，并借助     &lt;code&gt;HBase-client API&lt;/code&gt; 来完成对 HBase 表的操作。&lt;/p&gt;   &lt;p&gt;1&amp;gt; 使用    &lt;code&gt;Put&lt;/code&gt;组装单行数据&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;     &lt;pre&gt;/**
* 包名： org.apache.hadoop.hbase.client.Put
* hbaseData 为从binlog订阅到的数据，通过循环，为目标HBase表
* 添加rowkey、列簇、列数据。
* 作用：用来对单个行执行加入操作。
*/
Put put = new Put(Bytes.toBytes(hbaseData.get(&amp;quot;id&amp;quot;)));
// hbaseData 为从binlog订阅到的数据，通过循环，为目标HBase表添加列簇和列
put.addColumn(Bytes.toBytes(&amp;quot;info&amp;quot;), Bytes.toBytes(mapKey), Bytes.toBytes(hbaseData.get(mapKey)));&lt;/pre&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;    &lt;p&gt; &lt;/p&gt;&lt;/div&gt;   &lt;p&gt;2&amp;gt; 使用     &lt;code&gt;checkAndMutate&lt;/code&gt;，更新    &lt;code&gt;HBase&lt;/code&gt;表的数据&lt;/p&gt;   &lt;p&gt;只有服务端对应rowkey的列数据与预期的值符合期望条件（大于、小于、等于）时，才会将put操作提交至服务端。&lt;/p&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;     &lt;pre&gt;// 如果 update_info（列族） execute_time（列） 不存在值就插入数据，如果存在则返回false
boolean res1 = table.checkAndMutate(Bytes.toBytes(hbaseData.get(&amp;quot;id&amp;quot;)), Bytes.toBytes(&amp;quot;update_info&amp;quot;)) .qualifier(Bytes.toBytes(&amp;quot;execute_time&amp;quot;)).ifNotExists().thenPut(put);
 
// 如果存在，则去比较执行时间
if (!res1) {
// 如果本次传递的执行时间大于HBase中的执行时间，则插入put
boolean res2 =table.checkAndPut(Bytes.toBytes(hbaseData.get(&amp;quot;id&amp;quot;)), Bytes.toBytes(&amp;quot;update_info&amp;quot;),
Bytes.toBytes(&amp;quot;execute_time&amp;quot;), CompareFilter.CompareOp.GREATER, Bytes.toBytes(hbaseData.get(&amp;quot;execute_time&amp;quot;)),put);
 
// 执行时间相等时，则去比较偏移量，本次传递的值大于HBase中的值则插入put
if (!res2) {
boolean res3 = table.checkAndPut(Bytes.toBytes(hbaseData.get(&amp;quot;id&amp;quot;)),
Bytes.toBytes(&amp;quot;update_info&amp;quot;), Bytes.toBytes(&amp;quot;execute_position&amp;quot;), CompareFilter.CompareOp.GREATER, Bytes.toBytes(hbaseData.get(&amp;quot;execute_position&amp;quot;)),put);
}
}&lt;/pre&gt;     &lt;div&gt;      &lt;a title="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;"&gt;       &lt;img alt="&amp;#22797;&amp;#21046;&amp;#20195;&amp;#30721;" src="https://common.cnblogs.com/images/copycode.gif"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;    &lt;p&gt; &lt;/p&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;   &lt;h3&gt;四.总结&lt;/h3&gt;   &lt;ol&gt;    &lt;li&gt;     &lt;p&gt;目前移山的实时同步服务，kafka 消费端是使用一个线程去消费数据；&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;如果将来有版本升级需求，将消费端改为多个线程去消费数据时，要考虑到多线程消费时有序的消息会被打乱这种情况的解决办法。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;   &lt;h3&gt;    &lt;br /&gt;&lt;/h3&gt;&lt;/div&gt;&lt;/div&gt;
     
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60822-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Sun, 16 Aug 2020 08:48:15 CST</pubDate>
    </item>
    <item>
      <title>Mysql和Redis数据同步策略 - 元思 - 博客园</title>
      <link>https://itindex.net/detail/60712-mysql-redis-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;div&gt;    &lt;h2&gt;为什么对缓存只删除不更新&lt;/h2&gt;    &lt;p&gt;不更新缓存是防止并发更新导致的数据不一致。      &lt;br /&gt;所以为了降低数据不一致的概率，不应该更新缓存，而是直接将其删除，      &lt;br /&gt;然后等待下次发生cache miss时再把数据库中的数据同步到缓存。&lt;/p&gt;    &lt;h2&gt;先更新数据库还是先删除缓存?&lt;/h2&gt;    &lt;p&gt;有两个选择：      &lt;br /&gt;      &lt;strong&gt;1. 先删除缓存，再更新数据库&lt;/strong&gt;      &lt;br /&gt;      &lt;strong&gt;2. 先更新数据库，再删除缓存&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;如果先删除缓存，有一个明显的逻辑错误：考虑两个并发操作，线程A删除缓存后，线程B读该数据时会发生Cache Miss，然后从数据库中读出该数据并同步到缓存中，此时线程A更新了数据库。      &lt;br /&gt;结果导致，缓存中是老数据，数据库中是新数据，并且之后的读操作都会直接读取缓存中的脏数据。（直到key过期被删除或者被LRU策略踢出）      &lt;br /&gt;如果数据库更新成功后，再删除缓存，就不会有上面这个问题。      &lt;br /&gt;可能是由于数据库优先，第二种方式也被称为Cache Aside Pattern。&lt;/p&gt;    &lt;h2&gt;Cache Aside Pattern&lt;/h2&gt;    &lt;p&gt;cache aside在绝大多数情况下能做到数据一致性，但是在极端情况仍然存在问题。&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;首先更新数据库(A)和删除缓存(B)不是原子操作，任何在A之后B之前的读操作，都会读到redis中的旧数据。        &lt;br /&gt;但是，正常情况下操作缓存的速度会很快，通常是毫秒级，出现上述情况的概率很低。&lt;/li&gt;      &lt;li&gt;更新完数据库后，线程意外被kill掉，由于没有删除缓存，缓存中的脏数据会一直存在。&lt;/li&gt;      &lt;li&gt;线程A读数据时cache miss，从Mysql中查询到数据，还没来得及同步到redis中,        &lt;br /&gt;此时线程B更新了数据库并把Redis中的旧值删除。随后，线程A把之前查到的数据同步到了Redis。        &lt;br /&gt;显然，此时redis中的是脏数据。        &lt;br /&gt;通常数据库读操作比写操作快很多，所以除非线程A在同步redis前意外卡住了，否则发生上述情况的概率极低。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;虽然以上情况都有可能发生，但是发生的概率相比“先删除缓存再更新数据库”会低很多。&lt;/p&gt;    &lt;h2&gt;Read/Write Through Pattern&lt;/h2&gt;    &lt;p&gt;cache aside是我们自己的应用程序维护两个数据存储系统，而Read/Write Through Pattern是把同步数据的问题交给缓存系统了，应用程序不需要关心。      &lt;br /&gt;Read Through是指发生cache miss时，缓存系统自动去数据库加载数据。      &lt;br /&gt;Write Through是指如果cache miss，直接更新数据库，然后返回，如果cache hit，则更新缓存后，由缓存系统自动同步到数据库。      &lt;br /&gt;以Redis为例，通常我们不会把数据库的数据全部缓存到redis，而是采用一定的数据精简或压缩策略，以节省缓存空间。      &lt;br /&gt;就是说，让缓存系统设计出通用的缓存方案不太现实，不过根据自己的业务定制一个在项目内部通用的中间件是可行的。&lt;/p&gt;    &lt;h2&gt;Write Behind&lt;/h2&gt;    &lt;p&gt;Write Behind方案在更新数据时，只更新缓存，不更新数据库。而是由另外一个服务异步的把数据更新到数据库。      &lt;br /&gt;逻辑上，和Linux中的write back很类似。这个设计的好处是，I/O操作很快，因为是纯内存操作。      &lt;br /&gt;但是由于异步写库，可能要牺牲一些数据一致性，譬如突然宕机会丢失所有未写入数据库的内存数据。&lt;/p&gt;    &lt;p&gt;阿里巴巴的Canal中间件是一种相反的设计，它先更新mysql，然后通过binlog把数据自动同步到redis。      &lt;br /&gt;这种方案会全量同步数据到redis，不适合只缓存热点数据的应用。&lt;/p&gt;    &lt;h2&gt;总结&lt;/h2&gt;    &lt;p&gt;以上没有哪种方案是完美的，都无法做到强一致性。      &lt;br /&gt;我们总要在性能和数据准确性之间做出妥协。&lt;/p&gt;    &lt;blockquote&gt;      &lt;p&gt;        &lt;a href="https://www.pixelstech.net/article/1562504974-Consistency-between-Redis-Cache-and-SQL-Database"&gt;https://www.pixelstech.net/article/1562504974-Consistency-between-Redis-Cache-and-SQL-Database&lt;/a&gt;        &lt;br /&gt;        &lt;a href="https://coolshell.cn/articles/17416.html"&gt;https://coolshell.cn/articles/17416.html&lt;/a&gt;        &lt;br /&gt;        &lt;a href="https://www.quora.com/Why-does-Facebook-use-delete-to-remove-the-key-value-pair-in-Memcached-instead-of-updating-the-Memcached-during-write-request-to-the-backend"&gt;为什么不更新缓存，而是直接删除&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60712-mysql-redis-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Wed, 24 Jun 2020 09:55:37 CST</pubDate>
    </item>
    <item>
      <title>otter 数据同步项目 at master · alibaba/otter · GitHub</title>
      <link>https://itindex.net/detail/60700-otter-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#29615;&amp;#22659;&amp;#25645;&amp;#24314;--&amp;#25171;&amp;#21253;"&gt;&lt;/a&gt;环境搭建 &amp;amp; 打包&lt;/h1&gt;  &lt;strong&gt;环境搭建：&lt;/strong&gt;  &lt;ol&gt;    &lt;li&gt;进入$otter_home目录&lt;/li&gt;    &lt;li&gt;执行：mvn clean install&lt;/li&gt;    &lt;li&gt;导入maven项目。如果eclipse下报&amp;quot;Missing artifact com.oracle:ojdbc14:jar:10.2.0.3.0&amp;quot;，修改$otter_home/pom.xml中&amp;quot;${user.dir}/lib/ojdbc14-10.2.0.3.0.jar&amp;quot;为绝对路径，比如&amp;quot;d:/lib/ojdbc14-10.2.0.3.0.jar&amp;quot;&lt;/li&gt;&lt;/ol&gt;  &lt;strong&gt;打包：&lt;/strong&gt;  &lt;ol&gt;    &lt;li&gt;进入$otter_home目录&lt;/li&gt;    &lt;li&gt;执行：mvn clean install -Dmaven.test.skip -Denv=release&lt;/li&gt;    &lt;li&gt;发布包位置：$otter_home/target&lt;/li&gt;&lt;/ol&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#39033;&amp;#30446;&amp;#32972;&amp;#26223;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E9%A1%B9%E7%9B%AE%E8%83%8C%E6%99%AF" name="user-content-%e9%a1%b9%e7%9b%ae%e8%83%8c%e6%99%af"&gt;&lt;/a&gt;项目背景&lt;/h1&gt;  &lt;p&gt;   阿里巴巴B2B公司，因为业务的特性，卖家主要集中在国内，买家主要集中在国外，所以衍生出了杭州和美国异地机房的需求，同时为了提升用户体验，整个机房的架构为双A，两边均可写，由此诞生了otter这样一个产品。&lt;/p&gt;  &lt;p&gt;   otter第一版本可追溯到04~05年，此次外部开源的版本为第4版，开发时间从2011年7月份一直持续到现在，目前阿里巴巴B2B内部的本地/异地机房的同步需求基本全上了otte4。&lt;/p&gt;  &lt;strong&gt;目前同步规模：&lt;/strong&gt;  &lt;ol&gt;    &lt;li&gt;同步数据量6亿&lt;/li&gt;    &lt;li&gt;文件同步1.5TB(2000w张图片)&lt;/li&gt;    &lt;li&gt;涉及200+个数据库实例之间的同步&lt;/li&gt;    &lt;li&gt;80+台机器的集群规模&lt;/li&gt;&lt;/ol&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#39033;&amp;#30446;&amp;#20171;&amp;#32461;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D" name="user-content-%e9%a1%b9%e7%9b%ae%e4%bb%8b%e7%bb%8d"&gt;&lt;/a&gt;项目介绍&lt;/h1&gt;  &lt;p&gt;名称：otter [&amp;apos;ɒtə(r)]&lt;/p&gt;  &lt;p&gt;译意： 水獭，数据搬运工&lt;/p&gt;  &lt;p&gt;语言： 纯java开发&lt;/p&gt;  &lt;p&gt;定位： 基于数据库增量日志解析，准实时同步到本机房或异地机房的mysql/oracle数据库. 一个分布式数据库同步系统&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#24037;&amp;#20316;&amp;#21407;&amp;#29702;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86" name="user-content-%e5%b7%a5%e4%bd%9c%e5%8e%9f%e7%90%86"&gt;&lt;/a&gt;工作原理&lt;/h1&gt;  &lt;p&gt;    &lt;a href="https://camo.githubusercontent.com/2988fbbc7ddfe94ed027cd71720b1ffa5912a635/687474703a2f2f646c322e69746579652e636f6d2f75706c6f61642f6174746163686d656e742f303038382f313138392f64343230636131342d326438302d336435352d383038312d6239303833363036613830312e6a7067" rel="noopener noreferrer" target="_blank"&gt;      &lt;img alt="" height="303" src="https://camo.githubusercontent.com/2988fbbc7ddfe94ed027cd71720b1ffa5912a635/687474703a2f2f646c322e69746579652e636f6d2f75706c6f61642f6174746163686d656e742f303038382f313138392f64343230636131342d326438302d336435352d383038312d6239303833363036613830312e6a7067" width="848"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;原理描述：&lt;/p&gt;  &lt;p&gt;1.   基于Canal开源产品，获取数据库增量日志数据。 什么是Canal,  请    &lt;a href="https://github.com/alibaba/canal"&gt;点击&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;2.   典型管理系统架构，manager(web管理)+node(工作节点)&lt;/p&gt;  &lt;p&gt;    a.  manager运行时推送同步配置到node节点&lt;/p&gt;  &lt;p&gt;    b.  node节点将同步状态反馈到manager上&lt;/p&gt;  &lt;p&gt;3.  基于zookeeper，解决分布式状态调度的，允许多node节点之间协同工作.&lt;/p&gt;  &lt;h3&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#20160;&amp;#20040;&amp;#26159;canal-"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E4%BB%80%E4%B9%88%E6%98%AFcanal-" name="user-content-%e4%bb%80%e4%b9%88%e6%98%afcanal-"&gt;&lt;/a&gt;什么是canal?&lt;/h3&gt;otter之前开源的一个子项目，开源链接地址：  &lt;a href="https://github.com/alibaba/canal"&gt;http://github.com/alibaba/canal&lt;/a&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#introduction"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#introduction" name="user-content-introduction"&gt;&lt;/a&gt;Introduction&lt;/h1&gt;  &lt;p&gt;See the page for introduction:    &lt;a href="https://github.com/alibaba/otter/wiki/Introduction"&gt;Introduction&lt;/a&gt;.&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#quickstart"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#quickstart" name="user-content-quickstart"&gt;&lt;/a&gt;QuickStart&lt;/h1&gt;  &lt;p&gt;See the page for quick start:    &lt;a href="https://github.com/alibaba/otter/wiki/QuickStart"&gt;QuickStart&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#adminguide"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#adminguide" name="user-content-adminguide"&gt;&lt;/a&gt;AdminGuide&lt;/h1&gt;  &lt;p&gt;See the page for admin deploy guide :    &lt;a href="https://github.com/alibaba/otter/wiki/Adminguide"&gt;AdminGuide&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#30456;&amp;#20851;&amp;#25991;&amp;#26723;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E7%9B%B8%E5%85%B3%E6%96%87%E6%A1%A3" name="user-content-%e7%9b%b8%e5%85%b3%e6%96%87%e6%a1%a3"&gt;&lt;/a&gt;相关文档&lt;/h1&gt;  &lt;p&gt;See the page for 文档:    &lt;a href="https://github.com/alibaba/otter/wiki/%E7%9B%B8%E5%85%B3ppt%26pdf"&gt;相关PPT&amp;amp;PDF&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#24120;&amp;#35265;&amp;#38382;&amp;#39064;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98" name="user-content-%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98"&gt;&lt;/a&gt;常见问题&lt;/h1&gt;  &lt;p&gt;See the page for FAQ:    &lt;a href="https://github.com/alibaba/otter/wiki/Faq"&gt;FAQ&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#29256;&amp;#26412;&amp;#30456;&amp;#20851;-"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E7%89%88%E6%9C%AC%E7%9B%B8%E5%85%B3-" name="user-content-%e7%89%88%e6%9c%ac%e7%9b%b8%e5%85%b3-"&gt;&lt;/a&gt;版本相关:&lt;/h1&gt;  &lt;p&gt;1. 建议版本：4.2.15  (otter开源版本从内部演变而来，所以初始版本直接从4.x开始)&lt;/p&gt;  &lt;p&gt;2. 下载发布包：    &lt;a href="https://github.com/alibaba/otter/releases"&gt;download&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;3. maven依赖 ： 暂无&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#30456;&amp;#20851;&amp;#24320;&amp;#28304;"&gt;&lt;/a&gt;相关开源&lt;/h1&gt;  &lt;ol&gt;    &lt;li&gt;阿里巴巴mysql数据库binlog的增量订阅&amp;amp;消费组件：      &lt;a href="https://github.com/alibaba/canal"&gt;http://github.com/alibaba/canal&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;阿里巴巴去Oracle数据迁移同步工具(目标支持MySQL/DRDS)：      &lt;a href="https://github.com/alibaba/yugong"&gt;http://github.com/alibaba/yugong&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h1&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#38382;&amp;#39064;&amp;#21453;&amp;#39304;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88" name="user-content-%e9%97%ae%e9%a2%98%e5%8f%8d%e9%a6%88"&gt;&lt;/a&gt;问题反馈&lt;/h1&gt;  &lt;h3&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#&amp;#27880;&amp;#24847;canalotter-qq&amp;#35752;&amp;#35770;&amp;#32676;&amp;#24050;&amp;#32463;&amp;#24314;&amp;#31435;&amp;#32676;&amp;#21495;161559791-&amp;#27426;&amp;#36814;&amp;#21152;&amp;#20837;&amp;#36827;&amp;#34892;&amp;#25216;&amp;#26415;&amp;#35752;&amp;#35770;"&gt;&lt;/a&gt;    &lt;a href="https://github.com/alibaba/otter/blob/master/README.md#%E6%B3%A8%E6%84%8Fcanalotter-qq%E8%AE%A8%E8%AE%BA%E7%BE%A4%E5%B7%B2%E7%BB%8F%E5%BB%BA%E7%AB%8B%E7%BE%A4%E5%8F%B7161559791-%E6%AC%A2%E8%BF%8E%E5%8A%A0%E5%85%A5%E8%BF%9B%E8%A1%8C%E6%8A%80%E6%9C%AF%E8%AE%A8%E8%AE%BA" name="user-content-%e6%b3%a8%e6%84%8fcanalotter-qq%e8%ae%a8%e8%ae%ba%e7%be%a4%e5%b7%b2%e7%bb%8f%e5%bb%ba%e7%ab%8b%e7%be%a4%e5%8f%b7161559791-%e6%ac%a2%e8%bf%8e%e5%8a%a0%e5%85%a5%e8%bf%9b%e8%a1%8c%e6%8a%80%e6%9c%af%e8%ae%a8%e8%ae%ba"&gt;&lt;/a&gt;注意：canal&amp;amp;otter QQ讨论群已经建立，群号：161559791 ，欢迎加入进行技术讨论。&lt;/h3&gt;  &lt;p&gt;1.qq交流群： 161559791&lt;/p&gt;  &lt;p&gt;2.邮件交流： jianghang115@gmail.com&lt;/p&gt;  &lt;p&gt;3.新浪微博： agapple0002&lt;/p&gt;  &lt;p&gt;4.报告issue：    &lt;a href="https://github.com/alibaba/otter/issues"&gt;issues&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60700-otter-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Sun, 21 Jun 2020 19:08:27 CST</pubDate>
    </item>
    <item>
      <title>MySQL 双活同步复制的四种方案_咸鱼的梦想专栏-CSDN博客_mysql双机同步复制</title>
      <link>https://itindex.net/detail/60686-mysql-%E5%8F%8C%E6%B4%BB-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;    &lt;p&gt;对于数据实时同步，其核心是需要基于日志来实现，是可以实现准实时的数据同步，基于日志实现不会要求数据库本身在设计和实现中带来任何额外的约束。&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;h2&gt;      &lt;strong&gt;基于MySQL原生复制主主同步方案  &lt;/strong&gt;&lt;/h2&gt;    &lt;p&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2d0Z0dWSXk4N3VpYWRJczFpY3NEZGFDWXJsSThhVEg5bXo0d2xwcllnNmo0U3RGVlg0UWRSemljTFEvNjQw?x-oss-process=image/format,png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;这是常见的方案，一般来说，中小型规模的时候，采用这种架构是最省事的。&lt;/p&gt;    &lt;p&gt;两个节点可以采用简单的双主模式，并且使用专线连接，在master_A节点发生故障后，应用连接快速切换到master_B节点，反之也亦然。有几个需要注意的地方，脑裂的情况，两个节点写入相同数据而引发冲突，同时把两个节点的auto_increment_increment（自增步长）和auto_increment_offset（自增起始值）设成不同值。其目的是为了避免master节点意外宕机时，可能会有部分binlog未能及时复制到slave上被应用，从而会导致slave新写入数据的自增值和原先master上冲突了，因此一开始就使其错开；当然了，如果有合适的容错机制能解决主从自增ID冲突的话，也可以不这么做，使用更新的数据版本5.7+，可以利用多线程复制的方式可以很大程度降低复制延迟，同时，对复制延迟特别敏感的另一个备选方案，是semi-sync半同步复制，基本上无延迟，不过事务并发性能会有不小程度的损失，特别是在双向写的时候，需要综合评估再决定。&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dXdWJ0bm1xRTBGN1h2aWNjaWFBc3JDTVFyNU51cXNUU0swc0VMdE9DakdIWFhSZEFEY1cxYk9Gdy82NDA?x-oss-process=image/format,png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;基于Galera replication方案  &lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;Galera是Codership提供的多主数据同步复制机制，可以实现多个节点间的数据同步复制以及读写，并且可保障数据库的服务高可用及数据一致性，基于Galera的高可用方案主要有MariaDB Galera Cluster和Percona XtraDB Cluster（简称PXC）。&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dnZkh2THlNTXdYMWljTkQxa29acHhsb2lhOXZnVW9rTlVpYTRzVTVLUlZ5ZkhZbGhXVWJoU3FBcXcvNjQw?x-oss-process=image/format,png"&gt;&lt;/img&gt;      &lt;br /&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2d3YW00N3U3ZjM0ckdyY2libGhBZEZlekV0aWNsQTU2WXJ5bmRUOWliaWNQWjdzSnRqcjEzN3Zvam5RLzY0MA?x-oss-process=image/format,png"&gt;&lt;/img&gt;      &lt;br /&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dWNW5oZmtXSlh1TWljaktHVmhKeDBwaWNGSmUyZ0ZSWHN6YnNNQ0RWY2Z0emMzdmliUHJqaWJmaGNRLzY0MA?x-oss-process=image/format,png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;目前PXC用的会比较多一些，数据严格一致性，尤其适合电商类应用，不过PXC也是有其局限性的，如果并发事务量很大的话，建议采用InfiniBand网络，降低网络延迟，因为PXC存在写扩大以及短板效应，并发效率会有较大损失，类似semi-sync半同步复制，Gelera实际只能用三个节点，网络抖动造成的性能和稳定性习惯性问题&lt;/p&gt;    &lt;h2&gt; &lt;/h2&gt;    &lt;h2&gt;      &lt;strong&gt;基于Group Replication方案  &lt;/strong&gt;&lt;/h2&gt;    &lt;p&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X3BuZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dldjFaTmJ3V25LZnNyaWNrb1N5RlZyenkzTGoxTkduUEVUOG9hOVNLaWFpY2VldjBkdDBDaWNzMmxRLzY0MA?x-oss-process=image/format,png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;通过Paxos协议提供数据库集群节点数据强一致保证，MGR准确来说是MySQL官方推出的高可用解决方案，基于原生复制技术，并以插件的方式提供，并且集群间所有节点可写入，解决了单个集群的写入性能，所有节点都能读写，解决网络分区导致的脑裂问题，提升复制数据的可靠性，不过现实还是有些残酷，目前尝鲜的并不是很多，同时仅支持InnoDB表，并且每张表一定要有一个主键，用于做write set的冲突检测，必须打开GTID特性，二进制日志格式必须设置为ROW，用于选主与write set&lt;/p&gt;    &lt;p&gt;COMMIT可能会导致失败，类似于快照事务隔离级别的失败场景，目前一个MGR集群最多支持9个节点，不支持外键于save point特性，无法做全局间的约束检测与部分部分回滚，二进制日志不支持binlog event checksum&lt;/p&gt;    &lt;h2&gt; &lt;/h2&gt;    &lt;h2&gt;      &lt;strong&gt;基于canal方案&lt;/strong&gt;&lt;/h2&gt;    &lt;p&gt;对于数据库的实时同步，阿里巴巴专门有一个开源项目，即otter来实现分布式数据库的同步复制，其核心思想仍然是通过获取数据库的增量数据日志，来进行准实时的同步复制。因此otter本身又依赖于另外一个开源项目即canal，该项目重点则是获取增量数据库同步日志信息。&lt;/p&gt;    &lt;p&gt;当前otter的重点是实现mysql间的数据库同步复制，基本即利用的类似技术来实现两个mysql数据库间的双向同步数据库复制。要注意这个双向本身指既可以A-&amp;gt;B，也可以从B-&amp;gt;A，在某个时间节点本身是单向的。&lt;/p&gt;    &lt;p&gt;主从复制分成三步：      &lt;br /&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dYRDNJMUZ4b29FZ0xkN0c0SWxnb0twVUN6ZVpLbTRCVGJibnEzNXVBMjB1REp2aWNXQVNtNXpRLzY0MA?x-oss-process=image/format,png"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;master将改变记录到二进制日志(binary log)中（这些记录叫做二进制日志事件，binary log events，可以通过show binlog events进行查看）；&lt;/p&gt;    &lt;p&gt;slave将master的binary log events拷贝到它的中继日志(relay log)；&lt;/p&gt;    &lt;p&gt;slave重做中继日志中的事件，将改变反映它自己的数据。&lt;/p&gt;    &lt;p&gt;canal原理相对比较简单：      &lt;br /&gt;      &lt;img alt="" src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2pwZy9pY2RCQ3BjRVRvSTlidVpTMUp0Qkl5aHlWc3l6RHIzZ2dXeVVHa1VZSWtnZnhNc0FKNGlhbHJwaWF2WDlDOHJwNmZhUG5uR3pDckJ3SG05aWNHR2pZdXdoQ0EvNjQw?x-oss-process=image/format,png"&gt;&lt;/img&gt;      &lt;br /&gt;canal模拟mysql slave的交互协议，伪装自己为mysql slave，向mysql master发送dump协议&lt;/p&gt;    &lt;p&gt;mysql master收到dump请求，开始推送binary log给slave(也就是canal)      &lt;br /&gt;canal解析binary log对象(原始为byte流)&lt;/p&gt;    &lt;p&gt;更多参考 https://github.com/alibaba/canal&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60686-mysql-%E5%8F%8C%E6%B4%BB-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Fri, 19 Jun 2020 16:17:07 CST</pubDate>
    </item>
    <item>
      <title>12c ADG的同步和异步灾备方案</title>
      <link>https://itindex.net/detail/60683-12c-adg-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;div&gt;  &lt;div&gt;   &lt;a href="https://blogs.oracle.com/author/jian-zhang"&gt;Jian Zhang&lt;/a&gt;   &lt;br /&gt;&lt;/div&gt;&lt;/div&gt; &lt;div&gt;  &lt;div&gt;概述&lt;/div&gt;  &lt;div&gt;================&lt;/div&gt;  &lt;div&gt;Active Data Guard Far Sync是Oracle 12c的新功能（也称为Far Sync Standby），Far Sync功能的实现是通过在距离主库(Primary Database)相对较近的地点配置Far Sync实例，主库(Primary Database) 同步(synchronous)传输redo到Far Sync实例，然后Far Sync实例再将redo异步(asynchronous)传输到终端备库(Standby Database)。这样既可以保证零数据丢失又可以降低主库压力。Far Sync实例只有密码文件，init参数文件和控制文件，而没有数据文件。&lt;/div&gt;  &lt;div&gt;如果redo 传输采用Maximum Availability模式，我们可以在距离生产中心(Primary Database)相对较近的地点配置Far Sync实例，主库(Primary Database)同步(synchronous)传输redo到Far Sync实例，保证零数据丢失（zero data loss），同时主库和Far Sync距离较近，网络延时很小，因此对主库性能影响很小。然后Far Sync实例再将redo异步(asynchronous)发送到终端备库(Standby Database)。&lt;/div&gt;  &lt;div&gt;如果redo 传输采用Maximum Performance模式，我们可以在距离生产中心(Primary Database)相对较近的地点配置Far Sync实例，主库(Primary Database) 异步传输redo到Far Sync实例，然后Far Sync实例再负责传输redo到其他多个终端备库(Standby Database)。这样可以减少主库向多个终端备库(Standby Database)传输redo的压力（offload）。&lt;/div&gt;  &lt;div&gt;Far Sync配置对于Data Guard 角色转换(role transitions)是透明的，即switchover/failover命令方式与12c之前相同。&lt;/div&gt;  &lt;div&gt;考虑到可能发生Data Guard 角色转换，即switchover/failover，可以在距离备库较近的地方也配置Far Sync实例，这个Far Sync实例只有在当前的备库切换为主库后才启用。&lt;/div&gt;  &lt;div&gt;考虑到Far Sync实例的单点故障，可以在距离主库交近的地点配置2个Far Sync实例，起到备用的作用。&lt;/div&gt;  &lt;div&gt;本文重点是测试Far Sync安装配置。&lt;/div&gt;  &lt;div&gt;Far Sync示意图&lt;/div&gt;  &lt;div&gt;================   &lt;img src="https://cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/Image/89306778448aed21ab0c1cdbac9a1070/farsync2.png"&gt;&lt;/img&gt;&lt;/div&gt;  &lt;div&gt;创建配置Far Sync&lt;/div&gt;  &lt;div&gt;================&lt;/div&gt;  &lt;div&gt;1. 创建Data Guard，方法与11.2相同，详细过程参考《   &lt;a href="https://blogs.oracle.com/Database4CN/entry/11g_%E6%96%B0%E7%89%B9%E6%80%A7_active_database_duplication1"&gt;Active Database Duplication for A standby database&lt;/a&gt;》&lt;/div&gt;  &lt;div&gt;2. 创建配置Far Sync实例，Far Sync实例只有密码文件，init参数文件和控制文件，而没有数据文件。&lt;/div&gt;  &lt;div&gt;创建Far Sync实例的控制文件，在主库执行：&lt;/div&gt;  &lt;div&gt;SQL&amp;gt; ALTER DATABASE CREATE FAR SYNC INSTANCE CONTROLFILE AS &amp;apos;/tmp/controlfs01.ctl&amp;apos;;&lt;/div&gt;  &lt;div&gt;3. 设置主库redo同步传输到Far Sync实例，修改主库LOG_ARCHIVE_DEST_2参数：&lt;/div&gt;  &lt;div&gt;LOG_ARCHIVE_DEST_2=&amp;apos;SERVICE=dg12cfs SYNC AFFIRM MAX_FAILURE=1 ALTERNATE=LOG_ARCHIVE_DEST_3&lt;/div&gt;  &lt;div&gt;VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=dg12cfs&amp;apos;&lt;/div&gt;  &lt;div&gt;4. 设置Far Sync实例异步传输redo到备库，修改Far Sync实例LOG_ARCHIVE_DEST_2参数：&lt;/div&gt;  &lt;div&gt;LOG_ARCHIVE_DEST_2=&amp;apos;SERVICE=dg12cs ASYNC&lt;/div&gt;  &lt;div&gt;VALID_FOR=(STANDBY_LOGFILES,STANDBY_ROLE) DB_UNIQUE_NAME=dg12cs&amp;apos;&lt;/div&gt;  &lt;div&gt;5. 为了解决Far Sync实例的单点故障，可以在距离主库较近的地点配置2个Far Sync实例。&lt;/div&gt;  &lt;div&gt;6. 创建完成后确认：&lt;/div&gt;  &lt;div&gt;SQL&amp;gt; select * from  V$DATAGUARD_CONFIG;&lt;/div&gt;  &lt;div&gt;DB_UNIQUE_NAME       PARENT_DBUN       DEST_ROLE         CURRENT_SCN     CON_ID&lt;/div&gt;  &lt;div&gt;------------------------------ ------------------------------     ----------------- ----------- ----------&lt;/div&gt;  &lt;div&gt;dg12cfs                        dg12cp          FAR SYNC INSTANCE      682995          0&lt;/div&gt;  &lt;div&gt;dg12cs                         dg12cfs         PHYSICAL STANDBY       682995          0&lt;/div&gt;  &lt;div&gt;dg12cp                        NONE             PRIMARY DATABASE      683138          0&lt;/div&gt;  &lt;div&gt;详细配置过程和测试案例请参考附件：   &lt;a href="https://cdn.app.compendium.com/uploads/user/e7c690e8-6ff9-102a-ac6d-e4aebca50425/f4a5b21d-66fa-4885-92bf-c4e81c06d916/File/18d72af879c57d3a4bf6cccd5817cab4/12c_active_data_guard_far_sync_v1.pdf"&gt;Oracle_12c_Active_Data_Guard_Far_Sync_v1.pdf&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
     
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60683-12c-adg-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Thu, 18 Jun 2020 11:18:34 CST</pubDate>
    </item>
    <item>
      <title>Oracle GoldenGate系统之----双向同步数据表_ITPUB博客</title>
      <link>https://itindex.net/detail/60642-oracle-goldengate-%E7%B3%BB%E7%BB%9F</link>
      <description>&lt;div&gt;双向同步与单向同步类似，但需要着重注意两个问题：防止数据循环和防止数据冲突。    &lt;br /&gt;1、防止数据循环    &lt;br /&gt;在EXTRACT进程中忽略REPLICAT的事务，一般排除提取用户的操作，并且与需要同步的用户分开    &lt;br /&gt;如USERID SCOTT,PASSWORD TIGER    &lt;br /&gt;TRANLOGOPTIONS EXCLUDEUSER SCOTT    &lt;br /&gt;2、防止数据冲突    &lt;br /&gt;一般要从应用层面解决，避免操作相同的数据。    &lt;br /&gt;    &lt;br /&gt;以下是配置步骤和参数。假设是A、B两个库之间的同步配置。    &lt;br /&gt;从A到B    &lt;br /&gt;-- 管理进程配置    &lt;br /&gt;GGSCI (dgrac) 1&amp;gt; edit param mgr    &lt;p&gt;PORT 7801      &lt;br /&gt;DYNAMICPORTLIST 7802-7820      &lt;br /&gt;AUTOSTART ER *      &lt;br /&gt;AUTORESTART ER *,RETRIES 3,WAITMINUTES 2,RESETMINUTES 60      &lt;br /&gt;STARTUPVALIDATIONDELAY 5      &lt;br /&gt;PURGEOLDEXTRACTS ./dirdat/*,USECHECKPOINTS,MINKEEPDAYS 7&lt;/p&gt;    &lt;p&gt;--EXTRACT配置      &lt;br /&gt;GGSCI (dgrac) 2&amp;gt; add extract exts1,tranlog,begin now      &lt;br /&gt; EXTRACT added.&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 3&amp;gt; edit param exts1      &lt;br /&gt; extract exts1      &lt;br /&gt; setenv(NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)      &lt;br /&gt; userid      &lt;a href="mailto:scott@wailon,password"&gt;scott@wailon,password&lt;/a&gt;tiger      &lt;br /&gt; tranlogoptions excludeuser scott      &lt;br /&gt; exttrail /u01/app/ogg/dirdat/l1      &lt;br /&gt; table lrj.s1;&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 4&amp;gt; add exttrail /u01/app/ogg/dirdat/l1,extract exts1      &lt;br /&gt;EXTTRAIL added.&lt;/p&gt;    &lt;p&gt;-- PUMP配置      &lt;br /&gt;GGSCI (dgrac) 5&amp;gt; add extract pumps1 ,exttrailsource /u01/app/ogg/dirdat/l1,begin now      &lt;br /&gt;EXTRACT added.&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 6&amp;gt; edit param pumps1      &lt;br /&gt;extract pumps1      &lt;br /&gt;setenv(NLS_LANG=&amp;quot;AMERICAN_AMERICA.ZHS16GBK&amp;quot;)      &lt;br /&gt;userid scott      &lt;a href="mailto:lrj@ogg,password"&gt;@wailon,password&lt;/a&gt; tiger      &lt;br /&gt;rmthost 192.168.56.101,mgrport 7801      &lt;br /&gt;rmttrail /u01/app/ogg/dirdat/s1      &lt;br /&gt;PASSTHRU      &lt;br /&gt;table lrj.s1;&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 7&amp;gt; add rmttrail /u01/app/ogg/dirdat/s1,extract pumps1      &lt;br /&gt;RMTTRAIL added.&lt;/p&gt;    &lt;p&gt;--REPLICAT配置&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 8&amp;gt; edit param reps1      &lt;br /&gt;replicat reps1      &lt;br /&gt;setenv(NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)      &lt;br /&gt;userid scott      &lt;a href="mailto:lrj@ogg"&gt;@wailon&lt;/a&gt;, password tiger      &lt;br /&gt;assumetargetdefs      &lt;br /&gt;reperror default,discard      &lt;br /&gt;discardfile /u01/app/ogg/dirout/reps1.dsc,append,megabytes 100      &lt;br /&gt;map lrj.s1,target lrj.s1,&amp;amp;      &lt;br /&gt;colmap(usedefaults,&amp;quot;ENAME&amp;quot; = &amp;quot;USERNAME&amp;quot;),&amp;amp;      &lt;br /&gt;-- 解决冲突，更新记录不存在时插入，但只插入更新的列，其他列为空，还是要人工处理      &lt;br /&gt;RESOLVECONFLICT (UPDATEROWMISSING, (DEFAULT, OVERWRITE));      &lt;br /&gt;HANDLECOLLISIONS&lt;/p&gt;    &lt;p&gt;-- 启动所有进程      &lt;br /&gt;GGSCI (dgrac) 9&amp;gt; start mgr      &lt;br /&gt;MGR is already running.&lt;/p&gt;    &lt;p&gt;GGSCI (dgrac) 10&amp;gt; info all&lt;/p&gt;    &lt;p&gt;Program     Status      Group       Lag at Chkpt  Time Since Chkpt&lt;/p&gt;    &lt;p&gt;MANAGER     RUNNING              &lt;br /&gt;EXTRACT     RUNNING     EXTS1       00:00:00      00:00:09         &lt;br /&gt;EXTRACT     RUNNING     PUMPS1      00:00:00      00:00:07         &lt;br /&gt;REPLICAT    RUNNING     REPS1       00:00:00      00:00:05         &lt;br /&gt;       &lt;br /&gt;从B到A&lt;/p&gt;    &lt;p&gt;--管理进程配置      &lt;br /&gt;GGSCI (dg) 1&amp;gt; edit param mgr&lt;/p&gt;    &lt;p&gt;PORT 7801      &lt;br /&gt;DYNAMICPORTLIST 7802-7820      &lt;br /&gt;AUTOSTART ER *      &lt;br /&gt;AUTORESTART ER *,RETRIES 3,WAITMINUTES 2,RESETMINUTES 60      &lt;br /&gt;STARTUPVALIDATIONDELAY 5      &lt;br /&gt;PURGEOLDEXTRACTS ./dirdat/*,USECHECKPOINTS,MINKEEPDAYS 7&lt;/p&gt;    &lt;p&gt;-- EXTRACT配置      &lt;br /&gt;GGSCI (dg) 2&amp;gt; add extract exts1,tranlog,begin now      &lt;br /&gt; EXTRACT added.&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 3&amp;gt; edit param exts1      &lt;br /&gt; extract exts1      &lt;br /&gt; setenv(NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)      &lt;br /&gt; userid      &lt;a href="mailto:scott@ogg,password"&gt;scott@ogg,password&lt;/a&gt;tiger      &lt;br /&gt; tranlogoptions excludeuser scott      &lt;br /&gt; exttrail /u01/app/ogg/dirdat/l1      &lt;br /&gt; table lrj.s1;&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 4&amp;gt; add exttrail /u01/app/ogg/dirdat/l1,extract exts1      &lt;br /&gt;EXTTRAIL added.&lt;/p&gt;    &lt;p&gt;--PUMP配置      &lt;br /&gt;GGSCI (dg) 5&amp;gt; add extract pumps1 ,exttrailsource /u01/app/ogg/dirdat/l1,begin now      &lt;br /&gt;EXTRACT added.&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 6&amp;gt; edit param pumps1      &lt;br /&gt;extract pumps1      &lt;br /&gt;setenv(NLS_LANG=&amp;quot;AMERICAN_AMERICA.ZHS16GBK&amp;quot;)      &lt;br /&gt;userid scott@      &lt;a href="mailto:lrj@ogg,password"&gt;ogg,password&lt;/a&gt; tiger      &lt;br /&gt;rmthost 192.168.56.131,mgrport 7801      &lt;br /&gt;rmttrail /u01/app/ogg/dirdat/s1      &lt;br /&gt;PASSTHRU      &lt;br /&gt;table lrj.s1;&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 7&amp;gt; add rmttrail /u01/app/ogg/dirdat/s1,extract pumps1      &lt;br /&gt;RMTTRAIL added.&lt;/p&gt;    &lt;p&gt;-- REPLICAT配置&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 8&amp;gt; edit param reps1      &lt;br /&gt;replicat reps1      &lt;br /&gt;setenv(NLS_LANG=AMERICAN_AMERICA.ZHS16GBK)      &lt;br /&gt;userid scott      &lt;a href="mailto:lrj@ogg"&gt;@ogg&lt;/a&gt;, password tiger      &lt;br /&gt;assumetargetdefs      &lt;br /&gt;reperror default,discard      &lt;br /&gt;discardfile /u01/app/ogg/dirout/reps1.dsc,append,megabytes 100      &lt;br /&gt;map lrj.s1,target lrj.s1,&amp;amp;      &lt;br /&gt;colmap(usedefaults,&amp;quot;ENAME&amp;quot; = &amp;quot;USERNAME&amp;quot;),&amp;amp;      &lt;br /&gt;RESOLVECONFLICT (UPDATEROWMISSING, (DEFAULT, OVERWRITE));      &lt;br /&gt;HANDLECOLLISIONS&lt;/p&gt;    &lt;p&gt;-- 启动所有进程      &lt;br /&gt;GGSCI (dg) 9&amp;gt; start mgr      &lt;br /&gt;MGR is already running.&lt;/p&gt;    &lt;p&gt;GGSCI (dg) 10&amp;gt; info all&lt;/p&gt;    &lt;p&gt;Program     Status      Group       Lag at Chkpt  Time Since Chkpt&lt;/p&gt;    &lt;p&gt;MANAGER     RUNNING              &lt;br /&gt;EXTRACT     RUNNING     EXTS1       00:00:00      00:00:09         &lt;br /&gt;EXTRACT     RUNNING     PUMPS1      00:00:00      00:00:07         &lt;br /&gt;REPLICAT    RUNNING     REPS1       00:00:00      00:00:05  &lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;数据测试      &lt;br /&gt;B端插入：      &lt;br /&gt;02:44:09      &lt;a href="mailto:LRJ@ogg&gt;select"&gt;LRJ@ogg&amp;gt;select&lt;/a&gt;* from s1      &lt;br /&gt;02:44:12   2  /&lt;/p&gt;    &lt;p&gt;     EMPNO USERNAME   JOB              MGR HIREDATE            SAL       COMM     DEPTNO      &lt;br /&gt;---------- ---------- --------- ---------- ------------ ---------- ---------- ----------      &lt;br /&gt;      7369 SMITH      CLERK           7902 17-DEC-80          1000        100         20      &lt;br /&gt;      7499 ALLEN      SALESMAN        7698 20-FEB-81          1800        400         30      &lt;br /&gt;      7521 WARD       SALESMAN        7698 22-FEB-81          1450        600         30      &lt;br /&gt;      7566 JONES      MANAGER         7839 02-APR-81          3175        100         20      &lt;br /&gt;      7654 MARTIN     SALESMAN        7698 28-SEP-81          1450       1500         30      &lt;br /&gt;      7698 BLAKE      MANAGER         7839 01-MAY-81          3050        100         30      &lt;br /&gt;      7782 CLARK      MANAGER         7839 09-JUN-81          2650        100         10      &lt;br /&gt;      7788 SCOTT      ANALYST         7566 19-APR-87          3200        100         20      &lt;br /&gt;      7839 KING       PRESIDENT            17-NOV-81          5200        100         10      &lt;br /&gt;      7844 TURNER     SALESMAN        7698 08-SEP-81          1700        100         30      &lt;br /&gt;      7876 ADAMS      CLERK           7788 23-MAY-87          1300        100         20      &lt;br /&gt;      7900 JAMES      CLERK           7698 03-DEC-81          1150        100         30      &lt;br /&gt;      7902 FORD       ANALYST         7566 03-DEC-81          3200        100         20      &lt;br /&gt;      7934 MILLER     CLERK           7782 23-JAN-82          1500        100         10      &lt;br /&gt;      2000 yojan&lt;/p&gt;    &lt;p&gt;15 rows selected.&lt;/p&gt;    &lt;p&gt;02:45:41      &lt;a href="mailto:LRJ@ogg&gt;insert"&gt;LRJ@ogg&amp;gt;insert&lt;/a&gt;into s1(empno,username,sal) values(4000,&amp;apos;GZITECH&amp;apos;,2000);&lt;/p&gt;    &lt;p&gt;1 row created.&lt;/p&gt;    &lt;p&gt;02:45:46      &lt;a href="mailto:LRJ@ogg&gt;commit"&gt;LRJ@ogg&amp;gt;commit&lt;/a&gt;;&lt;/p&gt;    &lt;p&gt;Commit complete.&lt;/p&gt;    &lt;p&gt;A端查看：      &lt;br /&gt;02:45:50      &lt;a href="mailto:LRJ@ogg&gt;select"&gt;LRJ@ogg&amp;gt;select&lt;/a&gt;* from s1;&lt;/p&gt;    &lt;p&gt;     EMPNO USERNAME   JOB              MGR HIREDATE            SAL       COMM     DEPTNO      &lt;br /&gt;---------- ---------- --------- ---------- ------------ ---------- ---------- ----------      &lt;br /&gt;      7369 SMITH      CLERK           7902 17-DEC-80          1000        100         20      &lt;br /&gt;      7499 ALLEN      SALESMAN        7698 20-FEB-81          1800        400         30      &lt;br /&gt;      7521 WARD       SALESMAN        7698 22-FEB-81          1450        600         30      &lt;br /&gt;      7566 JONES      MANAGER         7839 02-APR-81          3175        100         20      &lt;br /&gt;      7654 MARTIN     SALESMAN        7698 28-SEP-81          1450       1500         30      &lt;br /&gt;      7698 BLAKE      MANAGER         7839 01-MAY-81          3050        100         30      &lt;br /&gt;      7782 CLARK      MANAGER         7839 09-JUN-81          2650        100         10      &lt;br /&gt;      7788 SCOTT      ANALYST         7566 19-APR-87          3200        100         20      &lt;br /&gt;      7839 KING       PRESIDENT            17-NOV-81          5200        100         10      &lt;br /&gt;      7844 TURNER     SALESMAN        7698 08-SEP-81          1700        100         30      &lt;br /&gt;      7876 ADAMS      CLERK           7788 23-MAY-87          1300        100         20      &lt;br /&gt;      7900 JAMES      CLERK           7698 03-DEC-81          1150        100         30      &lt;br /&gt;      7902 FORD       ANALYST         7566 03-DEC-81          3200        100         20      &lt;br /&gt;      7934 MILLER     CLERK           7782 23-JAN-82          1500        100         10      &lt;br /&gt;      2000 yojan      &lt;br /&gt;      4000 GZITECH                                            2000&lt;/p&gt;    &lt;p&gt;16 rows selected.&lt;/p&gt;    &lt;p&gt;A端插入：      &lt;br /&gt;09:34:18      &lt;a href="mailto:LRJ@wailon&gt;insert"&gt;LRJ@wailon&amp;gt;insert&lt;/a&gt;into s1(empno,ename,job) values(3000,&amp;apos;WAILON&amp;apos;,&amp;apos;CLERK&amp;apos;);&lt;/p&gt;    &lt;p&gt;1 row created.&lt;/p&gt;    &lt;p&gt;09:34:40      &lt;a href="mailto:LRJ@wailon&gt;commit"&gt;LRJ@wailon&amp;gt;commit&lt;/a&gt;;&lt;/p&gt;    &lt;p&gt;Commit complete.&lt;/p&gt;    &lt;p&gt;B端查看：      &lt;br /&gt;09:34:42      &lt;a href="mailto:LRJ@wailon&gt;select"&gt;LRJ@wailon&amp;gt;select&lt;/a&gt;* from s1;&lt;/p&gt;    &lt;p&gt;     EMPNO USERNAME   JOB              MGR HIREDATE            SAL       COMM     DEPTNO      &lt;br /&gt;---------- ---------- --------- ---------- ------------ ---------- ---------- ----------      &lt;br /&gt;      7369 SMITH      CLERK           7902 17-DEC-80          1000        100         20      &lt;br /&gt;      7499 ALLEN      SALESMAN        7698 20-FEB-81          1800        400         30      &lt;br /&gt;      7521 WARD       SALESMAN        7698 22-FEB-81          1450        600         30      &lt;br /&gt;      7566 JONES      MANAGER         7839 02-APR-81          3175        100         20      &lt;br /&gt;      7654 MARTIN     SALESMAN        7698 28-SEP-81          1450       1500         30      &lt;br /&gt;      7698 BLAKE      MANAGER         7839 01-MAY-81          3050        100         30      &lt;br /&gt;      7782 CLARK      MANAGER         7839 09-JUN-81          2650        100         10      &lt;br /&gt;      7788 SCOTT      ANALYST         7566 19-APR-87          3200        100         20      &lt;br /&gt;      7839 KING       PRESIDENT            17-NOV-81          5200        100         10      &lt;br /&gt;      7844 TURNER     SALESMAN        7698 08-SEP-81          1700        100         30      &lt;br /&gt;      7876 ADAMS      CLERK           7788 23-MAY-87          1300        100         20      &lt;br /&gt;      7900 JAMES      CLERK           7698 03-DEC-81          1150        100         30      &lt;br /&gt;      7902 FORD       ANALYST         7566 03-DEC-81          3200        100         20      &lt;br /&gt;      7934 MILLER     CLERK           7782 23-JAN-82          1500        100         10      &lt;br /&gt;      2000 yojan      &lt;br /&gt;      3000 WAILON     CLERK      &lt;br /&gt;      4000 GZITECH                                            2000&lt;/p&gt;    &lt;p&gt;17 rows selected.&lt;/p&gt;    &lt;p&gt; &lt;/p&gt;    &lt;p&gt;&lt;/p&gt;    &lt;p&gt;来自 “ ITPUB博客 ” ，链接：http://blog.itpub.net/429786/viewspace-1062584/，如需转载，请注明出处，否则将追究法律责任。&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60642-oracle-goldengate-%E7%B3%BB%E7%BB%9F</guid>
      <pubDate>Fri, 05 Jun 2020 16:57:59 CST</pubDate>
    </item>
    <item>
      <title>MySQL 同步复制及高可用方案总结</title>
      <link>https://itindex.net/detail/60490-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;h4&gt;  &lt;strong&gt;1.前言&lt;/strong&gt;&lt;/h4&gt;
 &lt;p&gt;mysql作为应用程序的数据存储服务，要实现mysql数据库的高可用。必然要使用的技术就是数据库的复制，如果主节点出现故障可以手动的切换应用到从节点，这点相信运维同学都是知道，并且可以实现的。但是这种情况只是手动的切换，对可用性有要求的业务需要分别实现主库和从库的高可用，保障在数据库出现down机的情况下，可以自动实现数据库的故障转移，保障应用的可用性和用户体验。&lt;/p&gt;
 &lt;p&gt;本文将会对一些常用的数据库高可用方案进行介绍，根据你不同的场景，选择合适的高可用方案即可。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;2.MMM高可用方案&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;  &lt;strong&gt;2.1.Mysql-MMM介绍&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MMM(Master-Master replication managerfor Mysql，Mysql主主复制管理器)是一套灵活的脚本程序，基于perl实现，用来对mysql replication进行监控和故障迁移，并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的)。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.2.组件&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_mond：&lt;/strong&gt;监控进程，负责所有的监控工作，决定和处理所有节点角色活动。此脚本需要在监管机上运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_agentd：&lt;/strong&gt;运行在每个mysql服务器上的代理进程，完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;mmm_control：&lt;/strong&gt;一个简单的脚本，提供管理mmm_mond进程的命令。&lt;/p&gt;
 &lt;p&gt;mysql-mmm的监管端会提供多个虚拟IP（VIP），包括一个可写VIP，多个可读VIP，通过监管的管理，这些IP会绑定在可用mysql之上，当某一台mysql宕机时，监管会将VIP迁移至其他mysql。&lt;/p&gt;
 &lt;p&gt;在整个监管过程中，需要在mysql中添加相关授权用户，以便让mysql可以支持监理机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户，如果想使用mmm的备份工具则还要添加一个mmm_tools用户。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.3.架构图&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;正常工作时：  &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313467" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主节点故障时：  &lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313468" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.4.MMM优点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）高可用性，扩展性好，出现故障自动转移，对于主主同步，在同一时间只提供一台数据库写操作，保证数据的一致性。&lt;/p&gt;
 &lt;p&gt;（2）配置简单，容易操作。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;2.5.MMM缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源&lt;/p&gt;
 &lt;p&gt;（2）需要多个虚拟IP&lt;/p&gt;
 &lt;p&gt;（3）agent可能意外终止，引起裂脑。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;3.MHA介绍&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;MHA（Master High Availability）目前在MySQL高可用方面是一个相对成熟的解决方案，它由日本DeNA公司youshimaton（现就职于Facebook公司）开发，是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中，MHA能做到在0~30秒之内自动完成数据库的故障切换操作，并且在进行故障切换的过程中，MHA能在最大程度上保证数据的一致性，以达到真正意义上的高可用。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.1.MHA架构介绍&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;该软件由两部分组成：MHA Manager（管理节点）和MHA Node（数据节点）。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群，也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上，MHA Manager会定时探测集群中的master节点，当master出现故障时，它可以自动将最新数据的slave提升为新的master，然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。&lt;/p&gt;
 &lt;p&gt;在MHA自动故障切换过程中，MHA试图从宕机的主服务器上保存二进制日志，最大程度的保证数据的不丢失(配合mysql半同步复制效果更佳)，但这并不总是可行的。例如，如果主服务器硬件故障或无法通过ssh访问，MHA没法保存二进制日志，只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制，可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志，MHA可以将最新的二进制日志应用于其他所有的slave服务器上，因此可以保证所有节点的数据一致性。&lt;/p&gt;
 &lt;p&gt;注意：目前MHA主要支持一主多从的架构，要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器，一主二从，即一台充当master，一台充当备用master，另外一台充当从库，因为至少需要三台服务器，出于机器成本的考虑，淘宝也在该基础上进行了改造，目前淘宝TMHA已经支持一主一从。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.2.MHA架构图&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;正常工作时架构图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313469" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主库down机时架构：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313466" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.3.故障转移过程&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）从宕机崩溃的master保存二进制日志事件（binlog events）;&lt;/p&gt;
 &lt;p&gt;（2）识别含有最新更新的slave；&lt;/p&gt;
 &lt;p&gt;（3）应用差异的中继日志（relay log）到其他的slave；&lt;/p&gt;
 &lt;p&gt;（4）应用从master保存的二进制日志事件（binlog events）；&lt;/p&gt;
 &lt;p&gt;（5）提升一个slave为新的master；&lt;/p&gt;
 &lt;p&gt;（6）使其他的slave连接新的master进行复制；&lt;/p&gt;
 &lt;p&gt;（7）在新的master启动vip地址，保证前端请求可以发送到新的master。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.4.MHA优点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）不需要备份服务器&lt;/p&gt;
 &lt;p&gt;（2）不改变现有环境&lt;/p&gt;
 &lt;p&gt;（3）操作非常简单&lt;/p&gt;
 &lt;p&gt;（4）可以进行日志的差异修复&lt;/p&gt;
 &lt;p&gt;（5）可以将任意slave提升为master&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;3.5.MHA缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）需要全部节点做ssh秘钥&lt;/p&gt;
 &lt;p&gt;（2）MHA出现故障后配置文件会被修改，如果再次故障转移需要重新修改配置文件。&lt;/p&gt;
 &lt;p&gt;（3）自带的脚本还需要进一步补充完善，且用perl开发，二次开发困难。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;4.DRBD+（heartbeat,corosync）&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;4.1.  &lt;strong&gt;方案简介&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;本方案采用Heartbeat或者corosync双机热备软件来保证数据库的高稳定性和连续性，数据的一致性由DRBD这个工具来保证（如果可以尽量放到分布式存储上面）。默认情况下只有一台mysql在工作，当主mysql服务器出现问题后，系统将自动切换到备机上继续提供服务，当主数据库修复完毕，又将服务切回继续由主mysql提供服务。&lt;/p&gt;
 &lt;h6&gt;4.2.  &lt;strong&gt;组件&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;Heartbeat,corosync作为心跳检测机制，监控primary节点的状态。当主节点宕掉之后，迅速提升secondary节点为新的主节点，并切换IP；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;drbd&lt;/strong&gt;负责数据同步&lt;/p&gt;
 &lt;h6&gt;4.3.  &lt;strong&gt;架构图&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313465" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h6&gt;4.4.  &lt;strong&gt;数据同步过程&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;mysql进行刷盘时，会通过不同的sync方式，最终将数据写入disk；&lt;/p&gt;
 &lt;p&gt;drbd收到刷盘成功的信息后，将对应的磁盘块位置，和变更动作，通过网络传递至secondary节点；&lt;/p&gt;
 &lt;p&gt;secondary的drbd接收到变更信息后，将这些信息落盘；&lt;/p&gt;
 &lt;h6&gt;4.5.  &lt;strong&gt;切换过程&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;前提：secondary节点的mysql服务不启动；&lt;/p&gt;
 &lt;p&gt;heartbeat检测到primary的mysql服务停止，则摘掉IP、umount掉数据盘、将primary切换为secondary；&lt;/p&gt;
 &lt;p&gt;在原来的secondary上，提升drbd同步为primary，挂载数据盘，启动mysql服务、绑定IP；&lt;/p&gt;
 &lt;p&gt;从库跟着IP和端口自动进行迁移；&lt;/p&gt;
 &lt;h6&gt;4.6.  &lt;strong&gt;方案优点&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;（1）历史悠久、安全性高、稳定性高、可用性高、出现故障自动切换。&lt;/p&gt;
 &lt;p&gt;（2）数据一致性强&lt;/p&gt;
 &lt;h6&gt;4.7.  &lt;strong&gt;方案缺点&lt;/strong&gt;
&lt;/h6&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源&lt;/p&gt;
 &lt;p&gt;（2）不方便扩展&lt;/p&gt;
 &lt;p&gt;（3）无论drbd还是headbetart，corosync都可能发生裂脑&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;5.Mysql route介绍&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h6&gt;  &lt;strong&gt;5.1.什么是mysql route&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router是处于应用client和dbserver之间的轻量级代理程序，它能检测，分析和转发查询到后端数据库实例，并把结果返回给client。是mysql-proxy的一个替代品。其架构图和功能如下。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000022313470" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;（1）Router实现读写分离，程序不是直接连接数据库IP，而是固定连接到mysql router。MySQL Router对前端应用是透明的。应用程序把MySQL Router当作是普通的mysql实例，把查询发给MySQL Router,而MySQL Router会把查询结果返回给前端的应用程序。&lt;/p&gt;
 &lt;p&gt;（2）从数据库服务器故障，业务可以正常运行。由MySQL Router来进行自动下线不可用服务器。程序配置不需要任何修改。&lt;/p&gt;
 &lt;p&gt;（3）主数据库故障，由MySQL Router来决定主从自动切换，业务可以正常访问。程序配置不需要做任何修改。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.2.读写分离原理&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router接受前端应用程序请求后，根据不同的端口来区分读写，把连接读写端口的所有查询发往主库，把连接只读端口的select查询以轮询方式发往多个从库，从而实现读写分离的目的。读写返回的结果会交给MySQL Router,由MySQL Router返回给客户端的应用程序。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.3.Mysql router用途&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;MySQL Router的主要用途是读写分离，主主故障自动切换，负载均衡，连接池等。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.4.Mysql router主主故障自动切换的坑&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;Mysql router主主故障切换功能经过测试没有问题，但是有一个比较大的坑需要注意，主库发生切换之后，从库的连接的master服务器地址不会发生改变，需要自己写脚本进行判断。&lt;/p&gt;
 &lt;h6&gt;5.5.优点&lt;/h6&gt;
 &lt;p&gt;（1）基于DAL层实现mysql的高可用。&lt;/p&gt;
 &lt;p&gt;（2）可以同时实现主主故障切换和读写分离。&lt;/p&gt;
 &lt;p&gt;（3）插件式架构允许用户进行额外的功能扩展。&lt;/p&gt;
 &lt;h6&gt;  &lt;strong&gt;5.6.缺点&lt;/strong&gt;&lt;/h6&gt;
 &lt;p&gt;（1）高可用功能需要进一步完善：存在主库切换之后，从库不会自动切换主库地址的坑。&lt;/p&gt;
 &lt;p&gt;（2）读写情况使用不同端口，需要修改应用程序。&lt;/p&gt;
 &lt;h4&gt;  &lt;strong&gt;6.mysql Cluster&lt;/strong&gt;&lt;/h4&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;p&gt;国内用的非常少，主要因为一下三点：&lt;/p&gt;
 &lt;p&gt;（1）需要更改存储引擎&lt;/p&gt;
 &lt;p&gt;（2）付费&lt;/p&gt;
 &lt;p&gt;（3）国内几乎没有使用案例&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;高可用，可用率达99.999%&lt;/p&gt;
 &lt;h4&gt;7.结束语&lt;/h4&gt;
 &lt;p&gt;上面的高可用方案，只是我自己比较熟悉的，而且也是应用比较多的。mysql毕竟发展了有20多年了，各种高可用方案还是很多的，其他的高可用方案各位钥匙有兴趣，可以自己研究。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql 数据库 运维 后端 程序员</category>
      <guid isPermaLink="true">https://itindex.net/detail/60490-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Thu, 09 Apr 2020 12:11:16 CST</pubDate>
    </item>
    <item>
      <title>彻底终结MySQL同步延迟问题 - 简书</title>
      <link>https://itindex.net/detail/60450-mysql-%E5%90%8C%E6%AD%A5-%E5%BB%B6%E8%BF%9F</link>
      <description>&lt;p&gt;作为一名DBA，在工作中会经常遇到一些MySQL主从同步延迟的问题，这些同步慢的问题，其实原因非常多，可能是因为主从的网络问题导致，可能是因为网络带宽问题导致，可能是因为大事务导致，也可能是因为单线程复制导致的延迟。最近遇到一个很典型的同步延迟问题，将分析过程写出来，希望对广大DBA在排查同步延迟问题有比较系统的方法论。&lt;/p&gt;  &lt;p&gt;首先交代一下背景（不交代背景和场景的问题分析都是耍流氓）&lt;/p&gt;  &lt;p&gt;最近有一组DB出现比较大的延迟，这组DB是专门用来存储监控数据，每分钟会使用load data的方式导入大量的数据。为了节省空间，将原来使用压缩表的innodb引擎转换成了TokuDB引擎，使用的版本和引擎如下：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;MySQL Version： 5.7&lt;/p&gt;    &lt;p&gt;Storage Engine： TokuDB&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;转换后，发现主从延迟逐渐增大，基本每天落后主机大概50个binlog左右，大概延迟7.5个小时左右的数据，主机每天大概产生160个binlog，binlog列表如下图所示：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;由于对该业务非常熟悉，因此很快就定位到造成主从同步延迟的原因，并很快就解决了延迟的问题。这里不直接说解决办法，而是想描述一套完整的解决主从延迟问题的思考方式，和大家一起来系统的做一些思考。带着问题去思考延迟的根本原因和解决办法。我想，这也许会更有意义。授人以鱼，不如授人以渔。接下来我们就一起开脑洞。&lt;/p&gt;  &lt;p&gt;首先，既然产生了主从延迟，就说明在从机上的消费速度赶不上主机binlog产生的速度。我们先来思考一下可能的原因，并根据现场的蛛丝马迹去验证猜想的正确性。其实所谓的问题排查，就是提出可能问题猜想，然后不断去证明的过程。不同的是，每个人的经验不同，排查的质量也不尽头相同，仅此而已。那就来从各个可能的方面开脑洞吧。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;网络&lt;/h4&gt;  &lt;p&gt;网络可能导致主从延迟的问题，比如主机或者从机的带宽打满、主从之间网络延迟很大，有可能会导致主上的binlog没有全量传输到从机，造成延迟。&lt;/p&gt;  &lt;p&gt;我的那组DB的IO线程已经将对应的binlog近乎实时的拉取到了从机DB上，基本排除网络导致的延迟。还可以结合网络质量相关监控来进一步确认是网络的问题。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;机器性能&lt;/h4&gt;  &lt;p&gt;从机使用了烂机器？之前有遇到过有的业务从机使用了很烂的机器，导致的主从延迟。比如主机使用SSD而从机还是使用的SATA。从机用烂机器的观念需要改改，随着DB自动切换的需求越来越高，尤其是我所在的金融行业，从机至少不要比主机配置差。&lt;/p&gt;  &lt;p&gt;从机高负载？有很多业务会在从机上做统计，把从机服务器搞成高负载，从而造成从机延迟很大的情况，这种使用top命令即可快速发现。&lt;/p&gt;  &lt;p&gt;从机磁盘有问题？磁盘、raid卡、调度策略有问题的情况下，有的时候会出现单个IO延迟很高的情况，比如raid卡电池充放电的时候，在没有设置强行write back的情况下得会将write back模式修改为write through。使用iostat命令查看DB数据盘的IO情况，是否是单个IO的执行时间很长，块大小和磁盘队列情况等，可以比较一下DB盘的IO调度规则以及块大小的设置等。使用iostat查看IO运行情况：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;从IO情况看也没什么问题，单个IO延迟很小，iops很低，写带宽也不大。调度规则（cat /sys/block/fioa/queue/scheduler）和块大小等和主机设置是一样的，排除磁盘的问题。&lt;/p&gt;  &lt;p&gt;从运行指标看，机器负载很低，机器性能也可以排除。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;大事务&lt;/h4&gt;  &lt;p&gt;是否是经常会有大事务？这个可能广大DBA们会遇到比较多，比如在RBR模式下，执行带有大量的delete操作，或者在MBR模式下删除的时候添加了不确定语句（类似limit），又或者一个表的alter操作等，都会导致延迟情况的发生。这种通过查看processlist相关信息以及使用mysqlbinlog查看binlog中的SQL就能快速进行确认。这个设想也被排除。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;锁&lt;/h4&gt;  &lt;p&gt;锁冲突问题也可能导致从机的SQL线程执行慢，比如从机上有一些select  ....  for update的SQL，或者使用了MyISAM引擎等。此类问题，可以通过抓去processlist以及查看information_schema下面和锁以及事务相关的表来查看。&lt;/p&gt;  &lt;p&gt;经过排查也并未发现锁的问题。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;参数&lt;/h4&gt;  &lt;p&gt;参数部分使用如果是innodb引擎，可以根据自己的使用环境调整innodb_flush_log_at_trx_commit、sync_binlog参数来提升复制速度，那组DB使用的TokuDB，则可以优化tokudb_commit_sync、tokudb_fsync_log_period、sync_binlog等参数来做调整。这些参数调整后，复制的延迟情况会有一些作用。&lt;/p&gt;  &lt;p&gt;备注：这种调整可能会影响数据的安全性，需要结合业务来考虑。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;多线程&lt;/h4&gt;  &lt;p&gt;多线程问题可能是DBA们遇到最多的问题，之前在5.1和5.5版本，mysql的单线程复制瓶颈就广受诟病。从5.6开始mysql正式支持多线程复制。&lt;/p&gt;  &lt;p&gt;很容易想到，如果是单线程同步的话，单个线程存在写入瓶颈，导致主从延迟。那就先调整为多线程试试效果。&lt;/p&gt;  &lt;p&gt;可以通过show processlist查看是否有多个同步线程，也可以查看参数的方式查看是否使用多线程（show variables like &amp;apos;%slave_parallel%&amp;apos;）&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;当你看到是上图这种结果的时候，恭喜你，你使用的是单线程。使用下面那行命令改造成多线程复制：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;STOP SLAVE SQL_THREAD;SET GLOBAL slave_parallel_type=&amp;apos;LOGICAL_CLOCK&amp;apos;;SET GLOBAL slave_parallel_workers=8;START SLAVE SQL_THREAD;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;改造后如下图所示：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;我的环境如上图所示，本来就已经是多线程复制了，因此问题的根源也不在是否开启多线程复制上。但是当我使用show processlist查看复制状态的时候，大多数情况下发现只有1个SQL线程在执行，如下图所示：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;通过上面的图可以发现，基本都是一个线程在执行，那么可以初步判定是多线程的威力没有得到很好的发挥，为了更有力地说明问题，想办法统计出来每个同步线程使用的比率。统计方法如下:&lt;/p&gt;  &lt;p&gt;1、将线上从机相关统计打开（出于性能考虑默认是关闭的），打开方法可以如下如下SQL：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;UPDATE performance_schema.setup_consumers SET ENABLED = &amp;apos;YES&amp;apos; WHERE NAME LIKE &amp;apos;events_transactions%&amp;apos;;&lt;/p&gt;    &lt;p&gt;UPDATE performance_schema.setup_instruments SET ENABLED = &amp;apos;YES&amp;apos;, TIMED = &amp;apos;YES&amp;apos;WHERE NAME = &amp;apos;transaction&amp;apos;;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;2、创建一个查看各个同步线程使用量的视图，代码如下：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;USE test;&lt;/p&gt;    &lt;p&gt;CREATE VIEW rep_thread_count AS SELECT a.THREAD_ID AS THREAD_ID,a.COUNT_STAR AS COUNT_STAR FROM performance_schema.events_transactions_summary_by_thread_by_event_name a WHERE a.THREAD_ID in (SELECT b.THREAD_ID FROM performance_schema.replication_applier_status_by_worker b);&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;3、一段时间后，统计各个同步线程的使用比率，SQL如下:&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;SELECT SUM(COUNT_STAR) FROMrep_thread_count INTO @total;&lt;/p&gt;    &lt;p&gt;SELECT 100*(COUNT_STAR/@total) AS thread_usage FROMrep_thread_count;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;结果如下：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;从上面的结果我们可以看出，绝大多数情况下，都是一个线程在跑，在监控这种存在大量数据导入的场景，肯定容易出现瓶颈。如果能提高各个线程并发执行的能力，可能很好地改善同步延迟的情况，那该如何来解决呢？&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;组提交&lt;/h4&gt;  &lt;p&gt;我们不妨从多线程同步的原理来思考，在5.7中，多线程复制的功能有很很大的改善，支持LOGICAL_CLOCK的方式，在这种方式下，并发执行的多个事务只要能在同一时刻commit，就说明线程之间没有锁冲突，那么master就可以将这一组的事务标记并在slave机器上安全的进行并发执行。因此，可以尽可能地使所有线程能在同一时刻提交，这样就能很大程度上提升从机的执行的并行度，从而减少从机的延迟。&lt;/p&gt;  &lt;p&gt;有了这个猜想后，很自然想到了人为控制尽可能多地使所有线程在同一时刻提交，其实官方已经给我们提供了类似的参数，参数如下：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;binlog_group_commit_sync_delay&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;#参数说明见：    &lt;a href="https://link.jianshu.com?t=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.7%2Fen%2Freplication-options-binary-log.html%23sysvar_binlog_group_commit_sync_delay" rel="nofollow" target="_blank"&gt;https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_group_commit_sync_delay&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;备注：这个参数会对延迟SQL的响应，对延迟非常敏感的环境需要特别注意，单位是微秒&lt;/strong&gt;&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;binlog_group_commit_sync_no_delay_count&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;#参数说明见：    &lt;a href="https://link.jianshu.com?t=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.7%2Fen%2Freplication-options-binary-log.html%23sysvar_binlog_group_commit_sync_no_delay_count" rel="nofollow" target="_blank"&gt;https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html#sysvar_binlog_group_commit_sync_no_delay_count&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;备注：这个参数取到了一定的保护作用，在达到binlog_group_commit_sync_no_delay_count设定的值的时候，不管是否达到了binlog_group_commit_sync_delay设置定的阀值，都立即进行提交。&lt;/p&gt;  &lt;p&gt;由于是监控的DB，主要是load数据，然后展示，1秒左右的导入延迟对业务没什么影响，因此将两个参数调整为：&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;SET GLOBAL binlog_group_commit_sync_delay = 1000000;&lt;/p&gt;    &lt;p&gt;SET GLOBAL binlog_group_commit_sync_no_delay_count = 20;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;#备注，这两个参数请根据业务特性进行调整，以免造成线上故障。&lt;/p&gt;  &lt;p&gt;为了防止导入SQL堆积，设置SET GLOBAL binlog_group_commit_sync_no_delay_count为20，在达到20个事务的时候不管是否达到了1秒都进行提交。减少对业务的影响。&lt;/p&gt;  &lt;p&gt;设置完这两个参数后，发现并发复制瞬间提升了好多，很多时候8个线程都能跑满。于是将线程调整到16个。运行一段事件后，再次统计各个同步线程的使用比率，发现并发度提升了非常多，新的比率如下图所示：&lt;/p&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;通过show slave status查看，发现从机延迟越来越小，目前已经完全追上，并稳定运行了一周。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h4&gt;回顾总结&lt;/h4&gt;  &lt;p&gt;最后，简单总结一下：&lt;/p&gt;  &lt;p&gt;在遇到主从延迟的问题的时候，可以从如下几个地方开脑洞，寻找蛛丝马迹，找到问题的根源，对症下药，药到病除，排查范围包括但不限于如下几方面：&lt;/p&gt;  &lt;p&gt;网络方面&lt;/p&gt;  &lt;p&gt;性能方面&lt;/p&gt;  &lt;p&gt;配置方面（参数优化）&lt;/p&gt;  &lt;p&gt;大事务&lt;/p&gt;  &lt;p&gt;锁&lt;/p&gt;  &lt;p&gt;多线程复制&lt;/p&gt;  &lt;p&gt;组提交&lt;/p&gt;  &lt;p&gt;通过上面对整个问题排查的梳理，希望广大DBA遇到类似复制延迟的问题都能彻底终结。&lt;/p&gt;  &lt;h4&gt;    &lt;br /&gt;&lt;/h4&gt;  &lt;h4&gt;参考资料：&lt;/h4&gt;  &lt;p&gt;    &lt;a href="https://link.jianshu.com?t=https%3A%2F%2Fdev.mysql.com%2Fdoc%2Frefman%2F5.7%2Fen%2Freplication-options-binary-log.html" rel="nofollow" target="_blank"&gt;https://dev.mysql.com/doc/refman/5.7/en/replication-options-binary-log.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;    &lt;a href="https://link.jianshu.com?t=https%3A%2F%2Fwww.percona.com%2Fblog%2F2016%2F02%2F10%2Festimating-potential-for-mysql-5-7-parallel-replication%2F" rel="nofollow" target="_blank"&gt;https://www.percona.com/blog/2016/02/10/estimating-potential-for-mysql-5-7-parallel-replication/&lt;/a&gt;&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60450-mysql-%E5%90%8C%E6%AD%A5-%E5%BB%B6%E8%BF%9F</guid>
      <pubDate>Tue, 24 Mar 2020 08:46:53 CST</pubDate>
    </item>
    <item>
      <title>百万级商品数据实时同步，查询结果秒出</title>
      <link>https://itindex.net/detail/60441-%E7%99%BE%E4%B8%87-%E5%95%86%E5%93%81-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;img&gt;&lt;/img&gt;前阵子老板安排了一个新任务，要建设一个商家商品搜索系统，能够为用户提供快速、准确的搜索能力，在用户输入搜索内容时，要能从商家名称和商品名称两个维度去搜索，搜索出来的结果，按照准确率排序，并按商家所属商品的关联关系，来组合数据结构，同时提供API给业务系统调用。 &lt;br /&gt; &lt;br /&gt;背景很简单，现实蛮复杂！我们面临以下几个难题： &lt;strong&gt;①商家数据库和商品数据库是多台不同的服务器，并且数据量达百万级，如何才能实现跨数据库的数据同步呢？&lt;/strong&gt; &lt;strong&gt;  &lt;br /&gt;&lt;/strong&gt; &lt;strong&gt;②商家和商品的数据是有从属关系的，不然就会把肯德基的香辣鸡腿堡挂到麦当劳去，这就尴尬了！&lt;/strong&gt; &lt;strong&gt;  &lt;br /&gt;&lt;/strong&gt; &lt;strong&gt;③商家商品数据是经常更新的，比如修改价格、库存、上下架等，那搜索服务可不能搜出一堆过时的数据，如果客户明明搜出来的商品，点进去后却已下架了，那么客户就要吐槽了！如何实现搜索数据与源数据库增删改均实时同步呢？&lt;/strong&gt; &lt;strong&gt;  &lt;br /&gt;&lt;/strong&gt;带着以上3个问题，我们开始了搜索服务的整体架构设计。 &lt;br /&gt; &lt;h3&gt;  &lt;strong&gt;系统架构设计思路&lt;/strong&gt;&lt;/h3&gt;为了设计出合适的系统架构，我们分析了现状。首先，商家数据和商品数据分别存储在2个独立的MySQL8数据库，为满足商家数据和商品数据的关联，我们需要将两个库中所需要的表实时ETL到我们的搜索系统数据库。 &lt;br /&gt;其次，数据从商家、商品数据库ETL到搜索系统数据库后，需要实时的组合成为商家关联商品数据结构，并以父子文档的格式，存储到ES中。 &lt;br /&gt;最后，商家、商品数据库的增删改操作，需要实时的同步到ES中，也就是ES中的数据，需要支持实时的增加、删除和修改。 &lt;br /&gt;为此，我们设计了2个canal组件，第一个canal实现数据ETL，把商家、商品数据库的某些表及字段，抽取到搜索服务数据库；再利用第二个canal，读取搜索服务MySQL数据库的binlog，实时传输到kafka消息队列，再由canal adapter对数据进行关联、父子文档映射等，将处理好的数据存储到ElasticSearch中。 &lt;p&gt;具体系统架构设计如下图所示。&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;商家商品搜索系统架构设计&lt;/p&gt; &lt;br /&gt; &lt;strong&gt;项目实战&lt;/strong&gt; &lt;br /&gt; &lt;h4&gt;1、环境及软件说明&lt;/h4&gt;操作系统：CentOS 7canal：canal.adapter-1.1.4，canal.deployer-1.1.4kafka：kafka_2.12-2.3.0ElasticSearch：elasticsearch-6.3.2kibana：kibana-6.3.2 &lt;br /&gt; &lt;h3&gt;2、利用Canal实现数据ETL到MySQL8  &lt;br /&gt;&lt;/h3&gt;这个步骤是利用canal从2个独立的MySQL8数据库中，抽取需要的表到搜索服务的MySQL数据库。 &lt;br /&gt; &lt;h4&gt;2.1 安装canaldeployer&lt;/h4&gt;（1）解压canal.deployer-1.1.4.tar.gz（2）配置canal deployer进入canaldeployer/conf目录，修改canal.properties文件，主要配置serverMode、MQ和destination三部分。首先，我们serverMode修改为kafka模式，增加系统缓冲能力以及提高系统稳定性： &lt;img&gt;&lt;/img&gt; &lt;p&gt;serverMode&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;接着，配置kafka的MQ信息（kafka请自行安装）： &lt;img&gt;&lt;/img&gt; &lt;p&gt;kafka MQ信息&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;最后，配置需要实例化的instance，这里配置了3个，表示canal deploy会启动这3个实例，同步MySQL的binlog到kafka的topic内。如下图所示： &lt;img&gt;&lt;/img&gt; &lt;p&gt;destinations实例配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;（3）配置canal deployer instance进入canaldeployer/conf/example目录，发现有一个 &lt;strong&gt;instance.properties&lt;/strong&gt;文件，这是canal给的示例，我们可以参考其配置。①我们拷贝整个example目录，并重命名为上个步骤配置的destination之一，如xxxsearch；②进入xxxsearch目录，编辑instance.properties文件，主要配置源数据库信息、所需数据表及字段，以及指定kafka的topic名，这样源数据库的binlog就会转换为json数据，并实时的通过canal deployer传输到kafka该topic中。如下所示： &lt;img&gt;&lt;/img&gt; &lt;p&gt;canaldeploy instance 源数据库配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;canaldeploy instance kafka topic配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;③进入canaldeployer/bin目录，执行./startup.sh，启动canal deployer及所属实例。至此canal deployer搭建完成。 &lt;br /&gt; &lt;h4&gt;2.2 安装canal.adapter&lt;/h4&gt;我们需要利用canal.adapter将kafka topic中的binlog json数据，经过清洗转换等操作，存储到MySQL8中。由于canal原生是不支持MySQL8的，故我们需要做一些调整。（1）增加MySQL8连接驱动解压canal.adapter-1.1.4.tar.gz，进入canaladapter/lib目录，移除mysql-connector-java-5.1.40.jar，导入mysql-connector-java-8.0.18.jar &lt;br /&gt;（2）配置canal adapter，使数据输出到MySQL8。进入canaladapter/conf目录，编辑application.yml文件，主要配置消费kafka、源数据库信息和搜索系统数据库信息，如下所示： &lt;img&gt;&lt;/img&gt; &lt;p&gt;ETL到MySQL8配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;接着，进入canaladapter/conf/rdb目录，以官方提供的mytest_user.yml为例，配置kafka topic名、源数据库名、源数据表名，以及目标数据库名和目标数据表名， &lt;strong&gt;建议一张表对应一个yml文件&lt;/strong&gt;。 &lt;img&gt;&lt;/img&gt; &lt;p&gt;ETL表结构映射配置  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt;（3）启动canaladapter进入canaladapter/bin目录，执行./startup.sh，启动canal adapter，观察logs/adapter/adapter.log日志文件，手动在搜索系统数据库新增一条记录，看是否会打印如下日志，即有2条记录，一条INFO，一条DEBUG，则表示配置成功。 &lt;img&gt;&lt;/img&gt;canaladapter日志至此，数据ETL阶段搭建完成，数据可从两个不同的MySQL8数据库，实时同步到搜索服务的MySQL数据库。 &lt;br /&gt; &lt;h3&gt;3、实现数据多表关联、父子文档映射  &lt;br /&gt;&lt;/h3&gt; &lt;strong&gt;（1）配置第二个canal的canaladapter&lt;/strong&gt;进入canaladapter/conf目录，编辑application.yml文件，主要配置消费kafka、搜索系统数据库，和ES连接信息，如下所示： &lt;img&gt;&lt;/img&gt; &lt;p&gt;canaladapter MQ及mysql配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;canaladapter ES配置&lt;/p&gt; &lt;p&gt;  &lt;br /&gt;&lt;/p&gt; &lt;strong&gt;（2）配置多表关联&lt;/strong&gt;进入canaladapter/conf/es目录，vim mytest_user.yml，编辑多表关联配置： &lt;img&gt;&lt;/img&gt; &lt;p&gt;多表关联配置&lt;/p&gt; &lt;blockquote&gt;注意，sql支持多表关联自由组合, 但是有一定的限制:（a）主表不能为子查询语句（b）只能使用left outer join即最左表一定要是主表（c）关联从表如果是子查询不能有多张表（d）主sql中不能有where查询条件(从表子查询中可以有where条件但是不推荐, 可能会造成数据同步的不一致, 比如修改了where条件中的字段内容)（e）关联条件只允许主外键的&amp;apos;=&amp;apos;操作不能出现其他常量判断比如: on a.role_id=b.id and b.statues=1（f）关联条件必须要有一个字段出现在主查询语句中比如: on a.role_id=b.id  其中的 a.role_id 或者 b.id 必须出现在主select语句中（g）Elastic Search的mapping 属性与sql的查询值将一一对应(不支持 select *), 比如: select a.id as _id, a.name, a.email as _email from user, 其中name将映射到es mapping的name field, _email将映射到mapping的_email field, 这里以别名(如果有别名)作为最终的映射字段. 这里的_id可以填写到配置文件的 _id: _id映射.&lt;/blockquote&gt; &lt;strong&gt;（3）配置父子文档&lt;/strong&gt;以官方的biz_order.yml为例，vim biz_order.yml，配置父子文档映射： &lt;img&gt;&lt;/img&gt; &lt;p&gt;配置父子文档映射&lt;/p&gt; &lt;br /&gt; &lt;strong&gt;（4）在ElasticSearch6中，建立index和父子文档映射关系&lt;/strong&gt;进入kibana页面，点击Dev Tools，执行如下命令，即可建立索引及父子文档映射： &lt;img&gt;&lt;/img&gt;建立index和父子文档映射其中，ES6和kibana的安装，在此无特别配置，不做赘述。 &lt;br /&gt; &lt;strong&gt;（5）启动canal adapter&lt;/strong&gt;进入canaladapter/bin目录，执行./startup.sh，启动canal adapter，观察logs/adapter/adapter.log日志文件，手动在搜索系统数据库新增一条记录，看是否会打印如下日志，如打印则表示配置成功。 &lt;img&gt;&lt;/img&gt;正确配置adapter日志示例 &lt;br /&gt; &lt;h3&gt;4、运行结果  &lt;br /&gt;&lt;/h3&gt;现在，我们可以通过kibana来执行DSL语句来查询看看。我们事先已在商家系统中增加了一个“肯德基”商店，然后在商品系统中添加了“西红柿”和”新鲜西红柿“2个商品，并将商品关联到“肯德基”上。接着我们查询”肯德基“或者“西红柿”，得到以下是查询的结果（去除了ES默认字段）： &lt;img&gt;&lt;/img&gt;通过DSL查询的结果由图可见，我们可以通过商家名查询商品，也可通过商品名查询商店和商品，并且canal支持数据的实时增删改，所以ES的数据也会与商家系统和商品系统保持一致，同时数据结构包含商家及对应的商品，满足业务需求。 &lt;br /&gt; &lt;h3&gt;5、总结  &lt;br /&gt;&lt;/h3&gt;至此，基于Canal、kafka、MySQL8、ElasticSearch6技术的商家商品搜索系统基础框架搭建完成。我们采用canal deployer实时读取商家、商品系统的MySQL数据库binlog，并发送至kafka，接着由canal adapter消费kafka，并将binlog json数据进行多表关联、父子文档映射，最后存储到ES6中，供上层搜索服务调用。 &lt;strong&gt;搜索服务系统最终成功上线，为公司百万级商家商品提供实时数据同步，秒级搜索结果展示，达到业务要求，老板说了，给研发团队每人加个鸡腿！想想还有点小激动，嘿嘿~~&lt;/strong&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/60441-%E7%99%BE%E4%B8%87-%E5%95%86%E5%93%81-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Sat, 21 Mar 2020 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>利用ogg实现oracle到kafka的增量数据实时同步 | 伦少的博客</title>
      <link>https://itindex.net/detail/60332-%E5%88%A9%E7%94%A8-ogg-oracle</link>
      <description>&lt;div&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#&amp;#21069;&amp;#35328;" title=""&gt;&lt;/a&gt;前言&lt;/h2&gt;    &lt;p&gt;ogg即Oracle GoldenGate是Oracle的同步工具，本文讲如何配置ogg以实现Oracle数据库增量数据实时同步到kafka中，其中同步消息格式为json。      &lt;br /&gt;下面是我的源端和目标端的一些配置信息：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;th&gt;-&lt;/th&gt;        &lt;th&gt;版本&lt;/th&gt;        &lt;th&gt;OGG版本&lt;/th&gt;        &lt;th&gt;ip&lt;/th&gt;        &lt;th&gt;别名&lt;/th&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td&gt;源端&lt;/td&gt;        &lt;td&gt;OracleRelease 11.2.0.1.0&lt;/td&gt;        &lt;td&gt;Oracle GoldenGate 11.2.1.0.3 for Oracle on Linux x86-64&lt;/td&gt;        &lt;td&gt;192.168.44.128&lt;/td&gt;        &lt;td&gt;master&lt;/td&gt;&lt;/tr&gt;      &lt;tr&gt;        &lt;td&gt;目标端&lt;/td&gt;        &lt;td&gt;kafka_2.11-1.1.0&lt;/td&gt;        &lt;td&gt;Oracle GoldenGate for Big Data 12.3.1.1.1 on Linux x86-64&lt;/td&gt;        &lt;td&gt;192.168.44.129&lt;/td&gt;        &lt;td&gt;slave1&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#1&amp;#12289;&amp;#19979;&amp;#36733;" title=""&gt;&lt;/a&gt;1、下载&lt;/h2&gt;    &lt;p&gt;可在      &lt;a href="http://www.oracle.com/technetwork/middleware/goldengate/downloads/index.html" rel="noopener" target="_blank"&gt;这里&lt;/a&gt;或      &lt;a href="https://edelivery.oracle.com/osdc/faces/SoftwareDelivery" rel="noopener" target="_blank"&gt;旧版本&lt;/a&gt;查询下载      &lt;br /&gt;注意：源端和目标端的文件不一样，目标端需要下载Oracle GoldenGate for Big Data,源端需要下载Oracle GoldenGate for Oracle具体下载方法见最后的附录截图。&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2&amp;#12289;&amp;#28304;&amp;#31471;&amp;#65288;Oracle&amp;#65289;&amp;#37197;&amp;#32622;" title=""&gt;&lt;/a&gt;2、源端（Oracle）配置&lt;/h2&gt;    &lt;p&gt;注意：源端是安装了oracle的机器，oracle环境变量之前都配置好了&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-1-&amp;#35299;&amp;#21387;" title=""&gt;&lt;/a&gt;2.1 解压&lt;/h3&gt;    &lt;p&gt;先建立ogg目录      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;mkdir -p /opt/ogg             &lt;br /&gt;unzip V34339-01.zip             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;解压后得到一个tar包，再解压这个tar      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;tar xf fbo_ggs_Linux_x64_ora11g_64bit.tar -C /opt/ogg             &lt;br /&gt;chown -R oracle:oinstall /opt/ogg （使oracle用户有ogg的权限，后面有些需要在oracle用户下执行才能成功）             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;a&gt;&lt;/a&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-2-&amp;#37197;&amp;#32622;ogg&amp;#29615;&amp;#22659;&amp;#21464;&amp;#37327;" title=""&gt;&lt;/a&gt;2.2 配置ogg环境变量&lt;/h3&gt;    &lt;p&gt;为了简单方便起见，我在/etc/profile里配置的，建议在生产中配置oracle的环境变量文件/home/oracle/.bash_profile里配置，为了怕出问题，我把OGG_HOME等环境变量在/etc/profile配置了一份，不知道这是否是必须的。      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;vim /etc/profile             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;export OGG_HOME=/opt/ogg            &lt;br /&gt;export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib            &lt;br /&gt;export PATH=$OGG_HOME:$PATH            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;使之生效      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;source/etc/profile             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;测试一下ogg命令      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;ggsci             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;如果命令成功即可进行下一步，不成功请检查前面的步骤。&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-3-oracle&amp;#25171;&amp;#24320;&amp;#24402;&amp;#26723;&amp;#27169;&amp;#24335;" title=""&gt;&lt;/a&gt;2.3 oracle打开归档模式&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;su - oracle            &lt;br /&gt;sqlplus / as sysdba            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;执行下面的命令查看当前是否为归档模式      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;archive log list             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;SQL&amp;gt; archive log list            &lt;br /&gt;Database log mode	       No Archive Mode            &lt;br /&gt;Automatic archival	       Disabled            &lt;br /&gt;Archive destination	       USE_DB_RECOVERY_FILE_DEST            &lt;br /&gt;Oldest online log sequence     12            &lt;br /&gt;Current log sequence	       14            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;若为Disabled，手动打开即可      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;conn / as sysdba (以DBA身份连接数据库)             &lt;br /&gt;shutdown immediate (立即关闭数据库)             &lt;br /&gt;startup mount (启动实例并加载数据库，但不打开)             &lt;br /&gt;alterdatabasearchivelog; (更改数据库为归档模式)             &lt;br /&gt;alterdatabaseopen; (打开数据库)             &lt;br /&gt;altersystemarchivelogstart; (启用自动归档)             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;再执行一下      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;archive log list             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;Database log mode	       Archive Mode            &lt;br /&gt;Automatic archival	       Enabled            &lt;br /&gt;Archive destination	       USE_DB_RECOVERY_FILE_DEST            &lt;br /&gt;Oldest online log sequence     12            &lt;br /&gt;Next log sequence to archive   14            &lt;br /&gt;Current log sequence	       14            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;可以看到为Enabled，则成功打开归档模式。&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-4-Oracle&amp;#25171;&amp;#24320;&amp;#26085;&amp;#24535;&amp;#30456;&amp;#20851;" title=""&gt;&lt;/a&gt;2.4 Oracle打开日志相关&lt;/h3&gt;    &lt;p&gt;OGG基于辅助日志等进行实时传输，故需要打开相关日志确保可获取事务内容，通过下面的命令查看该状态      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;selectforce_logging, supplemental_log_data_minfromv$database;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;FORCE_ SUPPLEMENTAL_LOG            &lt;br /&gt;------ ----------------            &lt;br /&gt;NO     NO            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;若为NO，则需要通过命令修改      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;alterdatabaseforcelogging;             &lt;br /&gt;alterdatabaseaddsupplementallogdata;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;再查看一下为YES即可      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;SQL&amp;gt; select force_logging, supplemental_log_data_min from v$database;             &lt;br /&gt;             &lt;br /&gt;FORCE_ SUPPLEMENTAL_LOG             &lt;br /&gt;------ ----------------             &lt;br /&gt;YES    YES             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-5-oracle&amp;#21019;&amp;#24314;&amp;#22797;&amp;#21046;&amp;#29992;&amp;#25143;" title=""&gt;&lt;/a&gt;2.5 oracle创建复制用户&lt;/h3&gt;    &lt;p&gt;首先root用户建立相关文件夹，并赋予权限      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;mkdir -p /u01/app/oracle/oggdata/orcl             &lt;br /&gt;chown -R oracle:oinstall /u01/app/oracle/oggdata/orcl             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;然后执行下面sql      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;SQL&amp;gt; create tablespace oggtbs datafile &amp;apos;/u01/app/oracle/oggdata/orcl/oggtbs01.dbf&amp;apos; size 1000M autoextend on;             &lt;br /&gt;             &lt;br /&gt;Tablespace created.             &lt;br /&gt;             &lt;br /&gt;SQL&amp;gt;  create user ogg identified by ogg default tablespace oggtbs;             &lt;br /&gt;             &lt;br /&gt;User created.             &lt;br /&gt;             &lt;br /&gt;SQL&amp;gt; grant dba to ogg;             &lt;br /&gt;             &lt;br /&gt;Grantsucceeded.             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-6-OGG&amp;#21021;&amp;#22987;&amp;#21270;" title=""&gt;&lt;/a&gt;2.6 OGG初始化&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;ggsci            &lt;br /&gt;create subdirs            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;8            &lt;br /&gt;9            &lt;br /&gt;10            &lt;br /&gt;11            &lt;br /&gt;12            &lt;br /&gt;13            &lt;br /&gt;14            &lt;br /&gt;15            &lt;br /&gt;16            &lt;br /&gt;17            &lt;br /&gt;18            &lt;br /&gt;19            &lt;br /&gt;20            &lt;br /&gt;21            &lt;br /&gt;22            &lt;br /&gt;23            &lt;br /&gt;24            &lt;br /&gt;25            &lt;br /&gt;26            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;ggsci            &lt;br /&gt;            &lt;br /&gt;Oracle GoldenGate Command InterpreterforOracle            &lt;br /&gt;Version 11.2.1.0.3 14400833 OGGCORE_11.2.1.0.3_PLATFORMS_120823.1258_FBO            &lt;br /&gt;Linux, x64, 64bit (optimized), Oracle 11g on Aug 23 2012 20:20:21            &lt;br /&gt;            &lt;br /&gt;Copyright (C) 1995, 2012, Oracle and/or its affiliates. All rights reserved.            &lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;GGSCI (ambari.master.com) 1&amp;gt; create subdirs            &lt;br /&gt;            &lt;br /&gt;Creating subdirectories under current directory /root            &lt;br /&gt;            &lt;br /&gt;Parameter files                /root/dirprm: created            &lt;br /&gt;Report files                   /root/dirrpt: created            &lt;br /&gt;Checkpoint files               /root/dirchk: created            &lt;br /&gt;Process status files           /root/dirpcs: created            &lt;br /&gt;SQL script files               /root/dirsql: created            &lt;br /&gt;Database definitions files     /root/dirdef: created            &lt;br /&gt;Extract data files             /root/dirdat: created            &lt;br /&gt;Temporary files                /root/dirtmp: created            &lt;br /&gt;Stdout files                   /root/dirout: created            &lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;GGSCI (ambari.master.com) 2&amp;gt;            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#2-7-Oracle&amp;#21019;&amp;#24314;&amp;#27979;&amp;#35797;&amp;#34920;" title=""&gt;&lt;/a&gt;2.7 Oracle创建测试表&lt;/h3&gt;    &lt;p&gt;创建一个用户,在该用户下新建测试表，用户名、密码、表名均为 test_ogg。      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;createusertest_oggidentifiedbytest_oggdefaulttablespaceusers;             &lt;br /&gt;grantdbatotest_ogg;             &lt;br /&gt;conn test_ogg/test_ogg;             &lt;br /&gt;createtabletest_ogg(idint,namevarchar(20),primarykey(id));             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#3-&amp;#30446;&amp;#26631;&amp;#31471;&amp;#65288;kafka&amp;#65289;&amp;#37197;&amp;#32622;" title=""&gt;&lt;/a&gt;3 目标端（kafka）配置&lt;/h2&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;mkdir -p /opt/ogg            &lt;br /&gt;unzip 123111_ggs_Adapters_Linux_x64.zip            &lt;br /&gt;tar xf ggs_Adapters_Linux_x64.tar  -C /opt/ogg/            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#3-2-&amp;#29615;&amp;#22659;&amp;#21464;&amp;#37327;" title=""&gt;&lt;/a&gt;3.2 环境变量&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;vim /etc/profile            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;export OGG_HOME=/opt/ogg            &lt;br /&gt;export LD_LIBRARY_PATH=$JAVA_HOME/jre/lib/amd64:$JAVA_HOME/jre/lib/amd64/server:$JAVA_HOME/jre/lib/amd64/libjsig.so:$JAVA_HOME/jre/lib/amd64/server/libjvm.so:$OGG_HOME/lib            &lt;br /&gt;export PATH=$OGG_HOME:$PATH            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;source/etc/profile            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;同样测试一下ogg命令      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;ggsci             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#3-3-&amp;#21021;&amp;#22987;&amp;#21270;&amp;#30446;&amp;#24405;" title=""&gt;&lt;/a&gt;3.3 初始化目录&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;create subdirs            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4&amp;#12289;OGG&amp;#28304;&amp;#31471;&amp;#37197;&amp;#32622;" title=""&gt;&lt;/a&gt;4、OGG源端配置&lt;/h2&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-1-&amp;#37197;&amp;#32622;OGG&amp;#30340;&amp;#20840;&amp;#23616;&amp;#21464;&amp;#37327;" title=""&gt;&lt;/a&gt;4.1 配置OGG的全局变量&lt;/h3&gt;    &lt;p&gt;先切换到oracle用户下      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;su oracle             &lt;br /&gt;cd/opt/ogg             &lt;br /&gt;ggsci             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 1&amp;gt; dblogin userid ogg password ogg            &lt;br /&gt;Successfully logged into database.            &lt;br /&gt;            &lt;br /&gt;GGSCI (ambari.master.com) 2&amp;gt; edit param ./globals            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;然后和用vim编辑一样添加      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;oggschema ogg             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-2-&amp;#37197;&amp;#32622;&amp;#31649;&amp;#29702;&amp;#22120;mgr" title=""&gt;&lt;/a&gt;4.2 配置管理器mgr&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 3&amp;gt; edit param mgr            &lt;br /&gt;PORT 7809            &lt;br /&gt;DYNAMICPORTLIST 7810-7909            &lt;br /&gt;AUTORESTART EXTRACT *,RETRIES 5,WAITMINUTES 3            &lt;br /&gt;PURGEOLDEXTRACTS ./dirdat/*,usecheckpoints, minkeepdays 3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;说明：PORT即mgr的默认监听端口；DYNAMICPORTLIST动态端口列表，当指定的mgr端口不可用时，会在这个端口列表中选择一个，最大指定范围为256个；AUTORESTART重启参数设置表示重启所有EXTRACT进程，最多5次，每次间隔3分钟；PURGEOLDEXTRACTS即TRAIL文件的定期清理&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-3-&amp;#28155;&amp;#21152;&amp;#22797;&amp;#21046;&amp;#34920;" title=""&gt;&lt;/a&gt;4.3 添加复制表&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;8            &lt;br /&gt;9            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 4&amp;gt; add trandata test_ogg.test_ogg            &lt;br /&gt;            &lt;br /&gt;Logging of supplemental redo data enabledfortable TEST_OGG.TEST_OGG.            &lt;br /&gt;            &lt;br /&gt;GGSCI (ambari.master.com) 5&amp;gt; info trandata test_ogg.test_ogg            &lt;br /&gt;            &lt;br /&gt;Logging of supplemental redologdata is enabledfortable TEST_OGG.TEST_OGG.            &lt;br /&gt;            &lt;br /&gt;Columns supplementally loggedfortable TEST_OGG.TEST_OGG: ID            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-4-&amp;#37197;&amp;#32622;extract&amp;#36827;&amp;#31243;" title=""&gt;&lt;/a&gt;4.4 配置extract进程&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;8            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 6&amp;gt; edit param extkafka            &lt;br /&gt;extract extkafka            &lt;br /&gt;dynamicresolution            &lt;br /&gt;SETENV (ORACLE_SID =&amp;quot;orcl&amp;quot;)            &lt;br /&gt;SETENV (NLS_LANG =&amp;quot;american_america.AL32UTF8&amp;quot;)            &lt;br /&gt;userid ogg,password ogg            &lt;br /&gt;exttrail /opt/ogg/dirdat/to            &lt;br /&gt;table test_ogg.test_ogg;            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;说明：第一行指定extract进程名称；dynamicresolution动态解析；SETENV设置环境变量，这里分别设置了Oracle数据库以及字符集；userid ggs,password ggs即OGG连接Oracle数据库的帐号密码，这里使用2.5中特意创建的复制帐号；exttrail定义trail文件的保存位置以及文件名，注意这里文件名只能是2个字母，其余部分OGG会补齐；table即复制表的表名，支持*通配，必须以;结尾&lt;/p&gt;    &lt;p&gt;添加extract进程：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 16&amp;gt; add extract extkafka,tranlog,begin now            &lt;br /&gt;EXTRACT added.            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;(注：若报错      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;ERROR: Could not create checkpoint file /opt/ogg/dirchk/EXTKAFKA.cpe (error 2, No such file or directory).             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;执行下面的命令再重新添加即可。      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;create subdirs             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;)&lt;/p&gt;    &lt;p&gt;添加trail文件的定义与extract进程绑定：&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.master.com) 17&amp;gt; add exttrail /opt/ogg/dirdat/to,extract extkafka            &lt;br /&gt;EXTTRAIL added.            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-5-&amp;#37197;&amp;#32622;pump&amp;#36827;&amp;#31243;" title=""&gt;&lt;/a&gt;4.5 配置pump进程&lt;/h3&gt;    &lt;p&gt;pump进程本质上来说也是一个extract，只不过他的作用仅仅是把trail文件传递到目标端，配置过程和extract进程类似，只是逻辑上称之为pump进程      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.master.com) 18&amp;gt; edit param pukafka             &lt;br /&gt;extract pukafka             &lt;br /&gt;passthru             &lt;br /&gt;dynamicresolution             &lt;br /&gt;userid ogg,password ogg             &lt;br /&gt;rmthost 192.168.44.129 mgrport 7809             &lt;br /&gt;rmttrail /opt/ogg/dirdat/to             &lt;br /&gt;table test_ogg.test_ogg;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;说明：第一行指定extract进程名称；passthru即禁止OGG与Oracle交互，我们这里使用pump逻辑传输，故禁止即可；dynamicresolution动态解析；userid ogg,password ogg即OGG连接Oracle数据库的帐号密码rmthost和mgrhost即目标端(kafka)OGG的mgr服务的地址以及监听端口；rmttrail即目标端trail文件存储位置以及名称。&lt;/p&gt;    &lt;p&gt;分别将本地trail文件和目标端的trail文件绑定到extract进程：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.master.com) 1&amp;gt; add extract pukafka,exttrailsource /opt/ogg/dirdat/to             &lt;br /&gt;EXTRACT added.             &lt;br /&gt;GGSCI (ambari.master.com) 2&amp;gt; add rmttrail /opt/ogg/dirdat/to,extract pukafka             &lt;br /&gt;RMTTRAIL added.             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#4-6-&amp;#37197;&amp;#32622;define&amp;#25991;&amp;#20214;" title=""&gt;&lt;/a&gt;4.6 配置define文件&lt;/h3&gt;    &lt;p&gt;Oracle与MySQL，Hadoop集群（HDFS，Hive，kafka等）等之间数据传输可以定义为异构数据类型的传输，故需要定义表之间的关系映射，在OGG命令行执行：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.master.com) 3&amp;gt; edit param test_ogg             &lt;br /&gt;defsfile /opt/ogg/dirdef/test_ogg.test_ogg             &lt;br /&gt;userid ogg,password ogg             &lt;br /&gt;table test_ogg.test_ogg;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;在OGG主目录下执行(oracle用户)：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;12             &lt;br /&gt;13             &lt;br /&gt;14             &lt;br /&gt;15             &lt;br /&gt;16             &lt;br /&gt;17             &lt;br /&gt;18             &lt;br /&gt;19             &lt;br /&gt;20             &lt;br /&gt;21             &lt;br /&gt;22             &lt;br /&gt;23             &lt;br /&gt;24             &lt;br /&gt;25             &lt;br /&gt;26             &lt;br /&gt;27             &lt;br /&gt;28             &lt;br /&gt;29             &lt;br /&gt;30             &lt;br /&gt;31             &lt;br /&gt;32             &lt;br /&gt;33             &lt;br /&gt;34             &lt;br /&gt;35             &lt;br /&gt;36             &lt;br /&gt;37             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;./defgen paramfile dirprm/test_ogg.prm             &lt;br /&gt;             &lt;br /&gt;***********************************************************************             &lt;br /&gt;Oracle GoldenGate Table Definition GeneratorforOracle             &lt;br /&gt;Version 11.2.1.0.3 14400833 OGGCORE_11.2.1.0.3_PLATFORMS_120823.1258             &lt;br /&gt;Linux, x64, 64bit (optimized), Oracle 11g on Aug 23 2012 16:58:29             &lt;br /&gt;             &lt;br /&gt;Copyright (C) 1995, 2012, Oracle and/or its affiliates. All rights reserved.             &lt;br /&gt;             &lt;br /&gt;             &lt;br /&gt;Starting at 2018-05-23 05:03:04             &lt;br /&gt;***********************************************************************             &lt;br /&gt;             &lt;br /&gt;Operating System Version:             &lt;br /&gt;Linux             &lt;br /&gt;Version#1 SMP Wed Apr 12 15:04:24 UTC 2017, Release 3.10.0-514.16.1.el7.x86_64             &lt;br /&gt;Node: ambari.master.com             &lt;br /&gt;Machine: x86_64             &lt;br /&gt;softlimithardlimit             &lt;br /&gt;Address Space Size   :    unlimited    unlimited             &lt;br /&gt;Heap Size            :    unlimited    unlimited             &lt;br /&gt;File Size            :    unlimited    unlimited             &lt;br /&gt;CPU Time             :    unlimited    unlimited             &lt;br /&gt;             &lt;br /&gt;Process id: 13126             &lt;br /&gt;             &lt;br /&gt;***********************************************************************             &lt;br /&gt;**            Running with the following parameters                  **             &lt;br /&gt;***********************************************************************             &lt;br /&gt;defsfile /opt/ogg/dirdef/test_ogg.test_ogg             &lt;br /&gt;userid ogg,password ***             &lt;br /&gt;table test_ogg.test_ogg;             &lt;br /&gt;Retrieving definitionforTEST_OGG.TEST_OGG             &lt;br /&gt;             &lt;br /&gt;             &lt;br /&gt;             &lt;br /&gt;Definitions generatedfor1 tablein/opt/ogg/dirdef/test_ogg.test_ogg             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;将生成的/opt/ogg/dirdef/test_ogg.test_ogg发送的目标端ogg目录下的dirdef里：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;scp -r /opt/ogg/dirdef/test_ogg.test_ogg root@slave1:/opt/ogg/dirdef/             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5&amp;#12289;OGG&amp;#30446;&amp;#26631;&amp;#31471;&amp;#37197;&amp;#32622;" title=""&gt;&lt;/a&gt;5、OGG目标端配置&lt;/h2&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-1-&amp;#24320;&amp;#21551;kafka&amp;#26381;&amp;#21153;" title=""&gt;&lt;/a&gt;5.1 开启kafka服务&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;cd/opt/kafka_2.11-1.1.0/            &lt;br /&gt;bin/zookeeper-server-start.sh config/zookeeper.properties            &lt;br /&gt;bin/kafka-server-start.sh config/server.properties            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-2-&amp;#37197;&amp;#32622;&amp;#31649;&amp;#29702;&amp;#22120;mgr" title=""&gt;&lt;/a&gt;5.2 配置管理器mgr&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.slave1.com) 1&amp;gt;  edit param mgr            &lt;br /&gt;PORT 7809            &lt;br /&gt;DYNAMICPORTLIST 7810-7909            &lt;br /&gt;AUTORESTART EXTRACT *,RETRIES 5,WAITMINUTES 3            &lt;br /&gt;PURGEOLDEXTRACTS ./dirdat/*,usecheckpoints, minkeepdays 3            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-3-&amp;#37197;&amp;#32622;checkpoint" title=""&gt;&lt;/a&gt;5.3 配置checkpoint&lt;/h3&gt;    &lt;p&gt;checkpoint即复制可追溯的一个偏移量记录，在全局配置里添加checkpoint表即可。      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;edit  param  ./GLOBALS             &lt;br /&gt;CHECKPOINTTABLE test_ogg.checkpoint             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-4-&amp;#37197;&amp;#32622;replicate&amp;#36827;&amp;#31243;" title=""&gt;&lt;/a&gt;5.4 配置replicate进程&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.slave1.com) 4&amp;gt; edit param rekafka            &lt;br /&gt;REPLICAT rekafka            &lt;br /&gt;sourcedefs /opt/ogg/dirdef/test_ogg.test_ogg            &lt;br /&gt;TARGETDB LIBFILE libggjava.so SET property=dirprm/kafka.props            &lt;br /&gt;REPORTCOUNT EVERY 1 MINUTES, RATE            &lt;br /&gt;GROUPTRANSOPS 10000            &lt;br /&gt;MAP test_ogg.test_ogg, TARGET test_ogg.test_ogg;            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;说明：REPLICATE rekafka定义rep进程名称；sourcedefs即在4.6中在源服务器上做的表映射文件；TARGETDB LIBFILE即定义kafka一些适配性的库文件以及配置文件，配置文件位于OGG主目录下的dirprm/kafka.props；REPORTCOUNT即复制任务的报告生成频率；GROUPTRANSOPS为以事务传输时，事务合并的单位，减少IO操作；MAP即源端与目标端的映射关系&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-5-&amp;#37197;&amp;#32622;kafka-props" title=""&gt;&lt;/a&gt;5.5 配置kafka.props&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;cd/opt/ogg/dirprm/            &lt;br /&gt;vim kafka.props            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;gg.handlerlist=kafkahandler //handler类型            &lt;br /&gt;gg.handler.kafkahandler.type=kafka            &lt;br /&gt;gg.handler.kafkahandler.KafkaProducerConfigFile=custom_kafka_producer.properties //kafka相关配置            &lt;br /&gt;gg.handler.kafkahandler.topicMappingTemplate=test_ogg //kafka的topic名称，无需手动创建            &lt;br /&gt;gg.handler.kafkahandler.format=json //传输文件的格式，支持json，xml等            &lt;br /&gt;gg.handler.kafkahandler.mode=op  //OGGforBig Data中传输模式，即op为一次SQL传输一次，tx为一次事务传输一次            &lt;br /&gt;gg.classpath=dirprm/:/opt/kafka_2.11-1.1.0/libs/*:/opt/ogg/:/opt/ogg/lib/*            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;vim custom_kafka_producer.properties            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;3            &lt;br /&gt;4            &lt;br /&gt;5            &lt;br /&gt;6            &lt;br /&gt;7            &lt;br /&gt;8            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;bootstrap.servers=192.168.44.129:9092//kafkabroker的地址            &lt;br /&gt;acks=1            &lt;br /&gt;compression.type=gzip //压缩类型            &lt;br /&gt;reconnect.backoff.ms=1000//重连延时            &lt;br /&gt;value.serializer=org.apache.kafka.common.serialization.ByteArraySerializer            &lt;br /&gt;key.serializer=org.apache.kafka.common.serialization.ByteArraySerializer            &lt;br /&gt;batch.size=102400            &lt;br /&gt;linger.ms=10000            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;其中需要将后面的注释去掉，ogg不识别注释，如果不去掉会报错&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#5-6-&amp;#28155;&amp;#21152;trail&amp;#25991;&amp;#20214;&amp;#21040;replicate&amp;#36827;&amp;#31243;" title=""&gt;&lt;/a&gt;5.6 添加trail文件到replicate进程&lt;/h3&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;2            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.slave1.com) 2&amp;gt; add replicat rekafka exttrail /opt/ogg/dirdat/to,checkpointtable test_ogg.checkpoint            &lt;br /&gt;REPLICAT added.            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#6&amp;#12289;&amp;#27979;&amp;#35797;" title=""&gt;&lt;/a&gt;6、测试&lt;/h2&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#6-1-&amp;#21551;&amp;#21160;&amp;#25152;&amp;#26377;&amp;#36827;&amp;#31243;" title=""&gt;&lt;/a&gt;6.1 启动所有进程&lt;/h3&gt;    &lt;p&gt;在源端和目标端的OGG命令行下使用start [进程名]的形式启动所有进程。      &lt;br /&gt;启动顺序按照源mgr——目标mgr——源extract——源pump——目标replicate来完成。      &lt;br /&gt;全部需要在ogg目录下执行ggsci目录进入ogg命令行。      &lt;br /&gt;源端依次是      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;start mgr             &lt;br /&gt;start extkafka             &lt;br /&gt;start pukafka             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;目标端      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;start mgr             &lt;br /&gt;start rekafka             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;可以通过info all 或者info [进程名] 查看状态，所有的进程都为RUNNING才算成功      &lt;br /&gt;源端      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.master.com) 5&amp;gt; info all             &lt;br /&gt;             &lt;br /&gt;Program     Status      Group       Lag at Chkpt  Time Since Chkpt             &lt;br /&gt;             &lt;br /&gt;MANAGER     RUNNING             &lt;br /&gt;EXTRACT     RUNNING     EXTKAFKA    04:50:21      00:00:03             &lt;br /&gt;EXTRACT     RUNNING     PUKAFKA     00:00:00      00:00:03             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;目标端      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.slave1.com) 3&amp;gt; info all             &lt;br /&gt;             &lt;br /&gt;Program     Status      Group       Lag at Chkpt  Time Since Chkpt             &lt;br /&gt;             &lt;br /&gt;MANAGER     RUNNING             &lt;br /&gt;REPLICAT    RUNNING     REKAFKA     00:00:00      00:00:01             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#6-2-&amp;#24322;&amp;#24120;&amp;#35299;&amp;#20915;" title=""&gt;&lt;/a&gt;6.2 异常解决&lt;/h3&gt;    &lt;p&gt;如果有不是RUNNING可通过查看日志的方法检查解决问题，具体通过下面两种方法      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;vim ggser.log             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;或者ogg命令行,以rekafka进程为例&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;GGSCI (ambari.slave1.com) 2&amp;gt; view report rekafka            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;列举其中我遇到的一个问题：      &lt;br /&gt;异常信息      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;SEVERE: Unable tosetproperty on handler&amp;apos;kafkahandler&amp;apos;(oracle.goldengate.handler.kafka.KafkaHandler). Failed tosetproperty: TopicName:=&amp;quot;test_ogg&amp;quot;(class: oracle.goldengate.handler.kafka.KafkaHandler).             &lt;br /&gt;oracle.goldengate.util.ConfigException: Failed tosetproperty: TopicName:=&amp;quot;test_ogg&amp;quot;(class: oracle.goldengate.handler.kafka.KafkaHandler).             &lt;br /&gt;at ......             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;具体原因是网上的教程是旧版的，设置topicName的属性为:      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;gg.handler.kafkahandler.topicName=test_ogg             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;新版的这样设置      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;gg.handler.kafkahandler.topicMappingTemplate=test_ogg             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;大家可根据自己的版本进行设置，附上stackoverflow原答案      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;I tried to move data from Oracle Database to Kafka using Golden gate adapter Version 12.3.0.1.0             &lt;br /&gt;             &lt;br /&gt;In new version there is no topicname             &lt;br /&gt;             &lt;br /&gt;The following resolves the topic name using the short table name             &lt;br /&gt;gg.handler.kafkahandler.topicMappingTemplate=test             &lt;br /&gt;             &lt;br /&gt;In previous version we have gg.handler.kafkahandler.topicName=test             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#6-3-&amp;#27979;&amp;#35797;&amp;#21516;&amp;#27493;&amp;#26356;&amp;#26032;&amp;#25928;&amp;#26524;" title=""&gt;&lt;/a&gt;6.3 测试同步更新效果&lt;/h3&gt;    &lt;p&gt;现在源端执行sql语句      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;conn test_ogg/test_ogg             &lt;br /&gt;insert into test_ogg values(1,&amp;apos;test&amp;apos;);             &lt;br /&gt;commit;             &lt;br /&gt;update test_oggsetname=&amp;apos;zhangsan&amp;apos;whereid=1;             &lt;br /&gt;commit;             &lt;br /&gt;delete test_oggwhereid=1;             &lt;br /&gt;commit;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;查看源端trail文件状态      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;ls -l /opt/ogg/dirdat/to*             &lt;br /&gt;-rw-rw-rw- 1 oracle oinstall 1464 May 23 10:31 /opt/ogg/dirdat/to000000             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;查看目标端trail文件状态      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;ls -l /opt/ogg/dirdat/to*             &lt;br /&gt;-rw-r----- 1 root root 1504 May 23 10:31 /opt/ogg/dirdat/to000000             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;查看kafka是否自动建立对应的主题      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;bin/kafka-topics.sh --list --zookeeper localhost:2181             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;在列表中显示有test_ogg则表示没问题      &lt;br /&gt;通过消费者看是否有同步消息      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;bin/kafka-console-consumer.sh --bootstrap-server 192.168.44.129:9092 --topic test_ogg --from-beginning             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TEST_OGG.TEST_OGG&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;I&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-23 10:31:28.000078&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-23T10:36:48.525000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000001093&amp;quot;,&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;NAME&amp;quot;:&amp;quot;test&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TEST_OGG.TEST_OGG&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-23 10:31:36.000073&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-23T10:36:48.874000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000001233&amp;quot;,&amp;quot;before&amp;quot;:{},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;NAME&amp;quot;:&amp;quot;zhangsan&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TEST_OGG.TEST_OGG&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;D&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-23 10:31:43.000107&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-23T10:36:48.875000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000001376&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1}}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;显然，Oracle的数据已准实时同步到Kafka,格式为json,其中op_type代表操作类型，这个可配置，我没有配置则按默认的来，默认为      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;gg.handler.kafkahandler.format.insertOpKey = I             &lt;br /&gt;gg.handler.kafkahandler.format.updateOpKey = U             &lt;br /&gt;gg.handler.kafkahandler.format.deleteOpKey = D             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;before代表操作之前的数据，after代表操作后的数据，现在已经可以从kafka获取到同步的json数据了，后面可以用SparkStreaming和Storm等解析然后存到hadoop等大数据平台里&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#6-4-SparkStreaming&amp;#27979;&amp;#35797;&amp;#28040;&amp;#36153;&amp;#21516;&amp;#27493;&amp;#28040;&amp;#24687;" title=""&gt;&lt;/a&gt;6.4 SparkStreaming测试消费同步消息&lt;/h3&gt;    &lt;p&gt;具体代码可参考      &lt;a href="https://dongkelun.com/2018/05/17/sparkKafka/"&gt;Spark Streaming连接Kafka入门教程&lt;/a&gt;      &lt;br /&gt;下面附上消费成功的结果图      &lt;br /&gt;      &lt;img alt="" src="https://tva2.sinaimg.cn/large/e44344dcly1frlosg4v2aj21730oajv9.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#7&amp;#12289;&amp;#26356;&amp;#26032;&amp;#65306;&amp;#21518;&amp;#32493;&amp;#36935;&amp;#21040;&amp;#30340;&amp;#38382;&amp;#39064;" title=""&gt;&lt;/a&gt;7、更新：后续遇到的问题&lt;/h2&gt;    &lt;p&gt;在后面的使用过程中发现上面同步到kafka的json数据中少一些我们想要的一些，下面讲一下我是如何解决的      &lt;br /&gt;首先建表：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;CREATETABLE&amp;quot;TCLOUD&amp;quot;.&amp;quot;T_OGG2&amp;quot;             &lt;br /&gt;(&amp;quot;ID&amp;quot;NUMBER(*,0),             &lt;br /&gt;&amp;quot;TEXT_NAME&amp;quot;VARCHAR2(20),             &lt;br /&gt;&amp;quot;AGE&amp;quot;NUMBER(*,0),             &lt;br /&gt;&amp;quot;ADD&amp;quot;VARCHAR2(100),             &lt;br /&gt;&amp;quot;IDD&amp;quot;VARCHAR2(100),             &lt;br /&gt;CONSTRAINT&amp;quot;T_OGG2_PK&amp;quot;PRIMARYKEY(&amp;quot;ID&amp;quot;,&amp;quot;IDD&amp;quot;)             &lt;br /&gt;             &lt;br /&gt;)             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;为什么不用之前建的表，主要是之前的字段太少，不容易看出问题，现在主要是增加几个字段，然后id,idd是联合主键。      &lt;br /&gt;看一下按照之前的配置，同步到kafka的数据(截取部分数据)      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;I&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 11:46:09.512672&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T11:46:15.292000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000001903&amp;quot;,&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:4,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:0,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;8&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 11:49:10.514549&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T11:49:16.450000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000002227&amp;quot;,&amp;quot;before&amp;quot;:{},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:4,&amp;quot;TEXT_NAME&amp;quot;:&amp;quot;lisi&amp;quot;,&amp;quot;IDD&amp;quot;:&amp;quot;7&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 11:49:48.514869&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T11:49:54.481000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000002373&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:4,&amp;quot;IDD&amp;quot;:&amp;quot;7&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;7&amp;quot;}}             &lt;br /&gt;             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;D&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 11:52:38.516877&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T11:52:45.633000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000003161&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;7&amp;quot;}}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;现在只有insert的数据是全的，update更新非主键字段before是没有数据的，更新主键before只有主键的数据，delete只有before的主键字段，也就是update和delete的信息是不全的，且没有主键信息（程序里是不能判断哪一个是主键的），这样对于程序自动解析同步数据是不利的（不同的需求可能不一样），具体自己可以分析，就不啰嗦了，这里主要解决，有需要before和after全部信息和主键信息的需求。&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#7-1-&amp;#28155;&amp;#21152;before" title=""&gt;&lt;/a&gt;7.1 添加before&lt;/h3&gt;    &lt;p&gt;在源端extract里添加下面几行      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;GGSCI (ambari.master.com) 33&amp;gt; edit param extkafka             &lt;br /&gt;GETUPDATEBEFORES             &lt;br /&gt;NOCOMPRESSDELETES             &lt;br /&gt;NOCOMPRESSUPDATES             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;重启 extkafka      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;stop extkafka             &lt;br /&gt;start extkafka             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;然后测试      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 14:48:55.630340&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T14:49:01.709000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000003770&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:20,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 14:48:55.630340&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T14:49:01.714000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000004009&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:20,&amp;quot;IDD&amp;quot;:&amp;quot;2&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;2&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 14:48:55.630340&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T14:49:01.715000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000004248&amp;quot;,&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:20,&amp;quot;IDD&amp;quot;:&amp;quot;8&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;8&amp;quot;}}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;发现update之后before里有数据即可，但是现在before和after的数据都不全（只有部分字段）&lt;/p&gt;    &lt;p&gt;网上有的说只添加GETUPDATES即可，但我测试了没有成功，关于每个配置项什么含义可以参考      &lt;a href="https://blog.csdn.net/linucle/article/details/13505939" rel="noopener" target="_blank"&gt;https://blog.csdn.net/linucle/article/details/13505939&lt;/a&gt;（有些配置的含义里面也没有给出）      &lt;br /&gt;参考：      &lt;a href="http://www.itpub.net/thread-2083473-1-1.html" rel="noopener" target="_blank"&gt;http://www.itpub.net/thread-2083473-1-1.html&lt;/a&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#7-2-&amp;#28155;&amp;#21152;&amp;#20027;&amp;#38190;" title=""&gt;&lt;/a&gt;7.2 添加主键&lt;/h3&gt;    &lt;p&gt;在kafka.props添加&lt;/p&gt;    &lt;table&gt;      &lt;tr&gt;        &lt;td&gt;          &lt;pre&gt;1            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;        &lt;td&gt;          &lt;pre&gt;gg.handler.kafkahandler.format.includePrimaryKeys=true            &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;    &lt;p&gt;重启 rekafka      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;stop rekafka             &lt;br /&gt;start rekafka             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;测试：      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 14:58:57.637035&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T14:59:03.401000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000004510&amp;quot;,&amp;quot;primary_keys&amp;quot;:[&amp;quot;ID&amp;quot;,&amp;quot;IDD&amp;quot;],&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:1,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;AGE&amp;quot;:20,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;}}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;发现有primary_keys，不错~&lt;/p&gt;    &lt;p&gt;参考：      &lt;a href="http://blog.51cto.com/lyzbg/2088409" rel="noopener" target="_blank"&gt;http://blog.51cto.com/lyzbg/2088409&lt;/a&gt;&lt;/p&gt;    &lt;h3&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#7-3-&amp;#34917;&amp;#20840;&amp;#20840;&amp;#37096;&amp;#23383;&amp;#27573;" title=""&gt;&lt;/a&gt;7.3 补全全部字段&lt;/h3&gt;    &lt;p&gt;如果字段补全应该是Oracle没有开启全列补充日志      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;SQL&amp;gt; select supplemental_log_data_all from v$database;             &lt;br /&gt;             &lt;br /&gt;SUPPLE             &lt;br /&gt;------             &lt;br /&gt;NO             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;通过以下命令开启      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;4             &lt;br /&gt;5             &lt;br /&gt;6             &lt;br /&gt;7             &lt;br /&gt;8             &lt;br /&gt;9             &lt;br /&gt;10             &lt;br /&gt;11             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;SQL&amp;gt; alter database add supplemental log data(all) columns;             &lt;br /&gt;             &lt;br /&gt;Database altered.             &lt;br /&gt;             &lt;br /&gt;SQL&amp;gt; select supplemental_log_data_all from v$database;             &lt;br /&gt;             &lt;br /&gt;SUPPLE             &lt;br /&gt;------             &lt;br /&gt;YES             &lt;br /&gt;             &lt;br /&gt;SQL&amp;gt;             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;测试一下      &lt;br /&gt;   &lt;/p&gt;  &lt;table&gt;       &lt;tr&gt;         &lt;td&gt;           &lt;pre&gt;1             &lt;br /&gt;2             &lt;br /&gt;3             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;         &lt;td&gt;           &lt;pre&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 15:27:45.655518&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T15:27:52.891000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000006070&amp;quot;,&amp;quot;primary_keys&amp;quot;:[&amp;quot;ID&amp;quot;,&amp;quot;IDD&amp;quot;],&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:1,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:20,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;1&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 15:27:45.655518&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T15:27:52.893000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000006341&amp;quot;,&amp;quot;primary_keys&amp;quot;:[&amp;quot;ID&amp;quot;,&amp;quot;IDD&amp;quot;],&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:1,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;2&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:20,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;2&amp;quot;}}             &lt;br /&gt;{&amp;quot;table&amp;quot;:&amp;quot;TCLOUD.T_OGG2&amp;quot;,&amp;quot;op_type&amp;quot;:&amp;quot;U&amp;quot;,&amp;quot;op_ts&amp;quot;:&amp;quot;2018-05-31 15:27:45.655518&amp;quot;,&amp;quot;current_ts&amp;quot;:&amp;quot;2018-05-31T15:27:52.895000&amp;quot;,&amp;quot;pos&amp;quot;:&amp;quot;00000000000000006612&amp;quot;,&amp;quot;primary_keys&amp;quot;:[&amp;quot;ID&amp;quot;,&amp;quot;IDD&amp;quot;],&amp;quot;before&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:1,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;8&amp;quot;},&amp;quot;after&amp;quot;:{&amp;quot;ID&amp;quot;:1,&amp;quot;TEXT_NAME&amp;quot;:null,&amp;quot;AGE&amp;quot;:20,&amp;quot;ADD&amp;quot;:null,&amp;quot;IDD&amp;quot;:&amp;quot;8&amp;quot;}}             &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;    &lt;p&gt;到现在json信息里的内容已经很全了，基本满足了我想要的，附图：      &lt;br /&gt;      &lt;img alt="" src="https://tva1.sinaimg.cn/large/e44344dcly1frunjlwbknj21gn0dw788.jpg"&gt;&lt;/img&gt;      &lt;br /&gt;启发我发现和Oracle全列补充日志没有开启有关的博客：      &lt;a href="https://blog.csdn.net/huoshuyinhua/article/details/79013387" rel="noopener" target="_blank"&gt;https://blog.csdn.net/huoshuyinhua/article/details/79013387&lt;/a&gt;      &lt;br /&gt;开启命令参考：      &lt;a href="https://blog.csdn.net/aaron8219/article/details/16825963" rel="noopener" target="_blank"&gt;https://blog.csdn.net/aaron8219/article/details/16825963&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;注：博客上讲到，开启全列补充日志会导致磁盘快速增长，LGWR进程繁忙，不建议使用。大家可根据自己的情况使用。&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#8&amp;#12289;&amp;#20851;&amp;#20110;&amp;#36890;&amp;#37197;" title=""&gt;&lt;/a&gt;8、关于通配&lt;/h2&gt;    &lt;blockquote&gt;      &lt;p&gt;如果想通配整个库的话，只需要把上面的配置所有表名的改为        &lt;em&gt;，如test_ogg.test_ogg改为 test_ogg.&lt;/em&gt;,但是kafka的topic不能通配，所以需要把所有的表的数据放在一个topic即可，后面再用程序解析表名即可。&lt;/p&gt;&lt;/blockquote&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#9&amp;#12289;&amp;#38468;&amp;#24405;" title=""&gt;&lt;/a&gt;9、附录&lt;/h2&gt;    &lt;p&gt;目标端在      &lt;a href="http://www.oracle.com/technetwork/middleware/goldengate/downloads/index.html" rel="noopener" target="_blank"&gt;这里&lt;/a&gt;，下载下来后文件名123111_ggs_Adapters_Linux_x64.zip&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://tva3.sinaimg.cn/large/e44344dcly1frlon533rgj217c0gg433.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;源端在      &lt;a href="https://edelivery.oracle.com/osdc/faces/SoftwareDelivery" rel="noopener" target="_blank"&gt;旧版本&lt;/a&gt;查询下载，下载后文件名为V34339-01.zip&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://tva3.sinaimg.cn/large/e44344dcly1frlon362fcj219o0fp40t.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://tva4.sinaimg.cn/large/e44344dcly1frloqscpk2j217h0bn3zh.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://tva4.sinaimg.cn/large/e44344dcly1frlon3njt7j21cp0l1add.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;      &lt;img alt="" src="https://tva4.sinaimg.cn/large/e44344dcly1frlon47xfij21fm0e6abb.jpg"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;h2&gt;      &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/#&amp;#21442;&amp;#32771;&amp;#36164;&amp;#26009;" title=""&gt;&lt;/a&gt;参考资料&lt;/h2&gt;    &lt;p&gt;      &lt;a href="https://www.cnblogs.com/purpleraintear/p/6071038.html" rel="noopener" target="_blank"&gt;基于OGG的Oracle与Hadoop集群准实时同步介绍&lt;/a&gt;&lt;/p&gt;    &lt;div&gt;      &lt;a href="javascript:;"&gt;        &lt;div&gt;赏          &lt;p&gt;            &lt;em&gt;&lt;/em&gt;感谢您的支持!            &lt;em&gt;&lt;/em&gt;&lt;/p&gt;          &lt;div&gt;            &lt;div&gt;              &lt;img src="https://dongkelun.com/img/alipay.png"&gt;&lt;/img&gt;支付宝&lt;/div&gt;            &lt;div&gt;              &lt;img src="https://dongkelun.com/img/weixin.png"&gt;&lt;/img&gt;微信&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;    &lt;div&gt;      &lt;p&gt;本文由         &lt;strong&gt;董可伦&lt;/strong&gt; 发表于         &lt;a href="http://dongkelun.com"&gt;伦少的博客&lt;/a&gt; ,采用        &lt;a href="https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh"&gt;署名-非商业性使用-禁止演绎 3.0&lt;/a&gt;进行许可。&lt;/p&gt;      &lt;p&gt;非商业转载请注明作者及出处。商业转载请联系作者本人。&lt;/p&gt;      &lt;p&gt;本文标题：        &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/"&gt;利用ogg实现oracle到kafka的增量数据实时同步&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;本文链接：        &lt;a href="https://dongkelun.com/2018/05/23/oggOracle2Kafka/"&gt;https://dongkelun.com/2018/05/23/oggOracle2Kafka/&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;    &lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60332-%E5%88%A9%E7%94%A8-ogg-oracle</guid>
      <pubDate>Sat, 01 Feb 2020 17:34:04 CST</pubDate>
    </item>
    <item>
      <title>基于OGG的Oracle与Hadoop集群准实时同步介绍 - 偶素浅小浅 - 博客园</title>
      <link>https://itindex.net/detail/60331-ogg-oracle-hadoop</link>
      <description>&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/60331-ogg-oracle-hadoop</guid>
      <pubDate>Sat, 01 Feb 2020 17:33:27 CST</pubDate>
    </item>
    <item>
      <title>MySQL 同步复制及高可用方案总结</title>
      <link>https://itindex.net/detail/60091-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</link>
      <description>&lt;h3&gt;1.前言&lt;/h3&gt;
 &lt;p&gt;mysql作为应用程序的数据存储服务，要实现mysql数据库的高可用。必然要使用的技术就是数据库的复制，如果主节点出现故障可以手动的切换应用到从节点，这点相信运维同学都是知道，并且可以实现的。但是这种情况只是手动的切换，对可用性有要求的业务需要分别实现主库和从库的高可用，保障在数据库出现down机的情况下，可以自动实现数据库的故障转移，保障应用的可用性和用户体验。&lt;/p&gt;
 &lt;p&gt;本文将会对一些常用的数据库高可用方案进行介绍，根据你不同的场景，选择合适的高可用方案即可。&lt;/p&gt;
 &lt;h3&gt;2.MMM高可用方案&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;2.1.Mysql-MMM介绍&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MMM(Master-Master replication managerfor Mysql，Mysql主主复制管理器)是一套灵活的脚本程序，基于perl实现，用来对mysql replication进行监控和故障迁移，并能管理mysql Master-Master复制的配置(同一时间只有一个节点是可写的)。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.2.组件&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;mmm_mond：监控进程，负责所有的监控工作，决定和处理所有节点角色活动。此脚本需要在监管机上运行。&lt;/p&gt;
 &lt;p&gt;mmm_agentd：运行在每个mysql服务器上的代理进程，完成监控的探针工作和执行简单的远端服务设置。此脚本需要在被监管机上运行。&lt;/p&gt;
 &lt;p&gt;mmm_control：一个简单的脚本，提供管理mmm_mond进程的命令。&lt;/p&gt;
 &lt;p&gt;mysql-mmm的监管端会提供多个虚拟IP（VIP），包括一个可写VIP，多个可读VIP，通过监管的管理，这些IP会绑定在可用mysql之上，当某一台mysql宕机时，监管会将VIP迁移至其他mysql。&lt;/p&gt;
 &lt;p&gt;在整个监管过程中，需要在mysql中添加相关授权用户，以便让mysql可以支持监理机的维护。授权的用户包括一个mmm_monitor用户和一个mmm_agent用户，如果想使用mmm的备份工具则还要添加一个mmm_tools用户。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.3.架构图&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;正常工作时：  &lt;br /&gt;  &lt;img alt="" src="https://segmentfault.com/img/remote/1460000020693591?w=605&amp;h=461" title=""&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主节点故障时：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154036.png" src="https://segmentfault.com/img/bVbyZvD" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154036.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.4.MMM优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）高可用性，扩展性好，出现故障自动转移，对于主主同步，在同一时间只提供一台数据库写操作，保证数据的一致性。  &lt;br /&gt;（2）配置简单，容易操作。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;2.5.MMM缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源  &lt;br /&gt;（2）需要多个虚拟IP  &lt;br /&gt;（3）agent可能意外终止，引起裂脑。&lt;/p&gt;
 &lt;h3&gt;3.MHA介绍&lt;/h3&gt;
 &lt;p&gt;MHA（Master High Availability）目前在MySQL高可用方面是一个相对成熟的解决方案，它由日本DeNA公司youshimaton（现就职于Facebook公司）开发，是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。在MySQL故障切换过程中，MHA能做到在0~30秒之内自动完成数据库的故障切换操作，并且在进行故障切换的过程中，MHA能在最大程度上保证数据的一致性，以达到真正意义上的高可用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.1.MHA架构介绍&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;该软件由两部分组成：MHA Manager（管理节点）和MHA Node（数据节点）。MHA Manager可以单独部署在一台独立的机器上管理多个master-slave集群，也可以部署在一台slave节点上。MHA Node运行在每台MySQL服务器上，MHA Manager会定时探测集群中的master节点，当master出现故障时，它可以自动将最新数据的slave提升为新的master，然后将所有其他的slave重新指向新的master。整个故障转移过程对应用程序完全透明。&lt;/p&gt;
 &lt;p&gt;在MHA自动故障切换过程中，MHA试图从宕机的主服务器上保存二进制日志，最大程度的保证数据的不丢失(配合mysql半同步复制效果更佳)，但这并不总是可行的。例如，如果主服务器硬件故障或无法通过ssh访问，MHA没法保存二进制日志，只进行故障转移而丢失了最新的数据。使用MySQL 5.5的半同步复制，可以大大降低数据丢失的风险。MHA可以与半同步复制结合起来。如果只有一个slave已经收到了最新的二进制日志，MHA可以将最新的二进制日志应用于其他所有的slave服务器上，因此可以保证所有节点的数据一致性。&lt;/p&gt;
 &lt;p&gt;注意：目前MHA主要支持一主多从的架构，要搭建MHA,要求一个复制集群中必须最少有三台数据库服务器，一主二从，即一台充当master，一台充当备用master，另外一台充当从库，因为至少需要三台服务器，出于机器成本的考虑，淘宝也在该基础上进行了改造，目前淘宝TMHA已经支持一主一从。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.2.MHA架构图&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;正常工作时架构图：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154220.png" src="https://segmentfault.com/img/bVbyZyh" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154220.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主库down机时架构：  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154249.png" src="https://segmentfault.com/img/bVbyZym" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154249.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.3.故障转移过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）从宕机崩溃的master保存二进制日志事件（binlog events）;  &lt;br /&gt;（2）识别含有最新更新的slave；  &lt;br /&gt;（3）应用差异的中继日志（relay log）到其他的slave；  &lt;br /&gt;（4）应用从master保存的二进制日志事件（binlog events）；  &lt;br /&gt;（5）提升一个slave为新的master；  &lt;br /&gt;（6）使其他的slave连接新的master进行复制；  &lt;br /&gt;（7）在新的master启动vip地址，保证前端请求可以发送到新的master。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.4.MHA优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）不需要备份服务器  &lt;br /&gt;（2）不改变现有环境  &lt;br /&gt;（3）操作非常简单  &lt;br /&gt;（4）可以进行日志的差异修复  &lt;br /&gt;（5）可以将任意slave提升为master&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;3.5.MHA缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要全部节点做ssh秘钥  &lt;br /&gt;（2）MHA出现故障后配置文件会被修改，如果再次故障转移需要重新修改配置文件。  &lt;br /&gt;（3）自带的脚本还需要进一步补充完善，且用perl开发，二次开发困难。&lt;/p&gt;
 &lt;h3&gt;4.DRBD+（heartbeat,corosync）&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;4.1.方案简介&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;本方案采用Heartbeat或者corosync双机热备软件来保证数据库的高稳定性和连续性，数据的一致性由DRBD这个工具来保证（如果可以尽量放到分布式存储上面）。默认情况下只有一台mysql在工作，当主mysql服务器出现问题后，系统将自动切换到备机上继续提供服务，当主数据库修复完毕，又将服务切回继续由主mysql提供服务。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.2.组件&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Heartbeat,corosync作为心跳检测机制，监控primary节点的状态。当主节点宕掉之后，迅速提升secondary节点为新的主节点，并切换IP；  &lt;br /&gt;drbd负责数据同步&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.3.架构图&lt;/strong&gt;  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154253.jpg" src="https://segmentfault.com/img/bVbyZyr" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154253.jpg"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.4.数据同步过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;mysql进行刷盘时，会通过不同的sync方式，最终将数据写入disk；  &lt;br /&gt;drbd收到刷盘成功的信息后，将对应的磁盘块位置，和变更动作，通过网络传递至secondary节点；&lt;/p&gt;
 &lt;p&gt;secondary的drbd接收到变更信息后，将这些信息落盘；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.5.切换过程&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;前提：secondary节点的mysql服务不启动；&lt;/p&gt;
 &lt;p&gt;heartbeat检测到primary的mysql服务停止，则摘掉IP、umount掉数据盘、将primary切换为secondary；&lt;/p&gt;
 &lt;p&gt;在原来的secondary上，提升drbd同步为primary，挂载数据盘，启动mysql服务、绑定IP；&lt;/p&gt;
 &lt;p&gt;从库跟着IP和端口自动进行迁移；&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.6.方案优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）历史悠久、安全性高、稳定性高、可用性高、出现故障自动切换。  &lt;br /&gt;（2）数据一致性强&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;4.7.方案缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）需要一台备份服务器，浪费资源  &lt;br /&gt;（2）不方便扩展  &lt;br /&gt;（3）无论drbd还是headbetart，corosync都可能发生裂脑&lt;/p&gt;
 &lt;h3&gt;5.Mysql route介绍&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;5.1.什么是mysql route&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router是处于应用client和dbserver之间的轻量级代理程序，它能检测，分析和转发查询到后端数据库实例，并把结果返回给client。是mysql-proxy的一个替代品。其架构图和功能如下。  &lt;br /&gt;  &lt;img alt="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154257.png" src="https://segmentfault.com/img/bVbyZyx" title="&amp;#24494;&amp;#20449;&amp;#22270;&amp;#29255;_20191015154257.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;（1）Router实现读写分离，程序不是直接连接数据库IP，而是固定连接到mysql router。MySQL Router对前端应用是透明的。应用程序把MySQL Router当作是普通的mysql实例，把查询发给MySQL Router,而MySQL Router会把查询结果返回给前端的应用程序。&lt;/p&gt;
 &lt;p&gt;（2）从数据库服务器故障，业务可以正常运行。由MySQL Router来进行自动下线不可用服务器。程序配置不需要任何修改。&lt;/p&gt;
 &lt;p&gt;（3）主数据库故障，由MySQL Router来决定主从自动切换，业务可以正常访问。程序配置不需要做任何修改。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.2.读写分离原理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router接受前端应用程序请求后，根据不同的端口来区分读写，把连接读写端口的所有查询发往主库，把连接只读端口的select查询以轮询方式发往多个从库，从而实现读写分离的目的。读写返回的结果会交给MySQL Router,由MySQL Router返回给客户端的应用程序。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.3.Mysql router用途&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;MySQL Router的主要用途是读写分离，主主故障自动切换，负载均衡，连接池等。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.4.Mysql router主主故障自动切换的坑&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Mysql router主主故障切换功能经过测试没有问题，但是有一个比较大的坑需要注意，主库发生切换之后，从库的连接的master服务器地址不会发生改变，需要自己写脚本进行判断。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.5.优点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）基于DAL层实现mysql的高可用。  &lt;br /&gt;（2）可以同时实现主主故障切换和读写分离。  &lt;br /&gt;（3）插件式架构允许用户进行额外的功能扩展。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;5.6.缺点&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;（1）高可用功能需要进一步完善：存在主库切换之后，从库不会自动切换主库地址的坑。  &lt;br /&gt;（2）读写情况使用不同端口，需要修改应用程序。&lt;/p&gt;
 &lt;h3&gt;6.mysql Cluster&lt;/h3&gt;
 &lt;p&gt;国内用的非常少，主要因为一下三点：  &lt;br /&gt;（1）需要更改存储引擎  &lt;br /&gt;（2）付费  &lt;br /&gt;（3）国内几乎没有使用案例&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;优点：&lt;/strong&gt;  &lt;br /&gt;高可用，可用率达99.999%&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;6.1.结束语&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;上面的高可用方案，只是我自己比较熟悉的，而且也是应用比较多的。mysql毕竟发展了有20多年了，各种高可用方案还是很多的，其他的高可用方案各位钥匙有兴趣，可以自己研究。&lt;/p&gt;
 &lt;blockquote&gt;版权申明：作者：西门飞冰，一名90后it男，一直在北京工作，热爱运动，热爱冒险，热爱旅行。由作者原创投稿，版权归原创者所有。除非无法确认，我们都会标明作者及出处，如有侵权烦请告知，我们会立即删除并表示歉意，谢谢。&lt;/blockquote&gt;
 &lt;p&gt;关注 民工哥技术之路 微信公众号对话框回复关键字：1024 可以获取一份最新整理的技术干货：包括系统运维、数据库、redis、MogoDB、电子书、Java基础课程、Java实战项目、架构师综合教程、架构师实战项目、大数据、Docker容器、ELK Stack、机器学习、BAT面试精讲视频等。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>mysql linux centos</category>
      <guid isPermaLink="true">https://itindex.net/detail/60091-mysql-%E5%90%8C%E6%AD%A5-%E5%A4%8D%E5%88%B6</guid>
      <pubDate>Tue, 15 Oct 2019 15:41:19 CST</pubDate>
    </item>
    <item>
      <title>基于datax的数据同步平台 - 黄小雪 - 博客园</title>
      <link>https://itindex.net/detail/59987-datax-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</link>
      <description>&lt;p&gt;  &lt;strong&gt;一、需求&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;         由于公司各个部门对业务数据的需求，比如进行数据分析、报表展示等等，且公司没有相应的系统、数据仓库满足这些需求，最原始的办法就是把数据提取出来生成excel表发给各个部门，这个功能已经由脚本转成了平台，交给了DBA使用，而有些数据分析部门，则需要运维把生产库的数据同步到他们自己的库，并且需要对数据进行脱敏，比如客户的身份证号、手机号等等，且数据来源分散在不同的机器，不同的数据库实例里，这样就无法使用MySQL的多源复制，只能用写脚本通过SQL语句实现，随着业务的发展，导致堆积到运维部门的同步数据任务越来越多，一个任务对应一个脚本，有的脚本多达20多张表，脚本超过10个以后，每次同步失败、或者对脚本里的参数进行增删改查，都要从10多个脚本里的10多个SQL去找，这是一件非常痛苦的事情，耗费时间、没有效率，且容易改错，是一件吃力不讨好的事。为此开发了一个数据同步平台，将同步任务的增删改查、执行的历史日志全部放到平台里，然后交给DBA去自己去操作。&lt;/p&gt; &lt;p&gt;         市面上也有一些ETL工具，比如kettle，但是为了练手决定重新造轮子。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;二、平台简介&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;          平台主要用于数据同步、数据处理等等ETL操作。&lt;/p&gt; &lt;p&gt;          平台基于阿里的开源同步工具datax3.0开发。&lt;/p&gt; &lt;p&gt;          开发语言：Python、Django、celery、bootstrap、jquery&lt;/p&gt; &lt;p&gt;          系统：Centos 7  64位&lt;/p&gt; &lt;p&gt;          注意：时间紧迫，平台只支持MySQL数据库，其它的sqlserver等等后期再开发。&lt;/p&gt; &lt;p&gt;          datax3.0 介绍：  &lt;a href="https://yq.aliyun.com/articles/59373"&gt;https://yq.aliyun.com/articles/59373&lt;/a&gt;&lt;/p&gt; &lt;p&gt;          datax3.0 github 地址：  &lt;a href="https://github.com/alibaba/DataX"&gt;https://github.com/alibaba/DataX&lt;/a&gt;&lt;/p&gt; &lt;p&gt;          项目地址：  &lt;a href="https://github.com/hanson007/FirstBlood" target="_blank"&gt;https://github.com/hanson007/FirstBlood&lt;/a&gt;  &lt;br /&gt;&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;三、功能模块&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;         1、数据同步&lt;/p&gt; &lt;p&gt;               主要用于数据同步&lt;/p&gt; &lt;p&gt;         2、SQL脚本（后期开发，包括备份模块等等。）&lt;/p&gt; &lt;p&gt;               保存并执行各种增删改查SQL语句。&lt;/p&gt; &lt;p&gt;         3、批处理作业&lt;/p&gt; &lt;p&gt;               将数据同步、SQL脚本等等各个模块的子任务组合成一个批处理作业。借鉴了数据库客户端工具Navicat Premium 的批处理作业功能。&lt;/p&gt; &lt;p&gt;               支持作业定时调度。&lt;/p&gt; &lt;p&gt;         4、数据库管理工具（web界面后期开发）&lt;/p&gt; &lt;p&gt;              主要用于管理生产数据库的IP、用户名、密码等等信息，供其它模块调用。&lt;/p&gt; &lt;p&gt;              目前模块的表已建好，生产库的信息需要通过其它平台同步或者用数据库客户端工具导入，web界面的增删改查后期开发。目前生产环境里是将其它平台保存的所有生产库IP、用户名、密码等等信息同步到此平台里。&lt;/p&gt; &lt;p&gt;         5、接口&lt;/p&gt; &lt;p&gt;               提供查询批处理作业执行历史的接口，供其它部门使用。（主要还是大数据部门，他们写了一个程序，根据我这边每次同步后的结果，是成功还是失败，再进行下一步的操作。）&lt;/p&gt; &lt;p&gt;               后续接口按业务部门的需求再开发。&lt;/p&gt; &lt;p&gt;         6、权限（Django自带）&lt;/p&gt; &lt;p&gt;               平台管理员账号拥有模块的所有权限，仅供运维部门使用。&lt;/p&gt; &lt;p&gt;               普通人员账号只能查看数据同步、批处理作业，以及执行历史，不能新增、修改、执行作业或任务。主要提供给业务部门使用。&lt;/p&gt; &lt;p&gt;               查看批处理作业的执行历史接口没有权限控制，普通人员也能调用。&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;四、表结构设计&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;          1、生产数据库信息&lt;/p&gt; &lt;p&gt;                功能：主要用于保存各种生产库的 ip、用户名、密码等等信息。&lt;/p&gt; &lt;p&gt;                表名：databaseinfo&lt;/p&gt; &lt;p&gt;           &lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空、不允许重复&lt;/td&gt;   &lt;td&gt;生产库英文标识。&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;生产库的业务信息描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;host&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空、不允许重复&lt;/td&gt;   &lt;td&gt;生产库的IP地址。&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;user&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;生产数据库的用户名&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;passwd&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;生产数据库的密码&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;db&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;生产数据库中的某一个库&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;type&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;生产数据库类型。 比如MySQL、sqlserver&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;create_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;创建时间，默认为当前时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;modify_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;修改时间，默认为当前时间，数据变化时自动改为当前时间。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;          2.数据库同步任务&lt;/p&gt; &lt;p&gt;            功能：用于保存数据库同步任务的各种参数，主要为datax的json配置文件里的各种参数。&lt;/p&gt; &lt;p&gt;            表名：datax_job&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空，不允许重复&lt;/td&gt;   &lt;td&gt;数据同步任务的英文标识&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;任务的详细描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;querySql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;提取数据时的查询SQL&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;reader_databaseinfo_id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;读取数据库（从哪个生产库执行SQL提取数据，对应databaseinfo表的主键）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_table&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;写入表名（提取的数据插入到哪张表里）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_databaseinfo_id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;写入数据库（提数据的数据插入到哪个数据库里）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_preSql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;允许为空&lt;/td&gt;   &lt;td&gt;写入前执行的SQL（比如同步数据前需要清空写入的表）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_postSql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;允许为空&lt;/td&gt;   &lt;td&gt;写入后执行的SQL（比如同步完数据后需要再结合其它表执行数据分析）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;create_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;创建时间，默认为当前时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;modify-time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;修改时间，默认为当前时间，数据变化时自动改为当前时间。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;        3.写入表的列信息&lt;/p&gt; &lt;p&gt;          功能：保存同步任务时写入到表的哪些列。比如写入表有20个字段，此时只需要往其中的10个字段写入信息，就需要保存这10个列名。&lt;/p&gt; &lt;p&gt;                     注意：* 星号代码写入到表的所有字段。&lt;/p&gt; &lt;p&gt;          表名：datax_job_writer_column&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;列名&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;datax_job_id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;数据同步任务ID，关联datax_job表的主键。&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;create_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;创建时间，默认为当前时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;modify_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;修改时间，默认为当前时间，随着数据的变化而变为当前时间。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;       4.数据同步任务实例&lt;/p&gt; &lt;p&gt;          功能：用于保存数据同步任务的执行历史。&lt;/p&gt; &lt;p&gt;                    方便自己及业务部门进行任务的分析和排错，省的每次同步失败后还得帮他们查日志。现在直接将日志记录表里，在平台开个账号后，让业务部门自己去查。&lt;/p&gt; &lt;p&gt;                    每一个数据同步任务执行后，可以看成是一个实例，类似面向对象里实例化。将任务的执行时间、执行结果等等保存起来。借鉴了腾讯蓝鲸的作业平台表结构设计思想。（麻花藤啊麻花藤，给你冲了几十年的点卡，终于是回了一点点利息。）&lt;/p&gt; &lt;p&gt;          表名：datax_job_instance&lt;/p&gt; &lt;p&gt;          说明：instance_id也对应datax生成的日志文件名，当需要在页面查看datax生成的日志时就通过instance_id去查找日志文件，并将其实时输出到页面。&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明 &lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空 &lt;/td&gt;   &lt;td&gt;自增主键 &lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;instance_id &lt;/td&gt;   &lt;td&gt;bigint&lt;/td&gt;   &lt;td&gt;任务实例ID ,不允许重复&lt;/td&gt;   &lt;td&gt;任务实例ID（由datax_job的id号+13位时间戳组成）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;任务名称 （执行时，datax_job表的name，同下面的字段一样） &lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;任务描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;querySql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;不允许为空 &lt;/td&gt;   &lt;td&gt;查询SQL语句&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;reader_databaseinfo_host&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;读取数据库IP&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;reader_databaseinfo_description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;读取数据库描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_table&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;写入表&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_databaseinfo_host&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;写入数据库IP&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_databaseinfo_description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;写入数据库描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_preSql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;允许为空&lt;/td&gt;   &lt;td&gt;写入数据前执行的SQL语句&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;writer_postSql&lt;/td&gt;   &lt;td&gt;longtext&lt;/td&gt;   &lt;td&gt;允许为空&lt;/td&gt;   &lt;td&gt;写入数据后执行的SQL语句&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;trigger_mode&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;触发模式 1 自动 2 手动（默认自动）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;status&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;状态 0 正在执行 1 执行完成&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;result&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;执行结果 0 成功 1 失败 2 未知&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;start_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;开始时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;end_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;允许为空&lt;/td&gt;   &lt;td&gt;结束时间&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;        5.批处理作业&lt;/p&gt; &lt;p&gt;         功能：保存批处理作业。&lt;/p&gt; &lt;p&gt;         表名：batch_job&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空，不允许重复&lt;/td&gt;   &lt;td&gt;名称&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;create_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;创建时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;modify_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;修改时间&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;        6.批处理作业详情&lt;/p&gt; &lt;p&gt;         功能：保存批处理作业的各个子任务。&lt;/p&gt; &lt;p&gt;                    比如一个批处理作业包含8个数据同步任务，一个SQL脚本任务，则将这几个任务的id保存起来。&lt;/p&gt; &lt;p&gt;         表名：batch_job_details&lt;/p&gt; &lt;p&gt;         说明：字段subjob_id，对应其它子任务的ID。比如，类型为数据同步，则对应datax_job表的主键。类型为SQL脚本，则对应SQL脚本表的主键。（SQL脚本后期开发）&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;batch_job_id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;批处理作业ID，对应batch_job表的主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;subjob_id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;子作业ID，对应其它子任务的主键。&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;type&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;类型 1 数据同步 2 SQL脚本 3 备份。 主要用于后期扩展&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;create_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;创建时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;modify_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;修改时间&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;        7.批处理作业执行实例&lt;/p&gt; &lt;p&gt;        功能：保存批处理作业的执行历史日志。功能同数据同步实例一样。&lt;/p&gt; &lt;p&gt;        表名：batch_job_instance&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;instance_id&lt;/td&gt;   &lt;td&gt;bigint&lt;/td&gt;   &lt;td&gt;不允许为空、不允许重复&lt;/td&gt;   &lt;td&gt;实例ID（由batch_job表的id号+13位时间戳组成）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;name&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;名称&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;description&lt;/td&gt;   &lt;td&gt;varchar&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;描述&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;trigger_mode&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;触发模式 1 自动 2 手动（默认自动）&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;status&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;状态 0 正在执行 1 执行完成&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;result&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;执行结果 0 成功 1 失败 2 未知&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;start_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;开始时间&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;end_time&lt;/td&gt;   &lt;td&gt;datetime&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;结束时间&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;         8.批处理作业执行实例详情&lt;/p&gt; &lt;p&gt;         功能：保存批处理作业执行实例的各个子任务实例&lt;/p&gt; &lt;p&gt;         表名：batch_job_instance_details&lt;/p&gt; &lt;p&gt;         说明：每个批处理作业执行时，实际是执行各个其它功能模块的子任务，而每个子任务都会保存子任务实例ID。&lt;/p&gt; &lt;p&gt;                   比如一个批处理作业有8个数据同步任务，1个备份任务（后期开发），执行后，datax_job_instance表会保存这8个数据同步任务的实例，备份实例表则保存备份实例ID。然后再将8个同步任务实例的ID及1个备份实例ID保存到batch_job_instance_details表里，查询时只要通过各个子任务的实例ID关联查询。&lt;/p&gt; &lt;table border="0"&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;strong&gt;名称&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;约束条件&lt;/strong&gt;&lt;/td&gt;   &lt;td&gt;    &lt;strong&gt;说明&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;id&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;自增主键&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;instance_id&lt;/td&gt;   &lt;td&gt;bigint&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;实例ID，对应batch_job_instance表的instance_id&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;subjob_instance_id&lt;/td&gt;   &lt;td&gt;bigint&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;子作业实例ID，比如datax_job_instance表的instance_id&lt;/td&gt;&lt;/tr&gt;  &lt;tr&gt;   &lt;td&gt;type&lt;/td&gt;   &lt;td&gt;int&lt;/td&gt;   &lt;td&gt;不允许为空&lt;/td&gt;   &lt;td&gt;类型 1 数据同步 2 SQL脚本 3 备份。 主要用于后期扩展&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;  &lt;strong&gt;      &lt;/strong&gt;9.建表语句&lt;/p&gt; &lt;p&gt;          &lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;a href="https://www.cnblogs.com/huangxiaoxue/p/9392817.html#"&gt;+ View Code&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;　　&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;  &lt;strong&gt;五、功能详解&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;2、数据同步&lt;/p&gt; &lt;p&gt;            功能：底层使用阿里的datax3.0工具进行同步。可以新增、修改同步任务。每个任务对应一张表。在页面添加任务后，执行时就在后台生成基于datax3.0的json配置文件。并且可以实时查看datax生成的同步日志，也可以查看任务的执行历史。&lt;/p&gt; &lt;p&gt; 　　              衍生：增量同步&lt;/p&gt; &lt;p&gt;                                 需要源表里增加时间戳字段，两种方案。&lt;/p&gt; &lt;p&gt;                              （1）如果历史数据不变，每次只同步前一天的数据。&lt;/p&gt; &lt;p&gt;                              （2）如果历史数据变化，需要在目标库里加一张临时表，每次同步时将前一天或前一个小时的时间戳有变化的数据插入到临时表里。再将临时表里的数据更新或插入到目标表里。&lt;/p&gt; &lt;p&gt;            操作&lt;/p&gt; &lt;p&gt;                （1）首页           &lt;/p&gt; &lt;p&gt;                         点击“数据同步-&amp;gt;作业”，进入数据同步首页，可以查看所有的数据同步任务&lt;/p&gt; &lt;p&gt;   &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730223124820-1628595868.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;               （2）新增同步任务&lt;/p&gt; &lt;p&gt;                        点击首页的“新增”按钮，进入新增任务页面，填完表单后点击保存。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730223417734-1660302919.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;（3）     更新、运行同步任务&lt;/p&gt; &lt;p&gt;              在数据同步首页点击“任务名称”，进入任务更新页面。可以对任务的SQL、数据库等等信息进行修改。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730224030753-1076962925.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730224014909-1406784001.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（1）     执行任务&lt;/p&gt; &lt;p&gt;              在更新页面点击“Run”按钮，可以执行任务。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730224230118-2025386072.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（1）执行历史&lt;/p&gt; &lt;p&gt;点击“数据同步-&amp;gt;执行历史”，在执行历史首页可以查看数据同步任务的执行历史，并且可以按照任务名称、描述、读取数据库、执行状态等等进行搜索。&lt;/p&gt; &lt;p&gt;衍生：由于执行历史是一个日志记录，随着时间推移，数据量会越来越多，为了减小平台数据库的压力，按照业务量大小可以只保存一年、或者半年的数据。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730224706969-1147968459.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（1）同步日志&lt;/p&gt; &lt;p&gt;在执行历史首页点击“任务名称”，可以实时查看同步日志。&lt;/p&gt; &lt;p&gt;日志是由工具datax生成的日志文件，文件名为执行时任务的ID号+13位时间戳组成。平台只保存文件名，查看日志时，后台通过文件名将日志文件内容实时输出到页面。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730224916108-948037361.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730225044955-637558237.png"&gt;&lt;/img&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730225243280-1304365452.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;2.批处理作业&lt;/p&gt; &lt;p&gt;   功能描述：&lt;/p&gt; &lt;p&gt;         将数据同步、SQL脚本（3.0版本后期开发）等等子任务组合成一个批处理作业，并发执行。并且支持linux crontab格式的定时执行。&lt;/p&gt; &lt;p&gt;         时间紧迫，暂时不支持任务串行，或者任务之间的依赖，比如A执行完成，并且成功后才能执行B，类似功能后期3.0版本开发。&lt;/p&gt; &lt;p&gt;  操作&lt;/p&gt; &lt;p&gt;（1）批处理作业首页&lt;/p&gt; &lt;p&gt;          点击“批处理作业-&amp;gt;作业列表”，进入批处理作业首页&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730225547448-672379285.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（2）新增批处理作业&lt;/p&gt; &lt;p&gt;点击“新增”按钮，进入新增批处理作业页面。&lt;/p&gt; &lt;p&gt;选择“执行时间”、勾选“是否启用”等等参数，填好表单后点击保存。后台会根据执行时间自动执行。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730225740513-762130925.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（3）更新、运行批处理作业&lt;/p&gt; &lt;p&gt;在批处理作业首页点击“任务名称”后，进入更新页面，可以修改批处理作业参数。&lt;/p&gt; &lt;p&gt;点击“Save”按钮，保存更新后的批处理作业。&lt;/p&gt; &lt;p&gt;在更新页面点击“Run”按钮可手动执行批处理作业。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730225937885-1976416114.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730230103256-810624801.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730230145957-1956000252.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（4）执行历史&lt;/p&gt; &lt;p&gt;点击“批处理作业-&amp;gt;执行历史”，即可进入批处理作业 - 执行历史首页。&lt;/p&gt; &lt;p&gt;可以按照任务名称、执行结果等等搜索历史的执行作业。&lt;/p&gt; &lt;p&gt;点击“任务名称”进入批处理作业详情 - 执行历史，可查看批处理作业执行时它的子任务。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730230704077-153859573.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730231112912-1545516201.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;（5）执行日志&lt;/p&gt; &lt;p&gt;在“批处理作业详情 - 执行历史”页面，点击“任务名称”可查看每个子任务的日志。如类型为数据同步的子任务，它的日志就是调的datax的日志文件内容。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730231241295-2037703384.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://images2018.cnblogs.com/blog/1036791/201807/1036791-20180730231416342-1327591333.png"&gt;&lt;/img&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/59987-datax-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Wed, 28 Aug 2019 22:14:23 CST</pubDate>
    </item>
    <item>
      <title>datax 3.0配合crontab实现数据定时增量同步</title>
      <link>https://itindex.net/detail/59965-datax-crontab-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;p&gt;使用datax 实现数据增量同步踩坑记录&lt;/p&gt; &lt;hr&gt;&lt;/hr&gt; &lt;h2&gt;  &lt;a name="t0"&gt;&lt;/a&gt;前提概要&lt;/h2&gt; &lt;p&gt;由于项目上需要将a服务器数据同步至b服务器，一开始使用mysql主从复制，但是由于主从同步无法触发位于b服务器的触发器，只能放弃此方案。后来找到了datax可以实现数据同步同时也可以触发触发器，决定使用此方案。&lt;/p&gt; &lt;h2&gt;  &lt;a name="t1"&gt;&lt;/a&gt;datax准备&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;安装datax，python，jdk    &lt;a href="https://github.com/alibaba/DataX/blob/master/userGuid.md" rel="nofollow"&gt;datax下载及安装地址&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;根据自身需求选择合适的writer和reader，我这里选择的是mysqlwriter和mysqlreader&lt;/li&gt;  &lt;li&gt;下面是我使用的json文件，有两点需要注意    &lt;br /&gt;   &lt;ol&gt;    &lt;li&gt;我在 where 使用了sql 语句      &lt;code&gt;create_time &amp;gt; FROM_UNIXTIME(${create_time}) and create_time &amp;lt; FROM_UNIXTIME(${end_time})&lt;/code&gt; ，其中FROM_UNIXTIME()是mysql时间戳转换为时间格式的函数，${name}是datax提供的占位符后面会使用到&lt;/li&gt;    &lt;li&gt;reader中连接字符串添加了     &lt;code&gt;useUnicode=true&amp;amp;characterEncoding=utf8&lt;/code&gt; ，因为没有加这个导入到目标数据库中文乱码了，虽然我两边的数据库都是utf8mb4格式的&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ul&gt; &lt;pre&gt;  &lt;code&gt; {
   &amp;quot;job&amp;quot;: {
     &amp;quot;setting&amp;quot;: {
       &amp;quot;speed&amp;quot;: {
         &amp;quot;channel&amp;quot;: 3
       },
       &amp;quot;errorLimit&amp;quot;: {
         &amp;quot;record&amp;quot;: 0,
         &amp;quot;percentage&amp;quot;: 0.02
       }
     },
     &amp;quot;content&amp;quot;: [
       {
         &amp;quot;reader&amp;quot;: {
           &amp;quot;name&amp;quot;: &amp;quot;mysqlreader&amp;quot;,
           &amp;quot;parameter&amp;quot;: {
             &amp;quot;username&amp;quot;: &amp;quot;root&amp;quot;,
             &amp;quot;password&amp;quot;: &amp;quot;root&amp;quot;,
             &amp;quot;where&amp;quot;: &amp;quot;create_time &amp;gt; FROM_UNIXTIME(${create_time}) and create_time &amp;lt; FROM_UNIXTIME(${end_time})&amp;quot;,
             &amp;quot;column&amp;quot;: [
               &amp;quot;clue_atta_id&amp;quot;,
               &amp;quot;url&amp;quot;,
               &amp;quot;create_time&amp;quot;,
               &amp;quot;atta_type&amp;quot;,
               &amp;quot;clue_id&amp;quot;,
               &amp;quot;name&amp;quot;,
               &amp;quot;attachment_id&amp;quot;,
               &amp;quot;attr_sequence&amp;quot;
             ],
             &amp;quot;connection&amp;quot;: [
               {
                 &amp;quot;table&amp;quot;: [
                   &amp;quot;bus_clue_atta&amp;quot;
                 ],
                 &amp;quot;jdbcUrl&amp;quot;: [
                   &amp;quot;jdbc:mysql://x.x.x.x:3306/dbname&amp;quot;
                 ]
               }
             ]
           }
         },
         &amp;quot;writer&amp;quot;: {
           &amp;quot;name&amp;quot;: &amp;quot;mysqlwriter&amp;quot;,
           &amp;quot;parameter&amp;quot;: {
             &amp;quot;writeMode&amp;quot;: &amp;quot;insert&amp;quot;,
             &amp;quot;username&amp;quot;: &amp;quot;root&amp;quot;,
             &amp;quot;password&amp;quot;: &amp;quot;root&amp;quot;,
             &amp;quot;column&amp;quot;: [
               &amp;quot;clue_atta_id&amp;quot;,
               &amp;quot;url&amp;quot;,
               &amp;quot;create_time&amp;quot;,
               &amp;quot;atta_type&amp;quot;,
               &amp;quot;clue_id&amp;quot;,
               &amp;quot;name&amp;quot;,
               &amp;quot;attachment_id&amp;quot;,
               &amp;quot;attr_sequence&amp;quot;
             ],
             &amp;quot;session&amp;quot;: [
               &amp;quot;set session sql_mode=&amp;apos;ANSI&amp;apos;&amp;quot;
             ],
             &amp;quot;connection&amp;quot;: [
               {
                 &amp;quot;jdbcUrl&amp;quot;: &amp;quot;jdbc:mysql://x.x.x.x:3306/dbname?useUnicode=true&amp;amp;characterEncoding=utf8&amp;quot;,
                 &amp;quot;table&amp;quot;: [
                   &amp;quot;bus_clue_atta&amp;quot;
                 ]
               }
             ]
           }
         }
       }
     ]
   }
 }   &lt;div&gt;&lt;/div&gt;&lt;/code&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;&lt;/pre&gt; &lt;h2&gt;  &lt;a name="t2"&gt;&lt;/a&gt;shell脚本准备&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;因为我有多张表，编写一个脚本    &lt;br /&gt;   &lt;ul&gt;    &lt;li&gt;需要添加source /etc/profile ，因为在cron的系统环境和shell的环境不一样，会导致     &lt;code&gt;java commond not found&lt;/code&gt;错误      &lt;a href="http://josh-persistence.iteye.com/blog/2229300" rel="nofollow"&gt;参考地址&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;     &lt;code&gt;$(date +%s)&lt;/code&gt; 为获取系统当前时间戳 ，     &lt;code&gt;$(($end_time - 60))&lt;/code&gt; 为算术表达式计算60前的时间戳&lt;/li&gt;    &lt;li&gt;     &lt;code&gt;&amp;quot;-Dcreate_time=$create_time -Dend_time=$end_time&amp;quot;&lt;/code&gt;这里就是datax使用占位符的作用，可以将外部自定义参数传入&lt;/li&gt;    &lt;li&gt;     &lt;code&gt;&amp;gt;&amp;gt;/home/gzjp/datax_log/bus_clue_atta_log.&lt;/code&gt;date +%Y%m%d     &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt; 我这里把日期都放入每天的日志文件以免单一文件过大&lt;/li&gt;    &lt;li&gt;最后由于我的同步的表格较少我直接使用      &lt;code&gt;&amp;amp;&lt;/code&gt; 进行后台操作以免发生阻塞&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;pre&gt;  &lt;code&gt;#!/bin/bash
source /etc/profile
# 截至时间设置为当前时间戳
end_time=$(date +%s)
# 开始时间设置为60s前时间戳
create_time=$(($end_time - 60))
/home/gzjp/datax/bin/datax.py /home/gzjp/jobs/bus_clue_atta_job.json -p &amp;quot;-Dcreate_time=$create_time -Dend_time=$end_time&amp;quot; &amp;gt;&amp;gt;/home/gzjp/datax_log/bus_clue_atta_log.`date +%Y%m%d`  2&amp;gt;&amp;amp;1 &amp;amp;
/home/gzjp/datax/bin/datax.py /home/gzjp/jobs/bus_clue_job.json -p &amp;quot;-Dcreate_time=$create_time -Dend_time=$end_time&amp;quot; &amp;gt;&amp;gt;/home/gzjp/datax_log/bus_clue_log.`date +%Y%m%d`  2&amp;gt;&amp;amp;1 &amp;amp;
/home/gzjp/datax/bin/datax.py /home/gzjp/jobs/bus_attachment.json -p &amp;quot;-Dcreate_time=$create_time -Dend_time=$end_time&amp;quot; &amp;gt;&amp;gt;/home/gzjp/datax_log/bus_attachment_log.`date +%Y%m%d`  2&amp;gt;&amp;amp;1 &amp;amp;
   &lt;div&gt;&lt;/div&gt;&lt;/code&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;&lt;/pre&gt; &lt;h2&gt;  &lt;a name="t3"&gt;&lt;/a&gt;crontab 定时任务准备&lt;/h2&gt; &lt;pre&gt;  &lt;code&gt;$ crontab -e

*/1 * * * * /home/gzjp/jm_db_sync.sh &amp;gt;/dev/null 2&amp;gt;&amp;amp;1   &lt;div&gt;&lt;/div&gt;&lt;/code&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;&lt;/pre&gt; &lt;ul&gt;  &lt;li&gt;我是定时每分钟跑一次脚本，   &lt;strong&gt;注意一定要处理输入文件，因为cron会见执行情况通过mail发给用户，时间长系统会被塞爆&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;有意义的参考内容&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;   &lt;a href="https://helpcdn.aliyun.com/document_detail/62149.html" rel="nofollow"&gt;https://helpcdn.aliyun.com/document_detail/62149.html&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
     
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/59965-datax-crontab-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Sun, 18 Aug 2019 10:22:46 CST</pubDate>
    </item>
  </channel>
</rss>

