<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Orchestrator on Tarragon</title><link>https://tarrragon.github.io/blog/tags/orchestrator/</link><description>Recent content in Orchestrator on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 19 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/orchestrator/index.xml" rel="self" type="application/rss+xml"/><item><title>MySQL Orchestrator Failover：HA 工具自己怎麼 HA？raft cluster + GTID-based promotion 的兩段 paradox</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/mysql/orchestrator-failover/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/mysql/orchestrator-failover/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/mysql/" data-link-title="MySQL" data-link-desc="高併發網路服務常用關聯式資料庫、Vitess / PlanetScale 分片生態、GitHub / Shopify / Facebook 規模驗證">MySQL&lt;/a> overview 的 implementation-layer deep article。Overview 已說明 MySQL 在 OLTP 譜系的定位、本文聚焦 &lt;em>Orchestrator failover&lt;/em> — 自動 HA 的工具雙層架構跟 5 段 decision tree。&lt;/p>&lt;/blockquote>
&lt;hr>
&lt;blockquote>
&lt;p>用詞註：Orchestrator 工具命名與 MySQL 5.7- SQL 命令（&lt;code>SHOW SLAVE STATUS&lt;/code> / &lt;code>CHANGE MASTER TO&lt;/code> / &lt;code>STOP SLAVE&lt;/code> 等）沿用 &lt;em>master / slave&lt;/em>。MySQL 8.0+ 改採 &lt;em>primary / replica&lt;/em>、但 SQL syntax 仍保留別名。本文出現 master / slave 處對應 8.0 primary / replica 概念。&lt;/p>&lt;/blockquote>
&lt;p>讀者第一個會問的問題：「Orchestrator 自己會壞嗎？壞了誰 failover Orchestrator？」這個 paradox 是 &lt;em>任何 HA 工具&lt;/em> 的核心議題、PostgreSQL 的 Patroni 用 DCS（etcd / Consul）解決、MySQL 的 Orchestrator 用 &lt;em>內建 raft cluster&lt;/em> 解決：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">被管的 (Layer 1): primary MySQL → replica MySQL → replica MySQL → ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">管理者 (Layer 2): orchestrator instance × 3 (or 5) — 用 raft 自己選 leader
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">管理者狀態存放 (Layer 3): 每個 orchestrator instance 自己有 MySQL backend (state)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Orchestrator 3 個 instance 構成 &lt;em>raft cluster&lt;/em>、自己選 leader。Leader 才有 &lt;em>寫入 state&lt;/em> + &lt;em>發起 failover&lt;/em> 權限、其他 instance follower 同步 state。Leader 失聯 → raft 重新選 leader（&amp;lt; 10 秒）、新 leader 繼續 manage MySQL topology。&lt;/p>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/postgresql/patroni-ha/" data-link-title="PostgreSQL Patroni HA：從 leader 失聯到 client 重連的 5 段 failover lifecycle" data-link-desc="Patroni 把 PostgreSQL HA 拆成 detection / election / promotion / reconfiguration / recovery 五段 lifecycle、每段都有獨立配置跟 failure mode；DCS quorum &amp;#43; watchdog 防 split-brain、async/sync replication 取捨、5 個 production 踩雷、跟 PgBouncer / HAProxy / cert-manager 整合">PostgreSQL Patroni&lt;/a> 不同：Patroni 需要 &lt;em>外部 DCS&lt;/em>（etcd / Consul）作為 source of truth、Patroni 本身 stateless；Orchestrator 內建 raft、不需要外部 DCS、但每個 orchestrator instance 需要 &lt;em>自己的 MySQL backend&lt;/em> 存 state。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/01-database/vendors/mysql/" data-link-title="MySQL" data-link-desc="高併發網路服務常用關聯式資料庫、Vitess / PlanetScale 分片生態、GitHub / Shopify / Facebook 規模驗證">MySQL</a> overview 的 implementation-layer deep article。Overview 已說明 MySQL 在 OLTP 譜系的定位、本文聚焦 <em>Orchestrator failover</em> — 自動 HA 的工具雙層架構跟 5 段 decision tree。</p></blockquote>
<hr>
<blockquote>
<p>用詞註：Orchestrator 工具命名與 MySQL 5.7- SQL 命令（<code>SHOW SLAVE STATUS</code> / <code>CHANGE MASTER TO</code> / <code>STOP SLAVE</code> 等）沿用 <em>master / slave</em>。MySQL 8.0+ 改採 <em>primary / replica</em>、但 SQL syntax 仍保留別名。本文出現 master / slave 處對應 8.0 primary / replica 概念。</p></blockquote>
<p>讀者第一個會問的問題：「Orchestrator 自己會壞嗎？壞了誰 failover Orchestrator？」這個 paradox 是 <em>任何 HA 工具</em> 的核心議題、PostgreSQL 的 Patroni 用 DCS（etcd / Consul）解決、MySQL 的 Orchestrator 用 <em>內建 raft cluster</em> 解決：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">被管的 (Layer 1):       primary MySQL → replica MySQL → replica MySQL → ...
</span></span><span class="line"><span class="ln">2</span><span class="cl">管理者 (Layer 2):       orchestrator instance × 3 (or 5) — 用 raft 自己選 leader
</span></span><span class="line"><span class="ln">3</span><span class="cl">管理者狀態存放 (Layer 3): 每個 orchestrator instance 自己有 MySQL backend (state)</span></span></code></pre></div><p>Orchestrator 3 個 instance 構成 <em>raft cluster</em>、自己選 leader。Leader 才有 <em>寫入 state</em> + <em>發起 failover</em> 權限、其他 instance follower 同步 state。Leader 失聯 → raft 重新選 leader（&lt; 10 秒）、新 leader 繼續 manage MySQL topology。</p>
<p>跟 <a href="/blog/backend/01-database/vendors/postgresql/patroni-ha/" data-link-title="PostgreSQL Patroni HA：從 leader 失聯到 client 重連的 5 段 failover lifecycle" data-link-desc="Patroni 把 PostgreSQL HA 拆成 detection / election / promotion / reconfiguration / recovery 五段 lifecycle、每段都有獨立配置跟 failure mode；DCS quorum &#43; watchdog 防 split-brain、async/sync replication 取捨、5 個 production 踩雷、跟 PgBouncer / HAProxy / cert-manager 整合">PostgreSQL Patroni</a> 不同：Patroni 需要 <em>外部 DCS</em>（etcd / Consul）作為 source of truth、Patroni 本身 stateless；Orchestrator 內建 raft、不需要外部 DCS、但每個 orchestrator instance 需要 <em>自己的 MySQL backend</em> 存 state。</p>
<h2 id="orchestrator-雙層架構管-mysql-的-layer-2">Orchestrator 雙層架構：管 MySQL 的 Layer 2</h2>
<p>Layer 1 是 <em>被管的</em> MySQL cluster — primary + replica 群。Layer 2 是 <em>管理者</em> — orchestrator instance 群。Layer 2 監視 Layer 1、Layer 2 自己用 raft 自管。</p>
<p><strong>Layer 1 對 Orchestrator 的需求</strong>：</p>
<ul>
<li>所有 MySQL server 啟用 <code>binlog</code> + <code>log_slave_updates</code>（讓 Orchestrator 看得到 binlog event）</li>
<li>啟用 GTID（Orchestrator failover decision 依賴 GTID 比較進度、不用算 binlog position）</li>
<li>每個 server 有 <em>orchestrator user</em>（<code>GRANT SUPER, REPLICATION CLIENT, REPLICATION SLAVE, PROCESS ON *.* TO 'orchestrator'@'%'</code>）</li>
</ul>
<p><strong>Layer 2 配置</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># /etc/orchestrator.conf.json (簡化)</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="na">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="na">&#34;MySQLOrchestratorHost&#34;: &#34;orchestrator-backend.example.com&#34;,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="na">&#34;MySQLOrchestratorPort&#34;: 3306,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="na">&#34;MySQLOrchestratorDatabase&#34;: &#34;orchestrator&#34;,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="c1"># 用 backend MySQL（每個 orchestrator instance 自己一個）+ raft 同步</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="na">&#34;RaftEnabled&#34;: true,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="na">&#34;RaftDataDir&#34;: &#34;/var/lib/orchestrator&#34;,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="na">&#34;RaftBind&#34;: &#34;10.0.1.10:10008&#34;,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="na">&#34;RaftNodes&#34;: [</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="na">&#34;orchestrator1.example.com:10008&#34;,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="na">&#34;orchestrator2.example.com:10008&#34;,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="na">&#34;orchestrator3.example.com:10008&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="na">],</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="c1"># Topology discovery</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="na">&#34;DiscoverByShowSlaveHosts&#34;: true,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="na">&#34;InstancePollSeconds&#34;: 5,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="c1"># Failover detection</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="na">&#34;FailureDetectionPeriodBlockMinutes&#34;: 60,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="na">&#34;RecoveryPeriodBlockSeconds&#34;: 3600,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="c1"># Failover automation</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="na">&#34;RecoverMasterClusterFilters&#34;: [&#34;*&#34;],</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="na">&#34;RecoverIntermediateMasterClusterFilters&#34;: [&#34;*&#34;],</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="na">&#34;PreFailoverProcesses&#34;: [&#34;/usr/local/bin/orchestrator-fence-master.sh&#34;],</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="na">&#34;PostFailoverProcesses&#34;: [&#34;/usr/local/bin/orchestrator-notify-proxysql.sh&#34;]</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="na">}</span></span></span></code></pre></div><h2 id="stage-1topology-discovery--自動發現--manual-seed">Stage 1：Topology Discovery — 自動發現 + manual seed</h2>
<p>Orchestrator 啟動後 <em>seed</em> 一個或多個 MySQL server、自動發現整個 topology：</p>
<ul>
<li>連 seed server → <code>SHOW SLAVE HOSTS</code> → 發現所有 replica</li>
<li>對每個 replica 跑 <code>SHOW MASTER STATUS</code> + <code>SHOW SLAVE STATUS</code> → 建立 <em>父子關係 graph</em></li>
<li>持續 poll（<code>InstancePollSeconds=5</code>）每 5 秒更新 topology state</li>
</ul>
<p><strong>Topology graph 的 node</strong>：</p>
<ul>
<li><em>Master</em>：no slave status、被多個 replica 指</li>
<li><em>Intermediate master</em>：有 slave status 也有下游 replica（chained replication）</li>
<li><em>Co-master</em>：互相 replicate（罕見、active-passive failover 場景）</li>
<li><em>Replica</em>：有 slave status、無下游</li>
</ul>
<p>Topology 可視化：Orchestrator UI（web）顯示 cluster 樹狀圖、操作員可手動 drag-and-drop replica 重新 attach。</p>
<h2 id="stage-2failure-detection--區分真壞跟假壞">Stage 2：Failure Detection — 區分真壞跟假壞</h2>
<p>Orchestrator 不是 <em>單一 ping 失敗就 failover</em>、有 <em>holistic detection</em>：</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>解讀</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Master <code>connect fail</code></td>
          <td>可能 network blip、不一定真壞</td>
      </tr>
      <tr>
          <td>Master <code>timeout poll</code></td>
          <td>可能 master loaded、不一定真壞</td>
      </tr>
      <tr>
          <td><strong>Replica 全部 <code>IO error</code></strong></td>
          <td>Master 真的對 replica 不可達、強訊號</td>
      </tr>
      <tr>
          <td>Replica 看到 master 還活著</td>
          <td>Master 對 orchestrator 不可達、可能是 <em>orchestrator network</em> 問題、不是 master</td>
      </tr>
      <tr>
          <td>Replica lag 暴增</td>
          <td>Master 可能還活著但 overload、不一定要 failover</td>
      </tr>
  </tbody>
</table>
<p><strong>Detection rule</strong>：Master <em>自己連不上</em> + <em>至少一個 replica 也看 master IO error</em> → 判定 <code>DeadMaster</code>。單一 orchestrator 連不上 master 不觸發 — 防 orchestrator network 隔離造成的 false positive failover。</p>
<h2 id="stage-3failover-decision-tree--選哪個-replica-promote">Stage 3：Failover Decision Tree — 選哪個 replica promote</h2>
<p>判定 <code>DeadMaster</code> 後不是 <em>選最近的 replica</em>、用 decision tree：</p>
<ol>
<li><strong>GTID 最新的 replica</strong>：跟舊 master 同步最完整（用 <code>Executed_Gtid_Set</code> 對比）</li>
<li><strong>同 DC / AZ 的 replica</strong>（如果有 multi-DC 配置）</li>
<li><strong>手動指定的 promotion candidate</strong>（<code>promote_rule=must</code> 或 <code>prefer</code>）</li>
<li><strong>Semi-sync ack 的 replica</strong>（如果 semi-sync 啟用）</li>
</ol>
<p>GTID 最新是基本要求。其他規則是 <em>tie-breaker</em>。</p>
<p><strong>Errant transaction 處理</strong>：選出的 candidate replica 如果有 <em>errant GTID</em>（master 沒有但 replica 有的 transaction）、Orchestrator <em>不會 promote 這個 replica</em>（怕 errant transaction 變成 new master state）。改選次優 candidate。</p>
<h2 id="stage-4promote-action--5-步-atomic理想情況">Stage 4：Promote Action — 5 步 atomic（理想情況）</h2>
<p>選好 candidate 後執行：</p>
<ol>
<li><strong>Fence 舊 master</strong>（pre-failover hook）：把舊 master 對外停掉、防 split-brain</li>
<li><strong>STOP SLAVE on candidate</strong>：candidate 不再從舊 master pull binlog</li>
<li><strong>RESET SLAVE ALL on candidate</strong>：candidate 清掉 slave 配置、變成獨立 master</li>
<li><strong>Re-attach 其他 replica</strong>：用 <code>CHANGE MASTER TO MASTER_HOST=&lt;candidate&gt;, MASTER_AUTO_POSITION=1</code>（GTID auto-position）</li>
<li><strong>Post-failover hook</strong>：通知 ProxySQL / HAProxy / DNS 切流量</li>
</ol>
<p>每步任一失敗、Orchestrator 可能停在中間狀態、需要 <em>人工介入</em>。</p>
<h2 id="stage-5recovery--old-master-怎麼處理">Stage 5：Recovery — Old master 怎麼處理</h2>
<p>Failover 完、舊 master 可能：</p>
<ul>
<li><em>真的死了</em>：物理 server 故障 / region outage → 不必處理、未來修好作為新 replica re-attach</li>
<li><em>Network blip 後復活</em>：舊 master 自己 <em>仍認為自己是 master</em>、再次接受寫入會造成 split-brain</li>
</ul>
<p>修法：</p>
<ul>
<li><em>Fencing</em>（必須）：pre-failover hook 把舊 master 對外 firewall 掉、或 force <code>read_only=1</code>、防舊 master 復活後接受寫入</li>
<li><em>Manual reset</em>：舊 master 復活後人工 confirm 是否變成新 master 的 replica（不要自動、自動容易誤判）</li>
</ul>
<p>Orchestrator UI 在偵測到 errant master 時會標 warning、不會自動處理。</p>
<h2 id="5-個-production-踩雷">5 個 Production 踩雷</h2>
<h3 id="1-split-brain--pre-failover-hook-沒-fence-舊-master">1. Split-brain — pre-failover hook 沒 fence 舊 master</h3>
<p>舊 master network blip 後復活、orchestrator 已 promote 新 master、application 部分 instance 連舊 master、部分連新 master、雙寫造成 data divergence。</p>
<p>修法：</p>
<ul>
<li><em>Pre-failover hook 必須 fence</em>（不是可選）：
<ul>
<li>物理 fencing：透過 IPMI 重啟 / 關 server</li>
<li>Network fencing：透過 firewall rule 切斷 server 對外連線</li>
<li>MySQL fencing：<code>SET GLOBAL read_only=1</code> + <code>KILL</code> 所有 active connection</li>
</ul>
</li>
<li>用 <em>VIP / DNS</em> 配合：fence 完才切 VIP / DNS 到新 master、避免 application 連舊 IP</li>
<li>不依賴 application 連線 string 動態變更（DNS TTL 期間仍可能連舊 IP）</li>
</ul>
<h3 id="2-pre-failover-hook-失敗--orchestrator-該停還是該繼續">2. Pre-failover hook 失敗 — Orchestrator 該停還是該繼續</h3>
<p>Pre-failover hook 跑失敗（fence script 因為 SSH 不通、IPMI 沒回應）。Orchestrator 有兩種策略：</p>
<ul>
<li><em>PostponeReplicaRecoveryOnLagMinutes</em>：等 hook 成功才繼續、可能永遠 stuck</li>
<li><em>FailMasterPromotionOnLagMinutes</em>：放棄 promotion、留 cluster degraded（無 master）</li>
</ul>
<p>兩者都不理想。多數 production 選 <em>PostponeReplicaRecoveryOnLagMinutes=10</em>：等 10 分鐘 hook 成功、超時則 alert 人工介入、不繼續 auto-promote（人工 review 才是正確選擇）。</p>
<h3 id="3-anti-flapping-窗口太短--master-抖動-vs-真死">3. Anti-flapping 窗口太短 — Master 抖動 vs 真死</h3>
<p><code>FailureDetectionPeriodBlockMinutes=60</code>：偵測一次 failure 後 60 分鐘內不再 trigger failover（即使再偵測到 failure）。預設 60 分鐘對 <em>第一次 failover 後 master 仍不穩</em> 的場景太長 — 60 分鐘內 master 真的死了第二次、orchestrator 不 failover。預設 60 分鐘對 <em>網路抖動</em> 的場景太短 — 60 分鐘內可能 multiple failover、cluster 一直在 promote。</p>
<p>修法：</p>
<ul>
<li>評估自己 cluster 的 <em>typical recovery time</em>：1-2 小時、設 <code>FailureDetectionPeriodBlockMinutes=120</code></li>
<li>監控 <em>failover 頻率</em>、單週 &gt; 2 次表示底層問題（網路 / hardware）、不是調 anti-flapping window 解決</li>
</ul>
<h3 id="4-gtid-errant-transaction--orchestrator-拒絕-promote-但沒講原因">4. GTID errant transaction — Orchestrator 拒絕 promote 但沒講原因</h3>
<p>Candidate replica 有 <em>errant GTID</em>（從別處 inject 的 transaction）、Orchestrator 拒絕 promote、log 訊息 <code>errant GTID detected</code>、但 <em>沒寫實際是哪個 GTID</em>。On-call 在事故中沒辦法 debug。</p>
<p>修法：</p>
<ul>
<li>平時 <em>監控 errant GTID</em>：定期跑 <code>pt-show-grants</code> + GTID 比對、不要等 failover 才發現</li>
<li>Orchestrator 的 <code>OrchestratorIssuesAGtidPurge</code> 設 true：preview mode 看 errant GTID 的位置</li>
<li>Errant GTID 來源通常是 <em>人為 inject</em>（DBA 直接寫 replica 然後 binlog 出現）、教育 DBA 不要直接連 replica 寫</li>
</ul>
<h3 id="5-vip--proxysql-整合斷層--切流量延遲">5. VIP / ProxySQL 整合斷層 — 切流量延遲</h3>
<p>Post-failover hook 跑完 <em>script 上報</em>「我切完了」、但實際 <em>VIP / DNS / ProxySQL 還沒看到變化</em>。Application 連 stale endpoint 30 秒、寫入失敗。</p>
<p>修法：</p>
<ul>
<li><em>Post-failover hook 不只 trigger 切換、要 wait 切換完成</em>：
<ul>
<li>VIP：等 <code>arping</code> 確認新 IP 已 propagate</li>
<li>ProxySQL：等 <code>mysql_servers</code> runtime table 更新 + 確認 monitor module 看到新 primary</li>
<li>DNS：先把 TTL 降到極短（5 秒）、再切 DNS、等 TTL 過</li>
</ul>
</li>
<li>Orchestrator <code>PostFailoverProcessesFailOnError=true</code>：hook 失敗整個 failover 標記失敗、人工檢查</li>
<li>ProxySQL 用 <code>mysql_replication_hostgroups</code> 自動偵測 read_only flag、可不依賴 hook（推薦）</li>
</ul>
<h2 id="容量規劃要點">容量規劃要點</h2>
<table>
  <thead>
      <tr>
          <th>元件</th>
          <th>配置建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Orchestrator instance 數量</td>
          <td>3（raft cluster 最小、odd number、容忍 1 個故障）</td>
      </tr>
      <tr>
          <td>每個 instance MySQL backend</td>
          <td>1 個獨立 MySQL（不要共用、不要用被管的 cluster）</td>
      </tr>
      <tr>
          <td>Backend MySQL spec</td>
          <td>t3.small 級別、Orchestrator state ~1 GB</td>
      </tr>
      <tr>
          <td>Network latency</td>
          <td>raft 同 region 內、跨 AZ 可接受（&lt; 5ms）、跨 region 不推薦</td>
      </tr>
      <tr>
          <td>InstancePollSeconds</td>
          <td>5 秒（預設）— 越小越敏感、越大越省連線</td>
      </tr>
  </tbody>
</table>
<p>3 instance raft cluster 容忍 1 instance 故障。5 instance 容忍 2 instance 故障但 quorum cost 高、99% 場景 3 個夠用。</p>
<h2 id="跟其他模組整合">跟其他模組整合</h2>
<h3 id="跟-replication-topology">跟 Replication topology</h3>
<p>Orchestrator 100% 依賴 GTID + binlog ROW format（<a href="/blog/backend/01-database/vendors/mysql/replication-topology/" data-link-title="MySQL Replication Topology：async / semi-sync / GTID 不是三選一、是三個 trade-off 軸的疊加" data-link-desc="MySQL replication 不是「選 async 還是 semi-sync」、是 *durability / latency / consistency* 三個 trade-off 軸的疊加；GTID 是跨 mode 的 infrastructure layer、不是第三種 mode。本文走 3 軸取捨模型 → async / semi-sync 行為對比 → GTID 替代 binlog-position 的好處 → 配置 step-by-step → 5 production 踩雷（lag 暴衝 / semi-sync 退回 async / GTID gap / Loss-Less semi-sync 真的 loss-less / chained replication 雪崩）→ 跟 Aurora MySQL / Vitess / ProxySQL / Orchestrator 整合">Replication Topology</a>）。沒 GTID 用 binlog position、failover 時 re-pointing 容易出錯、Orchestrator 強烈建議 GTID。</p>
<h3 id="跟-proxysql">跟 ProxySQL</h3>
<p><a href="/blog/backend/01-database/vendors/mysql/proxysql-config/" data-link-title="MySQL ProxySQL 配置：connection / query / route / response 四段 lifecycle 跟 query rule 設計" data-link-desc="ProxySQL 是 MySQL 生態的 connection pool &#43; query routing 標準。本文走 connection → query parse → route → response 四段 lifecycle、query rule engine 的 rule chain 設計、Hostgroup / Server / User 三層 schema、配置 step-by-step（讀寫分離 &#43; replica lag-aware routing）、5 production 踩雷（query rule 順序錯亂 / connection 漂移 / write 路由到 replica / runtime / disk schema drift / mirror traffic 副作用）、跟 Replication / Orchestrator / HAProxy 整合">ProxySQL</a> 用 <code>mysql_replication_hostgroups</code> 自動偵測 <code>read_only</code> flag — orchestrator 切完新 master 後、ProxySQL monitor module 自動看到新 master 的 <code>read_only=0</code>、自動更新 routing、application 不用改 connection string。</p>
<p>這個 <em>無需 post-failover hook 通知 ProxySQL</em> 的整合是 ProxySQL + Orchestrator 組合的最大優勢、比手動 hook 通知 VIP / DNS 可靠。</p>
<h3 id="跟-patronipostgresql-對應">跟 Patroni（PostgreSQL 對應）</h3>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Orchestrator</th>
          <th>Patroni</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DCS</td>
          <td>內建 raft（不需外部）</td>
          <td>外部（etcd / Consul / ZooKeeper）</td>
      </tr>
      <tr>
          <td>State storage</td>
          <td>每 instance 一個 MySQL backend</td>
          <td>DCS 本身</td>
      </tr>
      <tr>
          <td>Topology discovery</td>
          <td>自動 + manual seed</td>
          <td>自動（透過 DCS）</td>
      </tr>
      <tr>
          <td>Fencing</td>
          <td>Pre-failover hook（自實作）</td>
          <td>Watchdog（內建）</td>
      </tr>
      <tr>
          <td>5+ year 生產驗證</td>
          <td>GitHub / Booking.com / Shopify</td>
          <td>Zalando / 多個歐美企業</td>
      </tr>
  </tbody>
</table>
<p>兩者角色相同、設計取捨不同。Patroni 對 DCS 高依賴、Orchestrator 對自己 backend MySQL 高依賴。</p>
<h3 id="跟-rds--aurora-mysql">跟 RDS / Aurora MySQL</h3>
<p>AWS RDS / Aurora 內建 multi-AZ failover、<em>不用 Orchestrator</em>。Aurora failover &lt; 30 秒、RDS failover ~60-120 秒。Aurora 把 replication / failover 整套封進 storage layer、application 看到的是 reader endpoint + writer endpoint。</p>
<p>詳見 <a href="/blog/backend/01-database/vendors/aurora/" data-link-title="AWS Aurora" data-link-desc="AWS managed PostgreSQL / MySQL、storage / compute 分離、&#43;75% 效能改善的 production 證據">Aurora vendor page</a>。</p>
<h3 id="跟-vitess">跟 Vitess</h3>
<p>Vitess shard 內部用 <em>VTOrc</em>（Vitess fork of Orchestrator）— 概念跟 Orchestrator 一致、針對 Vitess topology metadata 適配。</p>
<p>詳見 <em>Vitess sharding 設計</em> 篇（待寫）。</p>
<h2 id="相關連結">相關連結</h2>
<ul>
<li><a href="/blog/backend/01-database/vendors/mysql/" data-link-title="MySQL" data-link-desc="高併發網路服務常用關聯式資料庫、Vitess / PlanetScale 分片生態、GitHub / Shopify / Facebook 規模驗證">MySQL vendor overview</a></li>
<li><a href="/blog/backend/01-database/vendors/mysql/replication-topology/" data-link-title="MySQL Replication Topology：async / semi-sync / GTID 不是三選一、是三個 trade-off 軸的疊加" data-link-desc="MySQL replication 不是「選 async 還是 semi-sync」、是 *durability / latency / consistency* 三個 trade-off 軸的疊加；GTID 是跨 mode 的 infrastructure layer、不是第三種 mode。本文走 3 軸取捨模型 → async / semi-sync 行為對比 → GTID 替代 binlog-position 的好處 → 配置 step-by-step → 5 production 踩雷（lag 暴衝 / semi-sync 退回 async / GTID gap / Loss-Less semi-sync 真的 loss-less / chained replication 雪崩）→ 跟 Aurora MySQL / Vitess / ProxySQL / Orchestrator 整合">MySQL Replication Topology</a>（GTID 是 Orchestrator pre-requisite）</li>
<li><a href="/blog/backend/01-database/vendors/mysql/proxysql-config/" data-link-title="MySQL ProxySQL 配置：connection / query / route / response 四段 lifecycle 跟 query rule 設計" data-link-desc="ProxySQL 是 MySQL 生態的 connection pool &#43; query routing 標準。本文走 connection → query parse → route → response 四段 lifecycle、query rule engine 的 rule chain 設計、Hostgroup / Server / User 三層 schema、配置 step-by-step（讀寫分離 &#43; replica lag-aware routing）、5 production 踩雷（query rule 順序錯亂 / connection 漂移 / write 路由到 replica / runtime / disk schema drift / mirror traffic 副作用）、跟 Replication / Orchestrator / HAProxy 整合">MySQL ProxySQL 配置</a>（Orchestrator + ProxySQL 自動失效切換組合）</li>
<li><a href="/blog/backend/01-database/vendors/postgresql/patroni-ha/" data-link-title="PostgreSQL Patroni HA：從 leader 失聯到 client 重連的 5 段 failover lifecycle" data-link-desc="Patroni 把 PostgreSQL HA 拆成 detection / election / promotion / reconfiguration / recovery 五段 lifecycle、每段都有獨立配置跟 failure mode；DCS quorum &#43; watchdog 防 split-brain、async/sync replication 取捨、5 個 production 踩雷、跟 PgBouncer / HAProxy / cert-manager 整合">PostgreSQL Patroni HA</a>（PG sibling、不同 HA 機制）</li>
<li><a href="/blog/backend/01-database/vendors/aurora/" data-link-title="AWS Aurora" data-link-desc="AWS managed PostgreSQL / MySQL、storage / compute 分離、&#43;75% 效能改善的 production 證據">Aurora vendor page</a>（managed MySQL、Orchestrator 不需要）</li>
<li><a href="/blog/backend/knowledge-cards/quorum/" data-link-title="Quorum" data-link-desc="分散式系統以多數節點同意作為提交或讀取有效性的門檻">quorum 卡片</a> / <a href="/blog/backend/knowledge-cards/failover/" data-link-title="Failover" data-link-desc="說明主要服務或節點失效時如何切換到備援能力">failover 卡片</a></li>
<li>官方：<a href="https://github.com/openark/orchestrator">orchestrator GitHub</a> / <a href="https://github.com/openark/orchestrator/tree/master/docs">orchestrator docs</a></li>
</ul>
]]></content:encoded></item></channel></rss>