<?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>Valkey on Tarragon</title><link>https://tarrragon.github.io/blog/tags/valkey/</link><description>Recent content in Valkey on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/valkey/index.xml" rel="self" type="application/rss+xml"/><item><title>DragonflyDB → Redis / Valkey：回退到標準生態的遷移路徑</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/migrate-to-redis/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/migrate-to-redis/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。反向路徑見 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &amp;#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB&lt;/a>。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（RESP 協定相容），但 HA 和持久化有差異需要處理。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼從-dragonflydb-遷回">為什麼從 DragonflyDB 遷回&lt;/h2>
&lt;p>DragonflyDB 遷回 Redis/Valkey 的 driver 跟正向遷移互為鏡像：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Redis Modules 需求&lt;/strong>：業務開始需要 RedisJSON、RediSearch 或 RedisTimeSeries，DragonflyDB 不支援 Redis Modules 生態&lt;/li>
&lt;li>&lt;strong>Cluster mode 需求&lt;/strong>：DragonflyDB 設計為單機 scale-up，當資料量超過單機記憶體上限（數 TB）或需要跨 node sharding 時，Redis Cluster 或 Valkey Cluster 是成熟選擇&lt;/li>
&lt;li>&lt;strong>Sentinel / HA 生態&lt;/strong>：DragonflyDB 的 HA 用自家 replication，不支援 Sentinel。若團隊已有 Sentinel 或 Operator 基礎設施，回到 Redis/Valkey 整合成本更低&lt;/li>
&lt;li>&lt;strong>BSL 授權疑慮&lt;/strong>：DragonflyDB 是 BSL 1.1（4 年後轉 Apache 2.0），部分組織偏好 BSD（Valkey）或即使是 RSALv2（Redis）的已知授權&lt;/li>
&lt;/ul>
&lt;h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>RESP 相容、data types 一致&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>DragonflyDB replication → Sentinel/Cluster；snapshotting → RDB+AOF&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>相同（key-value cache）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>DragonflyDB 1-2 nodes → Redis primary + replica + Sentinel（或 Cluster 6 nodes）&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>endpoint 換、client config 微調（無 API 差異）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>DragonflyDB snapshot → Redis RDB 相容&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>全域 Low-Medium → &lt;strong>Type B drop-in&lt;/strong>，工作重心在 HA 架構切換和持久化模式對齊。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。反向路徑見 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a>。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（RESP 協定相容），但 HA 和持久化有差異需要處理。</p></blockquote>
<h2 id="為什麼從-dragonflydb-遷回">為什麼從 DragonflyDB 遷回</h2>
<p>DragonflyDB 遷回 Redis/Valkey 的 driver 跟正向遷移互為鏡像：</p>
<ul>
<li><strong>Redis Modules 需求</strong>：業務開始需要 RedisJSON、RediSearch 或 RedisTimeSeries，DragonflyDB 不支援 Redis Modules 生態</li>
<li><strong>Cluster mode 需求</strong>：DragonflyDB 設計為單機 scale-up，當資料量超過單機記憶體上限（數 TB）或需要跨 node sharding 時，Redis Cluster 或 Valkey Cluster 是成熟選擇</li>
<li><strong>Sentinel / HA 生態</strong>：DragonflyDB 的 HA 用自家 replication，不支援 Sentinel。若團隊已有 Sentinel 或 Operator 基礎設施，回到 Redis/Valkey 整合成本更低</li>
<li><strong>BSL 授權疑慮</strong>：DragonflyDB 是 BSL 1.1（4 年後轉 Apache 2.0），部分組織偏好 BSD（Valkey）或即使是 RSALv2（Redis）的已知授權</li>
</ul>
<h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>RESP 相容、data types 一致</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>DragonflyDB replication → Sentinel/Cluster；snapshotting → RDB+AOF</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>相同（key-value cache）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>DragonflyDB 1-2 nodes → Redis primary + replica + Sentinel（或 Cluster 6 nodes）</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>endpoint 換、client config 微調（無 API 差異）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>DragonflyDB snapshot → Redis RDB 相容</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>全域 Low-Medium → <strong>Type B drop-in</strong>，工作重心在 HA 架構切換和持久化模式對齊。</p>
<h2 id="相容性確認">相容性確認</h2>
<p>DragonflyDB → Redis 的相容方向跟 Redis → DragonflyDB 相反 — Redis 是 superset，回到 Redis 不會有功能缺失。但有幾個操作面差異需要處理：</p>
<table>
  <thead>
      <tr>
          <th>DragonflyDB 行為</th>
          <th>Redis 行為</th>
          <th>處理方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Multi-threaded 吞吐量</td>
          <td>單主線程（I/O threads 輔助）</td>
          <td>回到 Redis 後 throughput 下降是預期行為；若單機不夠需要 Cluster 分片</td>
      </tr>
      <tr>
          <td>Fork-less snapshot</td>
          <td>BGSAVE fork + COW</td>
          <td>關注 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence fork latency</a>，大 dataset 的 fork 會造成延遲 spike</td>
      </tr>
      <tr>
          <td>自家 replication</td>
          <td>Redis replication + Sentinel 或 Cluster</td>
          <td>需要重建 HA 架構，見下方階段二</td>
      </tr>
      <tr>
          <td>無 AOF</td>
          <td>AOF + RDB 混合持久化</td>
          <td>依需求決定是否開 AOF；純 cache 場景可只用 RDB</td>
      </tr>
      <tr>
          <td>無 Cluster mode</td>
          <td>Redis Cluster 或 Valkey Cluster</td>
          <td>資料量大時需要規劃 sharding</td>
      </tr>
  </tbody>
</table>
<h2 id="階段一資料匯出">階段一：資料匯出</h2>
<p>DragonflyDB 支援 <code>SAVE</code> / <code>BGSAVE</code> 產生 RDB 格式 snapshot，跟 Redis RDB 相容。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 在 DragonflyDB 觸發 snapshot</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli -h dragonfly-host BGSAVE
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 等 BGSAVE 完成</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli -h dragonfly-host LASTSAVE
</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"># 複製 snapshot 檔案到 Redis 資料目錄</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">cp /dragonfly-data/dump.rdb /redis-data/dump.rdb</span></span></code></pre></div><p>RDB 載入驗證：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 啟動 Redis 載入 RDB</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-server --dbfilename dump.rdb --dir /redis-data
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 驗證 key count</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli DBSIZE</span></span></code></pre></div><p>若 DragonflyDB 跑的是較新版本產出的 RDB，先在測試環境驗證 Redis 能正常載入。DragonflyDB 的 RDB 基於 Redis 6.x 格式，Redis 7.x 和 Valkey 8.x 向下相容無問題。</p>
<h2 id="階段二ha-架構重建">階段二：HA 架構重建</h2>
<p>DragonflyDB 回到 Redis/Valkey 後，HA 需要從 DragonflyDB replication 切換到 Sentinel 或 Cluster。</p>
<h3 id="sentinel-路徑適合非分片場景">Sentinel 路徑（適合非分片場景）</h3>
<p>1 primary + N replica + 3 Sentinel nodes。配置見 <a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a>。</p>
<h3 id="cluster-路徑適合需要分片的場景">Cluster 路徑（適合需要分片的場景）</h3>
<p>最小 3 primary + 3 replica。配置見 <a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Redis Cluster Resharding</a>。</p>
<p>選擇依據：資料量 &lt; 單機記憶體的 70% 用 Sentinel，需要水平擴展用 Cluster。</p>
<h2 id="階段三client-切換">階段三：Client 切換</h2>
<p>Application 的 Redis client 不需要改 API — DragonflyDB 跟 Redis 用同一套 RESP 協定。需要改的只有：</p>
<ol>
<li><strong>Endpoint</strong>：從 DragonflyDB host:port 改為 Redis primary（或 Sentinel/Cluster endpoint）</li>
<li><strong>認證</strong>：若 DragonflyDB 用 <code>requirepass</code>，Redis 同參數；若要升級到 ACL 趁此機會配置</li>
<li><strong>Sentinel/Cluster 配置</strong>：client library 需要啟用 Sentinel discovery 或 Cluster mode</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 切換前：直連 DragonflyDB</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">redis</span><span class="o">.</span><span class="n">Redis</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s2">&#34;dragonfly-host&#34;</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">6379</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s2">&#34;secret&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 切換後：Sentinel 模式</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">sentinel</span> <span class="o">=</span> <span class="n">redis</span><span class="o">.</span><span class="n">Sentinel</span><span class="p">([(</span><span class="s2">&#34;sentinel-1&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;sentinel-2&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">),</span> <span class="p">(</span><span class="s2">&#34;sentinel-3&#34;</span><span class="p">,</span> <span class="mi">26379</span><span class="p">)])</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="n">sentinel</span><span class="o">.</span><span class="n">master_for</span><span class="p">(</span><span class="s2">&#34;mymaster&#34;</span><span class="p">,</span> <span class="n">password</span><span class="o">=</span><span class="s2">&#34;secret&#34;</span><span class="p">)</span></span></span></code></pre></div><h2 id="階段四效能-baseline-與回退">階段四：效能 baseline 與回退</h2>
<h3 id="效能預期">效能預期</h3>
<p>回到 Redis 後，單機 throughput 會低於 DragonflyDB（Redis 單主線程 vs DragonflyDB 多線程）。建立 baseline 時要跟 Redis 的歷史數據比，不是跟 DragonflyDB 比。</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>預期變化</th>
          <th>應對</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>吞吐量</td>
          <td>下降（單線程限制）</td>
          <td>Cluster 分片或 read replica 分散</td>
      </tr>
      <tr>
          <td>Latency p99</td>
          <td>BGSAVE 期間可能有 spike</td>
          <td>調整 BGSAVE 排程避開高峰</td>
      </tr>
      <tr>
          <td>記憶體使用</td>
          <td>上升 ~30%（Redis 記憶體效率較低）</td>
          <td>預先調整 maxmemory 和 eviction policy</td>
      </tr>
  </tbody>
</table>
<h3 id="回退路徑">回退路徑</h3>
<p>回退到 DragonflyDB：把 Redis 的 RDB dump 回 DragonflyDB 載入，endpoint 改回。Cache 資料可重建，即使 RDB 不搬，DragonflyDB 重啟後 cache miss 回源到 DB 即可。</p>
<p>DragonflyDB 在遷移完成後保留 7 天再下線。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>反向路徑：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a></li>
<li>HA 重建：<a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Cluster Resharding</a></li>
<li>持久化注意：<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">Persistence Fork Latency</a></li>
</ul>
]]></content:encoded></item><item><title>KeyDB → Redis / Valkey：從多線程 fork 回歸主線的遷移路徑</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/migrate-to-redis/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/migrate-to-redis/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（KeyDB 是 Redis fork、RESP 相容、RDB/AOF 相容），但 active-active replication 跟 multi-threading 特性回退需要額外處理。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼從-keydb-遷回">為什麼從 KeyDB 遷回&lt;/h2>
&lt;p>KeyDB 是 Snap 維護的 Redis fork，主要差異化在多線程和 active-active replication。遷回的 driver：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>維護活躍度疑慮&lt;/strong>：KeyDB 的 release cadence 跟 Redis/Valkey 主線比較慢，部分組織擔心長期維護與安全 patch 的及時性&lt;/li>
&lt;li>&lt;strong>Valkey 生態收斂&lt;/strong>：Valkey 在 Linux Foundation 治理下快速演進（8.x 多線程改進），KeyDB 的多線程優勢逐漸縮小&lt;/li>
&lt;li>&lt;strong>Active-active 不再需要&lt;/strong>：業務不再需要跨 region active-active、或改用 application 層處理衝突解析&lt;/li>
&lt;li>&lt;strong>社群與工具生態&lt;/strong>：Redis/Valkey 的 client library、monitoring exporter、Operator 支援度更廣&lt;/li>
&lt;/ul>
&lt;h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>完全相容（fork 自 Redis 6.x）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>active-active → Sentinel/Cluster；multi-thread config 移除&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>相同&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>相近（1 primary + N replica + HA）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>endpoint 換、client config 微調&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>RDB/AOF 完全相容&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Type B drop-in，工作重心在 active-active replication 拆除和效能 baseline 對齊。&lt;/p>
&lt;h2 id="keydb-特有功能的處理">KeyDB 特有功能的處理&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>KeyDB 特有功能&lt;/th>
 &lt;th>Redis/Valkey 對應&lt;/th>
 &lt;th>遷移處理&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Multi-threading（&lt;code>server-threads&lt;/code>）&lt;/td>
 &lt;td>Redis I/O threads / Valkey 8 async I/O&lt;/td>
 &lt;td>回到 Redis 後吞吐量下降是預期，需要 benchmark 建立新 baseline&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Active-active replication&lt;/td>
 &lt;td>無原生等價。Redis 需要 application 層解衝突或用 CRDTs（社群方案）&lt;/td>
 &lt;td>遷移前確認業務是否仍需 multi-master。不需要則直接切 Sentinel/Cluster&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>FLASH storage（&lt;code>storage-provider flash&lt;/code>）&lt;/td>
 &lt;td>無原生等價。Redis 純記憶體&lt;/td>
 &lt;td>遷移前把 FLASH 資料回收到記憶體，或接受遷移後記憶體需求上升。調整 &lt;code>maxmemory&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Subkey expires&lt;/td>
 &lt;td>Redis 無 subkey expire（只有 top-level key TTL）&lt;/td>
 &lt;td>檢查 application 是否依賴 subkey expire；若有需要改寫為 top-level key 或用 sorted set 模擬&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>EXPIREMEMBER&lt;/code> 命令&lt;/td>
 &lt;td>Redis 無此命令&lt;/td>
 &lt;td>grep application code 確認未使用；若有需改寫&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>FLASH storage 的處理取決於冷資料比例。如果多數資料在 FLASH 上（用 &lt;code>OBJECT FREQ&lt;/code> 確認），遷移後的 Redis 記憶體需求會大幅上升 — 要提前計算純記憶體所需容量，調整 instance 規格或改用更積極的 eviction policy。Subkey expires 和 &lt;code>EXPIREMEMBER&lt;/code> 的影響範圍通常較小，但一旦 application 依賴就需要重構資料結構（用 top-level key + TTL 或 sorted set 模擬過期）。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（KeyDB 是 Redis fork、RESP 相容、RDB/AOF 相容），但 active-active replication 跟 multi-threading 特性回退需要額外處理。</p></blockquote>
<h2 id="為什麼從-keydb-遷回">為什麼從 KeyDB 遷回</h2>
<p>KeyDB 是 Snap 維護的 Redis fork，主要差異化在多線程和 active-active replication。遷回的 driver：</p>
<ul>
<li><strong>維護活躍度疑慮</strong>：KeyDB 的 release cadence 跟 Redis/Valkey 主線比較慢，部分組織擔心長期維護與安全 patch 的及時性</li>
<li><strong>Valkey 生態收斂</strong>：Valkey 在 Linux Foundation 治理下快速演進（8.x 多線程改進），KeyDB 的多線程優勢逐漸縮小</li>
<li><strong>Active-active 不再需要</strong>：業務不再需要跨 region active-active、或改用 application 層處理衝突解析</li>
<li><strong>社群與工具生態</strong>：Redis/Valkey 的 client library、monitoring exporter、Operator 支援度更廣</li>
</ul>
<h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>完全相容（fork 自 Redis 6.x）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>active-active → Sentinel/Cluster；multi-thread config 移除</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>相同</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>相近（1 primary + N replica + HA）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>endpoint 換、client config 微調</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>RDB/AOF 完全相容</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>Type B drop-in，工作重心在 active-active replication 拆除和效能 baseline 對齊。</p>
<h2 id="keydb-特有功能的處理">KeyDB 特有功能的處理</h2>
<table>
  <thead>
      <tr>
          <th>KeyDB 特有功能</th>
          <th>Redis/Valkey 對應</th>
          <th>遷移處理</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Multi-threading（<code>server-threads</code>）</td>
          <td>Redis I/O threads / Valkey 8 async I/O</td>
          <td>回到 Redis 後吞吐量下降是預期，需要 benchmark 建立新 baseline</td>
      </tr>
      <tr>
          <td>Active-active replication</td>
          <td>無原生等價。Redis 需要 application 層解衝突或用 CRDTs（社群方案）</td>
          <td>遷移前確認業務是否仍需 multi-master。不需要則直接切 Sentinel/Cluster</td>
      </tr>
      <tr>
          <td>FLASH storage（<code>storage-provider flash</code>）</td>
          <td>無原生等價。Redis 純記憶體</td>
          <td>遷移前把 FLASH 資料回收到記憶體，或接受遷移後記憶體需求上升。調整 <code>maxmemory</code></td>
      </tr>
      <tr>
          <td>Subkey expires</td>
          <td>Redis 無 subkey expire（只有 top-level key TTL）</td>
          <td>檢查 application 是否依賴 subkey expire；若有需要改寫為 top-level key 或用 sorted set 模擬</td>
      </tr>
      <tr>
          <td><code>EXPIREMEMBER</code> 命令</td>
          <td>Redis 無此命令</td>
          <td>grep application code 確認未使用；若有需改寫</td>
      </tr>
  </tbody>
</table>
<p>FLASH storage 的處理取決於冷資料比例。如果多數資料在 FLASH 上（用 <code>OBJECT FREQ</code> 確認），遷移後的 Redis 記憶體需求會大幅上升 — 要提前計算純記憶體所需容量，調整 instance 規格或改用更積極的 eviction policy。Subkey expires 和 <code>EXPIREMEMBER</code> 的影響範圍通常較小，但一旦 application 依賴就需要重構資料結構（用 top-level key + TTL 或 sorted set 模擬過期）。</p>
<h3 id="active-active-拆除">Active-active 拆除</h3>
<p>若 KeyDB 的 active-active replication 正在使用，遷移前需要先收斂為單主寫入：</p>
<ol>
<li>選定一個 region 的 KeyDB 為 primary，其他 region 停止寫入</li>
<li>等資料同步完成（replica 追上 primary offset）</li>
<li>從 primary 做 RDB export</li>
<li>用 RDB 建立 Redis/Valkey instance</li>
<li>各 region 的 application 切到新的 Redis/Valkey（Sentinel 或 Cluster）</li>
</ol>
<h2 id="資料搬遷">資料搬遷</h2>
<p>KeyDB 的 RDB 和 AOF 與 Redis 格式相容，搬遷流程跟 DragonflyDB 回退類似：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># KeyDB 端觸發 BGSAVE</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli -h keydb-host BGSAVE
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 複製 RDB 到 Redis/Valkey 資料目錄</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">scp keydb-host:/data/dump.rdb redis-host:/data/dump.rdb
</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"># Redis/Valkey 載入</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">redis-server --dbfilename dump.rdb --dir /data</span></span></code></pre></div><p>如果使用了 FLASH storage，RDB 只包含記憶體中的資料。FLASH 上的冷資料需要先用 <code>OBJECT FREQ</code> 確認存取頻率，決定是要 warm up 到記憶體再 export，還是接受遷移後冷資料 cache miss 回源。</p>
<h2 id="效能差異預期">效能差異預期</h2>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>KeyDB → Redis 變化</th>
          <th>應對</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>吞吐量</td>
          <td>下降（KeyDB multi-thread → Redis single-thread）</td>
          <td>評估是否需要 Cluster 分片補償。Valkey 8 的 async I/O 可部分彌補</td>
      </tr>
      <tr>
          <td>記憶體</td>
          <td>上升（若使用了 FLASH storage 被移除）</td>
          <td>提前計算純記憶體所需容量，調整 instance 規格</td>
      </tr>
      <tr>
          <td>Latency p99</td>
          <td>BGSAVE fork spike 可能出現</td>
          <td>KeyDB 的多線程降低了 fork 影響，回到 Redis 需要關注 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence fork latency</a></td>
      </tr>
      <tr>
          <td>Active-active latency</td>
          <td>不適用（已拆除）</td>
          <td>N/A</td>
      </tr>
  </tbody>
</table>
<h2 id="回退路徑">回退路徑</h2>
<p>Cache 資料可重建，回退方式：</p>
<ol>
<li>Application endpoint 改回 KeyDB</li>
<li>若 KeyDB 已下線，重啟 KeyDB 載入 Redis 的 RDB（格式相容）</li>
<li>Cache miss 回源到 DB 自然 warm up</li>
</ol>
<p>KeyDB 保留 7 天再下線。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/keydb/" data-link-title="KeyDB" data-link-desc="Redis multi-threaded fork、active-replication、Snap 採用">KeyDB</a>、<a href="/blog/backend/02-cache-redis/vendors/keydb/active-active-replication/" data-link-title="KeyDB active-active 多主複製：last-write-wins 會默默吃掉哪一筆寫入" data-link-desc="KeyDB 的 active-active 讓兩個 master 都能寫、互相同步，聽起來解決了跨區寫入的所有問題——直到兩邊同時寫同一個 key，last-write-wins 默默丟掉其中一筆。本文展開 active-active 的複製機制與衝突語意、實機驗證雙向同步、5 個把多主複製寫成資料遺失與迴圈的 production 踩坑，以及哪些資料能放 active-active、哪些不能的邊界">KeyDB Active-Active Replication</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>HA 重建：<a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a></li>
<li>效能參考：<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">Persistence Fork Latency</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/connection-pipeline-latency/" data-link-title="Redis 連線與 pipeline：RTT 稅、連線池與一次往返打包多命令" data-link-desc="Redis 單命令通常微秒級執行，但 application 端量到的延遲是毫秒級——差距全在網路往返（RTT）。pipelining 的本質不是『批次發命令』，是把 N 次 RTT 壓成 1 次。本文展開 RTT 會計、連線池配置、pipeline 與 MULTI 的差異、5 個把連線與往返寫成延遲與正確性問題的 production 踩坑，以及連線模型撞牆的邊界">Connection Pipeline Latency</a></li>
</ul>
]]></content:encoded></item><item><title>Redis → Valkey：同一份程式碼、不同授權的 drop-in 遷移</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-valkey/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-valkey/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type B drop-in&lt;/strong>（全維度 Low），結構走 6-section + 相容性 audit 前置。實機驗證於 valkey/valkey:8（valkey_version 8.1.8、redis_version 7.2.4）、最後檢查日 2026-06-16。&lt;/p>&lt;/blockquote>
&lt;h2 id="同一份程式碼不同授權">同一份程式碼、不同授權&lt;/h2>
&lt;p>多數 migration 的工作量在「source 跟 target 不一樣」——schema 要翻譯、API 要改、資料要轉。Redis → Valkey 幾乎沒有這個問題：Valkey 是 2024 年從 Redis 7.2.4 直接 fork 出來的，那一刻它跟 Redis 是 bit-for-bit 同一份程式碼。RDB 與 AOF 檔案格式相同（可以直接把 Redis 的資料目錄拷給 Valkey 載入）、RESP 協定相同、所有 Redis client library 不改一行就能連。技術上，這是 cache 領域最容易的遷移。&lt;/p>
&lt;p>那為什麼要寫一篇 playbook？因為這個遷移的工作量不在資料層，在兩個別的地方。第一是&lt;strong>授權&lt;/strong>——Redis 2024 改成 RSALv2 / SSPL（非 OSI 認可），Valkey 是 BSD 3-clause（OSI、Linux Foundation 治理），這個遷移的整個 driver 是授權合規，而合規驗證有它自己的流程。第二是&lt;strong>fork 後的分歧&lt;/strong>——fork 那一刻兩者相同，但之後各自演進：Redis 加了 7.4+ 的新功能、Valkey 加了自己的（如 8.x 多執行緒），用到 fork 之後 Redis 新功能的部署會有相容缺口。&lt;/p>
&lt;p>&lt;code>INFO server&lt;/code> 上看得到這個「同源但分歧」的事實：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">valkey-cli INFO server &lt;span class="p">|&lt;/span> grep -E &lt;span class="s2">&amp;#34;redis_version|valkey_version&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># redis_version:7.2.4 ← fork 點、client 以此判斷相容性（裝成 Redis 7.2.4）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># valkey_version:8.1.8 ← Valkey 自己的演進線&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>redis_version:7.2.4&lt;/code> 是相容性的保證（client 看到就以 Redis 7.2.4 行為運作）；&lt;code>valkey_version&lt;/code> 是分歧的證據。這篇 playbook 處理的就是「資料層幾乎零工作、工作在授權與分歧盤點」的 drop-in 遷移。&lt;/p>
&lt;h2 id="6-維-diff-dimension-audit為什麼是-type-b">6 維 diff dimension audit：為什麼是 Type B&lt;/h2>
&lt;p>跑 diff dimension audit，Redis → Valkey 全維度 Low：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>同 Redis 7.2.4（fork 同源）、RESP 協定一致&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>同 redis.conf、同監控指標、同 CLI 命令&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>完全相同（同一份 code base 演進）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>1 → 1（單服務換單服務）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>零（所有 Redis client library 直接相容）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>RDB / AOF 檔案相容、可直接拷資料目錄&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>全 Low → &lt;strong>Type B drop-in&lt;/strong>（6-section + 相容性 audit 前置、週期 1-4 週）。跟同模組的 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &amp;#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB&lt;/a> 對照：DragonflyDB 是 C++ 重寫（drop-in 但 Lua / encoding / module 有差異），Valkey 是 fork（同源、連 RDB 檔都相容）——Valkey 的相容度比 DragonflyDB 更高，是 Type B 裡最純粹的一端。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。跑 6 維 diff dimension audit 後判定為 <strong>Type B drop-in</strong>（全維度 Low），結構走 6-section + 相容性 audit 前置。實機驗證於 valkey/valkey:8（valkey_version 8.1.8、redis_version 7.2.4）、最後檢查日 2026-06-16。</p></blockquote>
<h2 id="同一份程式碼不同授權">同一份程式碼、不同授權</h2>
<p>多數 migration 的工作量在「source 跟 target 不一樣」——schema 要翻譯、API 要改、資料要轉。Redis → Valkey 幾乎沒有這個問題：Valkey 是 2024 年從 Redis 7.2.4 直接 fork 出來的，那一刻它跟 Redis 是 bit-for-bit 同一份程式碼。RDB 與 AOF 檔案格式相同（可以直接把 Redis 的資料目錄拷給 Valkey 載入）、RESP 協定相同、所有 Redis client library 不改一行就能連。技術上，這是 cache 領域最容易的遷移。</p>
<p>那為什麼要寫一篇 playbook？因為這個遷移的工作量不在資料層，在兩個別的地方。第一是<strong>授權</strong>——Redis 2024 改成 RSALv2 / SSPL（非 OSI 認可），Valkey 是 BSD 3-clause（OSI、Linux Foundation 治理），這個遷移的整個 driver 是授權合規，而合規驗證有它自己的流程。第二是<strong>fork 後的分歧</strong>——fork 那一刻兩者相同，但之後各自演進：Redis 加了 7.4+ 的新功能、Valkey 加了自己的（如 8.x 多執行緒），用到 fork 之後 Redis 新功能的部署會有相容缺口。</p>
<p><code>INFO server</code> 上看得到這個「同源但分歧」的事實：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">valkey-cli INFO server <span class="p">|</span> grep -E <span class="s2">&#34;redis_version|valkey_version&#34;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># redis_version:7.2.4    ← fork 點、client 以此判斷相容性（裝成 Redis 7.2.4）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># valkey_version:8.1.8   ← Valkey 自己的演進線</span></span></span></code></pre></div><p><code>redis_version:7.2.4</code> 是相容性的保證（client 看到就以 Redis 7.2.4 行為運作）；<code>valkey_version</code> 是分歧的證據。這篇 playbook 處理的就是「資料層幾乎零工作、工作在授權與分歧盤點」的 drop-in 遷移。</p>
<h2 id="6-維-diff-dimension-audit為什麼是-type-b">6 維 diff dimension audit：為什麼是 Type B</h2>
<p>跑 diff dimension audit，Redis → Valkey 全維度 Low：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>同 Redis 7.2.4（fork 同源）、RESP 協定一致</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>同 redis.conf、同監控指標、同 CLI 命令</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>完全相同（同一份 code base 演進）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>1 → 1（單服務換單服務）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>零（所有 Redis client library 直接相容）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>RDB / AOF 檔案相容、可直接拷資料目錄</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>全 Low → <strong>Type B drop-in</strong>（6-section + 相容性 audit 前置、週期 1-4 週）。跟同模組的 <a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a> 對照：DragonflyDB 是 C++ 重寫（drop-in 但 Lua / encoding / module 有差異），Valkey 是 fork（同源、連 RDB 檔都相容）——Valkey 的相容度比 DragonflyDB 更高，是 Type B 裡最純粹的一端。</p>
<p>這個遷移的特殊之處是 driver 在資料層之外：它是<strong>授權 / 合規驅動</strong>。依 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration 方法論</a> 的漏類處理，政策 / 合規驅動的遷移資料層仍走 Type B，但 audit 重點多一塊<strong>授權驗證與證據收集</strong>。</p>
<h2 id="相容性-auditcutover-前要確認的清單">相容性 audit：cutover 前要確認的清單</h2>
<p>Valkey 號稱 100% 相容 Redis 7.2.4，但「100%」的邊界在 fork 之後的分歧。Pre-migration 必跑的 audit：</p>
<table>
  <thead>
      <tr>
          <th>Redis feature</th>
          <th>Valkey 相容程度</th>
          <th>Action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Core data types / commands / RESP</td>
          <td>完全相容（fork 自 7.2.4）</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>RDB / AOF 檔案格式</td>
          <td>完全相容（可直接拷資料目錄）</td>
          <td>無需轉檔</td>
      </tr>
      <tr>
          <td>Eviction / persistence / pub-sub</td>
          <td>完全相容</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>Client libraries</td>
          <td>完全相容（透過 redis_version 協商）</td>
          <td>無需改 code</td>
      </tr>
      <tr>
          <td>Cluster / Sentinel</td>
          <td>完全相容（同 Redis 模型）</td>
          <td>無需處理</td>
      </tr>
      <tr>
          <td>Redis 7.4+ 新功能（fork 後新增）</td>
          <td>Valkey 不一定跟進</td>
          <td>盤點是否用到、確認 Valkey 對應</td>
      </tr>
      <tr>
          <td>Redis Stack 商業 module（JSON/Search）</td>
          <td>不相容（Valkey 有 valkey-search / valkey-bloom）</td>
          <td>盤點 module 使用、確認替代或改寫</td>
      </tr>
      <tr>
          <td>RedisInsight 等 Redis Inc 監控工具</td>
          <td>部分 vendor-specific 命令缺</td>
          <td>改通用工具（valkey-cli / redis_exporter）</td>
      </tr>
  </tbody>
</table>
<p><strong>audit 的關鍵 output</strong>：兩份清單——(1) 用到的 Redis 7.4+ 功能（fork 後新增、Valkey 可能沒有）、(2) 載入的 Redis Stack module。這兩塊是僅有的相容風險，其餘資料層零工作。盤點方法：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 盤點載入的 module（最大相容風險）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">redis-cli MODULE LIST
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 盤點是否用到 7.4+ 功能（抓 production traffic 對照 Redis 7.4 changelog）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">redis-cli MONITOR    <span class="c1"># 限時抓樣、grep 可疑的新命令</span></span></span></code></pre></div><h2 id="step-by-step-cutover">Step-by-step cutover</h2>
<p>因為 RDB 檔案相容，cutover 比 DragonflyDB 更簡單（無版本轉換風險）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 部署 Valkey（同 Redis 配置、可直接沿用 redis.conf）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">docker run -d --name valkey -p 6380:6379 <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  -v /data/valkey:/data <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>  valkey/valkey:8 valkey-server /etc/valkey/valkey.conf
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># 2. Redis 端 BGSAVE 產生 RDB</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">redis-cli -h redis-primary BGSAVE
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">redis-cli -h redis-primary INFO Persistence <span class="p">|</span> grep rdb_last_save_time
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 3. 把 dump.rdb 拷給 Valkey（檔案格式相容、無需轉換）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">scp redis-primary:/var/lib/redis/dump.rdb valkey-host:/data/valkey/
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 4. 重啟 Valkey 載入 RDB</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">docker restart valkey
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 5. 驗證資料一致 + 版本</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">valkey-cli -h valkey-host -p <span class="m">6380</span> DBSIZE          <span class="c1"># 對齊 Redis DBSIZE</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">valkey-cli -h valkey-host -p <span class="m">6380</span> INFO server <span class="p">|</span> grep redis_version  <span class="c1"># 7.2.4</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"># 6. 替代方案（零停機）：用 replicaof 讓 Valkey 當 Redis 的 replica、即時同步後 promote</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">#    valkey-cli -h valkey-host REPLICAOF redis-primary 6379</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">#    重要邊界：此路徑只在 source 是 Redis 7.2 或更早版本時成立。</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1">#    Redis 7.4+（Community Edition）改了複製格式、Valkey 無法當其 replica</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1">#    → source 為 7.4+ 時改走上面的 RDB 拷貝路徑（步驟 2-4）。</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"># 7. Cutover：client 配置切到 Valkey endpoint、Redis 留 standby</span></span></span></code></pre></div><p>關鍵時間點：</p>
<ul>
<li><strong>RDB 拷貝 + load</strong>：100GB 約 5-15 分鐘（無版本轉換、比 DragonflyDB 少一道風險）</li>
<li><strong>replicaof 路徑</strong>：要零停機可讓 Valkey 當 Redis replica 即時同步、確認 lag 趨零後 promote + 切 client（僅限 source 為 Redis 7.2 或更早；7.4+ 複製格式已分歧、不適用、改走 RDB 拷貝）</li>
<li><strong>Cutover</strong>：client 配置切換（單次完成、硬邊界）、Redis 留 standby 1-2 週</li>
<li><strong>Decom</strong>：無相容問題後關閉 Redis</li>
</ul>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1用到-redis-74-功能valkey-沒有">Case 1：用到 Redis 7.4+ 功能、Valkey 沒有</h3>
<p><strong>徵兆</strong>：cutover 後某功能報 <code>unknown command</code> 或行為不同，命令是 Redis 在 7.4 之後（fork 點之後）才加的。</p>
<p><strong>根因</strong>：Valkey fork 自 Redis 7.2.4，Redis 7.4+ 新增的功能 Valkey 不一定跟進。pre-migration audit 漏掉了這些 fork 後的新功能。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>pre-migration 對照 Redis 7.4+ changelog 盤點用到的新功能（audit 清單第一項）</li>
<li>Valkey 有對應就確認版本、沒有就評估改寫或留在 Redis 商業版</li>
<li>多數標準 cache 用法不碰 7.4+ 新功能，這個風險集中在用了較新進階功能的部署</li>
<li>Valkey 自己的 roadmap（valkey.io）會逐步補上 Redis 新功能，可追蹤</li>
</ol>
<h3 id="case-2載入了-redis-stack-商業-module">Case 2：載入了 Redis Stack 商業 module</h3>
<p><strong>徵兆</strong>：cutover 後 <code>JSON.SET</code> / <code>FT.SEARCH</code> 報 <code>unknown command</code>，application 部分功能失效。</p>
<p><strong>根因</strong>：用了 Redis Stack 的商業 module（RedisJSON / RedisSearch），這些不在 fork 範圍。Valkey 有自己的 valkey-search / valkey-bloom，但不是同一套命令、要另外安裝。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>pre-migration <code>MODULE LIST</code> 盤點所有載入的 module（audit 清單第二項）</li>
<li>確認 Valkey 對應替代（valkey-search 對 RedisSearch）、確認命令相容度</li>
<li>沒有對應的評估改 module-free 設計（JSON 操作拉回 application 層）或留在 Redis Inc 商業版</li>
<li>對應 <a href="/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/" data-link-title="Valkey 相容性驗證與 io-threads 調校：drop-in 切換與多執行緒的實機判讀" data-link-desc="Valkey 跟 Redis 100% 相容這句話要怎麼驗證、切換才敢上線。本文用 INFO server 的雙版本回報拆解相容性的真實邊界、展開 Valkey 8 的 io-threads 多執行緒調校、5 個把 drop-in 切換或執行緒配置寫成事故的 production 踩坑，以及相容性撞牆該怎麼判斷的邊界">Valkey 相容性 deep article</a> 的三層相容邊界</li>
</ol>
<h3 id="case-3以為換-valkey-解決了記憶體--fork-問題">Case 3：以為換 Valkey 解決了記憶體 / fork 問題</h3>
<p><strong>徵兆</strong>：因為 Redis 的 OOM 或 fork 延遲尖峰而遷 Valkey，遷完發現同樣問題還在。</p>
<p><strong>根因</strong>：Valkey fork 自 Redis 7.2.4，繼承了完全相同的記憶體模型、eviction 演算法、AOF/RDB fork 機制。這些行為在 Valkey 上一模一樣——遷移沒有改變它們。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>記憶體 / fork 調校在 Valkey 上跟 Redis 完全相同，直接套用 <a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">Redis 記憶體調校</a> 與 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence / fork latency</a></li>
<li>遷 Valkey 的理由應是授權合規 / 多執行緒吞吐 / managed 成本，不是記憶體問題</li>
<li>fork 尖峰要根治走 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a> 的 fork-less，不是換 Valkey</li>
<li>遷移前釐清痛點是授權（Valkey 解）還是架構（Valkey 不解）</li>
</ol>
<h3 id="case-4授權合規驗證沒做完整合規卡關">Case 4：授權合規驗證沒做完整、合規卡關</h3>
<p><strong>徵兆</strong>：技術遷移完成、但法務 / 合規 review 要求證明「不再使用 RSALv2 / SSPL 授權的軟體」，缺少證據。</p>
<p><strong>根因</strong>：這個遷移的 driver 是授權合規，但團隊只做了技術 cutover、沒收集合規證據。Redis 的 binary / image / 相依套件若還殘留在某些環境，合規目標沒真正達成。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>盤點所有環境（dev / staging / prod / CI）的 Redis binary / image / 相依，確認全部換成 Valkey</li>
<li>收集合規證據：image SBOM、套件清單、部署 manifest 顯示 Valkey BSD 授權</li>
<li>把「不再使用非 OSI 授權 cache」寫成可驗證的 CI 檢查（掃 image / 依賴）</li>
<li>依 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration 方法論</a> 的合規驅動漏類，audit 重點就是 evidence collection</li>
</ol>
<h3 id="case-5監控-dashboard-部分指標斷掉">Case 5：監控 dashboard 部分指標斷掉</h3>
<p><strong>徵兆</strong>：cutover 後 RedisInsight 或某監控 dashboard 部分面板空白、vendor-specific 命令回錯。</p>
<p><strong>根因</strong>：RedisInsight 等 Redis Inc 工具有部分偏商業版的命令，Valkey 不一定實作。核心指標通用，但進階面板可能缺。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>監控改用通用工具：valkey-cli INFO、Prometheus + redis_exporter（相容 Valkey）、Grafana</li>
<li>核心指標（used_memory / keyspace_hits / connected_clients）在 Valkey 完全相容、覆蓋不受影響</li>
<li>把監控相容性納入 cutover 前驗證、不要遷完才發現面板空白</li>
<li>RedisInsight 連 Valkey 多數仍可用、只是部分 vendor 進階面板缺</li>
</ol>
<h2 id="capacity--cost-對照">Capacity / cost 對照</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Redis（self-managed）</th>
          <th>Valkey（self-managed）</th>
          <th>取捨</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>授權</td>
          <td>RSALv2 / SSPL（非 OSI）</td>
          <td>BSD 3-clause（OSI、Linux Foundation）</td>
          <td>Valkey 對合規敏感場景是決定性優勢</td>
      </tr>
      <tr>
          <td>核心效能</td>
          <td>baseline</td>
          <td>同 Redis 7.2.4 + 8.x 多執行緒選項</td>
          <td>Valkey 多核 workload 可更高（依 workload）</td>
      </tr>
      <tr>
          <td>相容度</td>
          <td>原生</td>
          <td>100%（fork、檔案相容）</td>
          <td>平手（同源）</td>
      </tr>
      <tr>
          <td>記憶體 / fork</td>
          <td>baseline</td>
          <td>完全相同（同源）</td>
          <td>平手（遷移不改變這層）</td>
      </tr>
      <tr>
          <td>7.4+ 新功能</td>
          <td>有</td>
          <td>不一定跟進</td>
          <td>Redis 領先（用到才在意）</td>
      </tr>
      <tr>
          <td>Redis Stack module</td>
          <td>RedisJSON / Search / Graph</td>
          <td>valkey-search / valkey-bloom（不同套）</td>
          <td>Redis 商業 module 較全</td>
      </tr>
      <tr>
          <td>managed 選項</td>
          <td>ElastiCache for Redis（legacy）</td>
          <td>ElastiCache for Valkey（AWS default、約低 20%）</td>
          <td>Valkey 在 AWS 生態成本優勢</td>
      </tr>
      <tr>
          <td>遷移成本</td>
          <td>—</td>
          <td>極低（drop-in + 檔案相容）</td>
          <td>Valkey 是最容易的遷移目標</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：合規敏感（公部門 / 企業 OSI 政策）或想降 managed 成本 → 遷 Valkey（drop-in、風險集中在 module / 7.4+ 盤點）；重度依賴 Redis Stack 商業 module → 留 Redis Inc 商業版。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-elasticache-for-valkey-對位">跟 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">ElastiCache for Valkey</a> 對位</h3>
<p>AWS 已把 ElastiCache default engine 設為 Valkey（約低 Redis 20%）。自管 Redis → ElastiCache for Valkey 是「換授權 + 轉 managed」一次到位，但要同時處理 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/managed-responsibility-boundary/" data-link-title="AWS ElastiCache 的責任邊界：managed 接手了什麼、又默默留下什麼" data-link-desc="ElastiCache 把 failover、patching、snapshot、跨 AZ 複製接走，但 cache stampede、client 重連、key 設計、eviction policy 還是你的事。本文用 shared responsibility 拆解 managed 的真實邊界、展開 engine 選擇與 cluster mode 配置、5 個把『以為 AWS 全包』寫成事故的 production 踩坑，以及 ElastiCache 到 MemoryDB 的 durability 邊界">managed 責任邊界</a>（failover / cluster mode / client 重連）。</p>
<h3 id="跟-client--監控整合">跟 client / 監控整合</h3>
<p>client library 零改（透過 redis_version 協商）；監控把 exporter 指向 Valkey 即可（redis_exporter 相容）、RedisInsight 部分面板需換通用工具。</p>
<h3 id="跟-valkey-8-多執行緒對位">跟 Valkey 8 多執行緒對位</h3>
<p>遷移後可評估開 Valkey 8 的 io-threads 榨多核吞吐（Redis 7.2.4 沒有的能力），見 <a href="/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/" data-link-title="Valkey 相容性驗證與 io-threads 調校：drop-in 切換與多執行緒的實機判讀" data-link-desc="Valkey 跟 Redis 100% 相容這句話要怎麼驗證、切換才敢上線。本文用 INFO server 的雙版本回報拆解相容性的真實邊界、展開 Valkey 8 的 io-threads 多執行緒調校、5 個把 drop-in 切換或執行緒配置寫成事故的 production 踩坑，以及相容性撞牆該怎麼判斷的邊界">Valkey 相容性與 io-threads deep article</a>。</p>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>反向遷移</strong>（Valkey → Redis）：僅在重度依賴 Redis 7.4+ 功能或 Stack 商業 module 時需要、同樣 drop-in</li>
<li><strong>跨雲 managed Valkey</strong>：GCP Memorystore / Azure Cache 的 Valkey 支援陸續推出、評估 vendor boundary</li>
<li><strong>授權合規 CI 化</strong>：把「不使用非 OSI 授權 cache」寫成持續檢查</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a></li>
<li>Target vendor：<a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>平行 migration playbook：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-dragonflydb/" data-link-title="Redis → DragonflyDB：drop-in 相容下的容量躍升 &#43; 5 個踩雷" data-link-desc="DragonflyDB 號稱 Redis drop-in 替代、單機 throughput 25x、記憶體效率 30% 提升；遷移流程簡單但有 5 個 production 踩雷（RDB 版本差 / Lua 腳本不全支援 / Pub-Sub fanout 行為差異 / Cluster mode 兼容度 / Modules 不支援）、跟 Sentinel / Cluster 模式對位">Redis → DragonflyDB</a>（重寫型 drop-in）、<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-memcached/" data-link-title="Redis → Memcached：Memcached 不是 simpler Redis、是 cache paradigm" data-link-desc="Redis → Memcached 是 Type E paradigm reduction migration — 從 multi-paradigm（KV &#43; 資料結構 &#43; pub/sub &#43; Lua &#43; streams）退到 pure cache；不是「remove Redis features」、是「重新分配 Redis-specific feature 到對應 specialized 服務」；5 個 production 踩雷 &#43; paradigm reduction 路線">Redis → Memcached</a></li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a>（Type B drop-in + 合規驅動漏類）</li>
</ul>
]]></content:encoded></item><item><title>Valkey 相容性驗證與 io-threads 調校：drop-in 切換與多執行緒的實機判讀</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/redis-compatibility-and-io-threads/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a> overview 的 implementation-layer deep article。選型層（為何 fork、授權治理、何時選 Valkey）見 overview；本文只處理「決定用 Valkey 後，相容性怎麼驗、執行緒怎麼調」。命令實機驗證於 &lt;code>valkey/valkey:8&lt;/code> image（valkey_version 8.1.8）、最後檢查日 2026-06-16；效能數字以 &lt;a href="https://valkey.io/blog/">valkey.io 官方 benchmark&lt;/a> 為準。&lt;/p>&lt;/blockquote>
&lt;h2 id="100-相容要能驗證才敢切">「100% 相容」要能驗證才敢切&lt;/h2>
&lt;p>Valkey 從 Redis 7.2.4 fork、宣稱 100% API 相容、drop-in 替換——這對選型是好消息，對上線前的工程師卻是一個需要證據的斷言。把 production 的 Redis 換成 Valkey，最怕的不是「大部分指令能跑」，而是某個邊角行為、某個 client library 的版本協商、某個 module 沒有對應 fork，在切換後才浮現。相容性不能靠信任，要靠驗證。&lt;/p>
&lt;p>驗證的起點是一個容易被忽略的細節：Valkey 的 &lt;code>INFO server&lt;/code> 同時回報兩個版本號。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">docker &lt;span class="nb">exec&lt;/span> valkey valkey-cli INFO server &lt;span class="p">|&lt;/span> grep -E &lt;span class="s2">&amp;#34;redis_version|valkey_version|server_name&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># redis_version:7.2.4 ← client library 以此協商相容行為&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="c1"># server_name:valkey&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># valkey_version:8.1.8 ← Valkey 自身的演進線&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這個雙版本回報就是相容性的機制本身：client library 看到 &lt;code>redis_version:7.2.4&lt;/code>，就以 Redis 7.2.4 的協定與行為運作，完全不知道背後是 Valkey；&lt;code>valkey_version&lt;/code> 才是 Valkey 自己的版本，記錄它在 fork 之後加了什麼（例如 8.x 的多執行緒）。理解這條雙線——「對外裝成 Redis 7.2.4、對內持續演進」——是判斷相容性邊界的鑰匙。&lt;/p>
&lt;p>對大規模生產驗證，&lt;a href="https://tarrragon.github.io/blog/backend/09-performance-capacity/cases/tinder-elasticache-valkey-matching/" data-link-title="9.C6 Tinder：ElastiCache for Valkey 撐 4700 萬月活的配對引擎" data-link-desc="Tinder 用 Amazon ElastiCache for Valkey 提供配對引擎所需的次毫秒延遲快取層">Tinder 的配對引擎&lt;/a>是現成的證據：4700 萬月活、每次滑動讀多個 cache、sub-millisecond 延遲，跑在 Amazon ElastiCache for Valkey 上——這個規模的服務跑在 Valkey 上，本身就是相容性的背書。另一個訊號是 AWS 在 2024 把 ElastiCache 的 default engine 從 Redis 改成 Valkey（AWS 宣稱成本較 Redis OSS 低約 20%、以 &lt;a href="https://aws.amazon.com/elasticache/pricing/">ElastiCache 定價&lt;/a> 為準、最後檢查日 2026-06-16）。這些都是外部背書，但各服務有自己的 client library、module 與邊角用法，仍需自行驗證。&lt;/p>
&lt;h2 id="核心概念相容性的三層邊界">核心概念：相容性的三層邊界&lt;/h2>
&lt;p>「100% 相容」在不同層次有不同的精確度，驗證要分三層做。&lt;/p>
&lt;p>&lt;strong>協定與核心指令層：完全相容&lt;/strong>。string / hash / list / set / sorted set / stream / hyperloglog / geo 的所有指令、TTL / eviction / persistence / pub-sub / transaction、RESP 協定——這層是 fork 自 Redis 7.2.4 的部分，行為一致。所有標準 Redis client library 透過 &lt;code>redis_version&lt;/code> 協商，直接連、不改 code。&lt;/p>
&lt;p>&lt;strong>檔案格式層：相容&lt;/strong>。RDB 與 AOF 的檔案格式跟 Redis 7.2.4 一致，可以直接把 Redis 的資料目錄拷給 Valkey 載入——這是 drop-in 遷移的基礎，不需要 dump / reload。&lt;/p>
&lt;p>&lt;strong>生態與新功能層：要逐項確認&lt;/strong>。Redis 7.4+ 在 fork 之後新增的功能（Valkey 不一定跟進）、Redis Stack 的商業 module（RedisJSON / RedisSearch，Valkey 有自己的 valkey-search / valkey-bloom 但不是同一套）、偏 Redis Inc 的監控工具（RedisInsight 部分 vendor-specific 命令）——這層是相容性的真實風險所在，驗證要集中在這裡。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a> overview 的 implementation-layer deep article。選型層（為何 fork、授權治理、何時選 Valkey）見 overview；本文只處理「決定用 Valkey 後，相容性怎麼驗、執行緒怎麼調」。命令實機驗證於 <code>valkey/valkey:8</code> image（valkey_version 8.1.8）、最後檢查日 2026-06-16；效能數字以 <a href="https://valkey.io/blog/">valkey.io 官方 benchmark</a> 為準。</p></blockquote>
<h2 id="100-相容要能驗證才敢切">「100% 相容」要能驗證才敢切</h2>
<p>Valkey 從 Redis 7.2.4 fork、宣稱 100% API 相容、drop-in 替換——這對選型是好消息，對上線前的工程師卻是一個需要證據的斷言。把 production 的 Redis 換成 Valkey，最怕的不是「大部分指令能跑」，而是某個邊角行為、某個 client library 的版本協商、某個 module 沒有對應 fork，在切換後才浮現。相容性不能靠信任，要靠驗證。</p>
<p>驗證的起點是一個容易被忽略的細節：Valkey 的 <code>INFO server</code> 同時回報兩個版本號。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker <span class="nb">exec</span> valkey valkey-cli INFO server <span class="p">|</span> grep -E <span class="s2">&#34;redis_version|valkey_version|server_name&#34;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># redis_version:7.2.4    ← client library 以此協商相容行為</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># server_name:valkey</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># valkey_version:8.1.8   ← Valkey 自身的演進線</span></span></span></code></pre></div><p>這個雙版本回報就是相容性的機制本身：client library 看到 <code>redis_version:7.2.4</code>，就以 Redis 7.2.4 的協定與行為運作，完全不知道背後是 Valkey；<code>valkey_version</code> 才是 Valkey 自己的版本，記錄它在 fork 之後加了什麼（例如 8.x 的多執行緒）。理解這條雙線——「對外裝成 Redis 7.2.4、對內持續演進」——是判斷相容性邊界的鑰匙。</p>
<p>對大規模生產驗證，<a href="/blog/backend/09-performance-capacity/cases/tinder-elasticache-valkey-matching/" data-link-title="9.C6 Tinder：ElastiCache for Valkey 撐 4700 萬月活的配對引擎" data-link-desc="Tinder 用 Amazon ElastiCache for Valkey 提供配對引擎所需的次毫秒延遲快取層">Tinder 的配對引擎</a>是現成的證據：4700 萬月活、每次滑動讀多個 cache、sub-millisecond 延遲，跑在 Amazon ElastiCache for Valkey 上——這個規模的服務跑在 Valkey 上，本身就是相容性的背書。另一個訊號是 AWS 在 2024 把 ElastiCache 的 default engine 從 Redis 改成 Valkey（AWS 宣稱成本較 Redis OSS 低約 20%、以 <a href="https://aws.amazon.com/elasticache/pricing/">ElastiCache 定價</a> 為準、最後檢查日 2026-06-16）。這些都是外部背書，但各服務有自己的 client library、module 與邊角用法，仍需自行驗證。</p>
<h2 id="核心概念相容性的三層邊界">核心概念：相容性的三層邊界</h2>
<p>「100% 相容」在不同層次有不同的精確度，驗證要分三層做。</p>
<p><strong>協定與核心指令層：完全相容</strong>。string / hash / list / set / sorted set / stream / hyperloglog / geo 的所有指令、TTL / eviction / persistence / pub-sub / transaction、RESP 協定——這層是 fork 自 Redis 7.2.4 的部分，行為一致。所有標準 Redis client library 透過 <code>redis_version</code> 協商，直接連、不改 code。</p>
<p><strong>檔案格式層：相容</strong>。RDB 與 AOF 的檔案格式跟 Redis 7.2.4 一致，可以直接把 Redis 的資料目錄拷給 Valkey 載入——這是 drop-in 遷移的基礎，不需要 dump / reload。</p>
<p><strong>生態與新功能層：要逐項確認</strong>。Redis 7.4+ 在 fork 之後新增的功能（Valkey 不一定跟進）、Redis Stack 的商業 module（RedisJSON / RedisSearch，Valkey 有自己的 valkey-search / valkey-bloom 但不是同一套）、偏 Redis Inc 的監控工具（RedisInsight 部分 vendor-specific 命令）——這層是相容性的真實風險所在，驗證要集中在這裡。</p>
<p>驗證的操作順序：先確認 client library 連得上且核心指令正常（第一層），再確認資料能載入（第二層），最後盤點你實際用到的 module 與 7.4+ 功能（第三層）。前兩層幾乎必過，工夫花在第三層。</p>
<h2 id="配置io-threads-多執行緒調校">配置：io-threads 多執行緒調校</h2>
<p>Valkey 跟 Redis 7.2.4 拉開的第一個實質技術差異是執行緒模型。Redis 的命令處理是單執行緒（I/O threads 只分擔 socket 讀寫，命令仍在主執行緒），Valkey 8.x 把更多 I/O 路徑非同步化，在多核機器上能讓單實例吞吐明顯高於 Redis——具體倍數依 workload 與核數而定，以 <a href="https://valkey.io/blog/">valkey.io 官方 benchmark</a> 為準，這裡不複述未經自己壓測的數字。</p>
<p>執行緒由 <code>io-threads</code> 控制，預設 1（單執行緒，跟 Redis 行為一致）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 確認目前執行緒數（預設 1）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">valkey-cli CONFIG GET io-threads
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 1) &#34;io-threads&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2) &#34;1&#34;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># 調高 I/O 執行緒數（建議不超過機器實體核數、留核給其他進程）</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># redis.conf / valkey.conf:</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1">#   io-threads 4</span></span></span></code></pre></div><p>調校判讀：</p>
<ul>
<li><code>io-threads</code> 是啟動參數，多數版本需要重啟生效（不是所有 CONFIG SET 都能熱套），改 conf 後 rolling restart</li>
<li>設定值對齊機器核數但留 headroom，例如 8 核機器設 4-6，不要設滿</li>
<li>單核或低核機器設 1（預設）即可，多執行緒在核數不足時沒有收益反而增加切換開銷</li>
<li>I/O 密集（大量小命令、高連線數）的 workload 收益最明顯；CPU 密集的重命令（大 Lua、大 collection 操作）收益有限</li>
</ul>
<p>調完用實際 workload 壓測驗證，不要假設「開了就快」——執行緒配置的收益高度依賴 workload 形狀。</p>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1切換後-module-指令報-unknown-command">Case 1：切換後 module 指令報 unknown command</h3>
<p><strong>徵兆</strong>：drop-in 換成 Valkey 後核心功能正常，但某些路徑報 <code>ERR unknown command 'JSON.SET'</code> 或 <code>FT.SEARCH</code>，application 部分功能失效。</p>
<p><strong>根因</strong>：用到了 Redis Stack 的商業 module（RedisJSON / RedisSearch）。這些 module 不在 fork 範圍內，Valkey 有自己的 valkey-search / valkey-bloom，但不是同一套指令、需要另外安裝。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>切換前用 <code>MODULE LIST</code> 在原 Redis 上盤點所有載入的 module</li>
<li>逐個確認 Valkey 是否有對應替代（valkey-search 對 RedisSearch 等），確認指令相容度</li>
<li>沒有對應的 module，評估改用 module-free 設計（例如把 JSON 操作拉回 application 層）</li>
<li>重度依賴 Redis Stack 商業 module 的場景，相容性邊界在這裡，可能該留在 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> Inc 商業版</li>
</ol>
<h3 id="case-2client-library-太舊協商失敗">Case 2：client library 太舊、協商失敗</h3>
<p><strong>徵兆</strong>：絕大多數 client 正常，但某個老服務的 client library 連 Valkey 報協定錯誤或行為異常。</p>
<p><strong>根因</strong>：Valkey 回報 <code>redis_version:7.2.4</code>，client library 若太舊（不支援 Redis 7.2 對應的協定特性，例如 RESP3）會協商失敗。這不是 Valkey 的問題，是 client 本來就跟不上 Redis 7.2。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><code>valkey-cli INFO server</code> 確認回報的 <code>redis_version</code>，對照 client library 支援到哪個 Redis 版本</li>
<li>升級過舊的 client library 到支援 Redis 7.2 的版本</li>
<li>必要時 client 端強制用 RESP2（多數 library 可配置），避開 RESP3 協商</li>
<li>這類問題在升級 Redis 7.2 時也會遇到，不是 Valkey 特有</li>
</ol>
<h3 id="case-3監控工具部分指標消失">Case 3：監控工具部分指標消失</h3>
<p><strong>徵兆</strong>：切換後 RedisInsight 或某監控 dashboard 部分面板空白、某些 vendor-specific 命令回錯。</p>
<p><strong>根因</strong>：RedisInsight 等 Redis Inc 工具有部分偏 Redis 商業版的命令，Valkey 不一定實作。核心指標（memory / hit rate / connections）通用，但 vendor-specific 的進階面板可能缺。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>監控改用通用工具：<code>valkey-cli INFO</code>、Prometheus + redis_exporter（相容 Valkey）、Grafana</li>
<li>核心指標（<code>used_memory</code> / <code>keyspace_hits</code> / <code>connected_clients</code>）在 Valkey 完全相容，監控覆蓋不受影響</li>
<li>把監控的相容性納入切換前驗證清單，不要切換後才發現面板空白</li>
<li>對應 <a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">記憶體</a> 與 <a href="/blog/backend/02-cache-redis/vendors/redis/connection-pipeline-latency/" data-link-title="Redis 連線與 pipeline：RTT 稅、連線池與一次往返打包多命令" data-link-desc="Redis 單命令通常微秒級執行，但 application 端量到的延遲是毫秒級——差距全在網路往返（RTT）。pipelining 的本質不是『批次發命令』，是把 N 次 RTT 壓成 1 次。本文展開 RTT 會計、連線池配置、pipeline 與 MULTI 的差異、5 個把連線與往返寫成延遲與正確性問題的 production 踩坑，以及連線模型撞牆的邊界">連線</a> 調校用到的 INFO 指標，這些在 Valkey 都通用</li>
</ol>
<h3 id="case-4io-threads-開太多效能反而下降">Case 4：io-threads 開太多、效能反而下降</h3>
<p><strong>徵兆</strong>：把 <code>io-threads</code> 從 1 調到 16 想榨效能，結果延遲不降反升、CPU 使用率異常。</p>
<p><strong>根因</strong>：<code>io-threads</code> 設成超過機器實體核數，執行緒互搶 CPU、context switch 開銷超過平行收益。或 workload 是 CPU 密集（重命令），I/O 多執行緒對它沒幫助。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><code>io-threads</code> 不超過實體核數，留 headroom 給 OS 與其他進程（8 核設 4-6）</li>
<li>用實際 workload 壓測對比不同 io-threads 值的延遲與吞吐，不要憑感覺調滿</li>
<li>CPU 密集 workload 收益有限，問題可能在命令本身太重（大 collection / 大 Lua），先優化命令</li>
<li>多執行緒解的是 I/O 平行度，不是單命令執行速度，分清楚瓶頸在哪</li>
</ol>
<h3 id="case-5以為換-valkey-就解決了-redis-的記憶體--fork-問題">Case 5：以為換 Valkey 就解決了 Redis 的記憶體 / fork 問題</h3>
<p><strong>徵兆</strong>：因為 Redis 的 fork 延遲尖峰或記憶體 OOM 而切到 Valkey，切完發現同樣的尖峰與 OOM 還在。</p>
<p><strong>根因</strong>：Valkey fork 自 Redis 7.2.4，繼承了 Redis 的記憶體模型、eviction 演算法、AOF/RDB fork 機制。這些行為在 Valkey 上完全一致——Valkey 的差異在執行緒與授權，不在記憶體與持久化架構。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>記憶體 / 淘汰 / fork 的調校在 Valkey 上跟 Redis 完全一樣，直接套用 <a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">Redis 記憶體調校</a> 與 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence / fork latency</a></li>
<li>fork 尖峰是 Redis 系列的共同架構限制，要根治走 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a> 的 fork-less 機制，不是換 Valkey</li>
<li>切換 Valkey 的理由應該是授權合規、多執行緒吞吐或 managed 成本，不是記憶體問題</li>
<li>切換前釐清痛點：是授權 / 成本（Valkey 解）還是記憶體 / fork 架構（Valkey 不解）</li>
</ol>
<h2 id="capacity--cost-邊界">Capacity / cost 邊界</h2>
<p>Valkey 的容量判讀，多數沿用 Redis（同源），差異集中在執行緒與授權成本：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Valkey 的情況</th>
          <th>判讀</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>核心指標（記憶體 / hit rate）</td>
          <td>跟 Redis 完全一致</td>
          <td>直接套用 Redis 的容量判讀</td>
      </tr>
      <tr>
          <td><code>io-threads</code></td>
          <td>預設 1、可調至接近核數</td>
          <td>多核 + I/O 密集才有收益、需壓測驗證</td>
      </tr>
      <tr>
          <td>單實例吞吐</td>
          <td>多執行緒下高於 Redis（依 workload）</td>
          <td>以 valkey.io benchmark 為準、自己壓測</td>
      </tr>
      <tr>
          <td>授權成本</td>
          <td>BSD 3-clause、商業使用無限制</td>
          <td>合規敏感場景的決定性優勢</td>
      </tr>
      <tr>
          <td>managed 成本</td>
          <td>ElastiCache for Valkey 約低 Redis 20%</td>
          <td>AWS 生態的成本優化路徑</td>
      </tr>
  </tbody>
</table>
<p>撞牆後的路由判斷：</p>
<ul>
<li><strong>記憶體 / fork 是瓶頸</strong>：Valkey 同源、不解這層，走 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a>（fork-less + 更省記憶體）或 Redis 系列的 <a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Cluster 分片</a>。</li>
<li><strong>需要 Redis Stack 商業 module</strong>：Valkey 的 valkey-search / valkey-bloom 覆蓋不到全部，重度依賴走 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> Inc 商業版。</li>
<li><strong>不想自管</strong>：<a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">ElastiCache for Valkey</a> 是 AWS 的 default engine，managed failover / snapshot / patching 全託管，成本比 ElastiCache for Redis 低約 20%。</li>
</ul>
<h2 id="整合--下一步">整合 / 下一步</h2>
<p>Valkey 的 deep article 大量複用 Redis 的調校知識（同源），它自己的獨特性在相容性驗證、執行緒與授權：</p>
<ul>
<li><strong>跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis 全系列 deep article</a></strong>：記憶體、持久化、Sentinel、連線的調校在 Valkey 上完全一致，Valkey 不重寫這些，直接套用。</li>
<li><strong>跟 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">ElastiCache for Valkey</a></strong>：managed Valkey 把執行緒與 failover 託管，省掉自管的調校與演練。</li>
<li><strong>跟 <a href="/blog/backend/09-performance-capacity/cases/tinder-elasticache-valkey-matching/" data-link-title="9.C6 Tinder：ElastiCache for Valkey 撐 4700 萬月活的配對引擎" data-link-desc="Tinder 用 Amazon ElastiCache for Valkey 提供配對引擎所需的次毫秒延遲快取層">Tinder 的 ElastiCache for Valkey 案例</a></strong>：4700 萬月活的 sub-millisecond 配對引擎是相容性與規模化的生產證據，但 module / client 的相容性仍需逐案驗證。</li>
<li><strong>跟 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a></strong>：兩者都打「Redis 相容 + 更好的執行緒」，但 Valkey 是 fork（同源、最高相容），DragonflyDB 是 C++ 重寫（相容核心但架構不同），選型差異在相容度 vs 架構激進度。</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>上游 vendor 頁：<a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></li>
<li>同源 deep article：<a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">Redis 記憶體與淘汰調校</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence 與 fork latency</a></li>
<li>平行 vendor：<a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">AWS ElastiCache</a>（default engine 即 Valkey）、<a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a></li>
<li>Methodology：<a href="/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">Vendor 深度技術文章寫作方法論</a></li>
</ul>
]]></content:encoded></item><item><title>ElastiCache → 自管 Redis / Valkey：脫離 managed 的遷移路徑</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/aws-elasticache/migrate-to-self-managed/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/aws-elasticache/migrate-to-self-managed/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link 到 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">AWS ElastiCache&lt;/a>（source）跟 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey&lt;/a>（target）。跑 6 維 diff dimension audit 後判定為 &lt;strong>Type C operational redesign hybrid&lt;/strong>：engine 層相容（Low）但 operational model 差異大（IAM auth → password/ACL、CloudWatch → 自管監控、auto failover → Sentinel/自建 HA）。&lt;/p>&lt;/blockquote>
&lt;h2 id="為什麼從-managed-遷出">為什麼從 managed 遷出&lt;/h2>
&lt;p>ElastiCache 遷出的 driver 通常不是 engine 層問題 — 它跑的就是 Redis 或 Valkey。常見遷出原因：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>成本&lt;/strong>：managed premium 在大規模（數百 GB、多叢集）下比自管 + 運維人力更貴，尤其跨帳戶大量叢集時&lt;/li>
&lt;li>&lt;strong>跨雲或混合雲&lt;/strong>：業務需要在 GCP、Azure 或 on-prem 同時運行 cache 層，ElastiCache 只在 AWS&lt;/li>
&lt;li>&lt;strong>功能限制&lt;/strong>：ElastiCache 不支援所有 Redis module（RediSearch、RedisJSON 等），或 Valkey 8.x 新功能 ElastiCache 尚未上線&lt;/li>
&lt;li>&lt;strong>控制權&lt;/strong>：自管可以自訂 redis.conf、自選 kernel 參數、自決 upgrade 時機&lt;/li>
&lt;/ul>
&lt;p>資料搬遷用 RDB export + import 就完成，真正的工程量在 operational model 重建 — ElastiCache 幫你管的 HA、monitoring、backup、security，遷出後全要自建。&lt;/p>
&lt;h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>同 Redis/Valkey engine、RESP 相容&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>IAM auth → ACL/password、CloudWatch → 自管監控、auto failover → Sentinel 或手動&lt;/td>
 &lt;td>High&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>相同（key-value cache）&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Number of components&lt;/td>
 &lt;td>ElastiCache 1 → Redis/Valkey + Sentinel/HA + 監控 + backup 多元件&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>endpoint 換、認證方式換、少量 client config 修改&lt;/td>
 &lt;td>Low-Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>RDB 相容、cluster mode 對應 Redis Cluster&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Operational model 是 High — 這是 Type C 的判定依據。遷移重心在重建 ElastiCache 幫你做的那些事。&lt;/p>
&lt;h2 id="階段一盤點-elasticache-依賴">階段一：盤點 ElastiCache 依賴&lt;/h2>
&lt;p>在動手之前，先列出 ElastiCache 幫你管的所有東西，每一項都要在自管環境重建或決定不要。&lt;/p>
&lt;h3 id="認證與網路">認證與網路&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>IAM auth&lt;/strong>：ElastiCache 支援 IAM auth token（短效 token），自管 Redis 改用 &lt;code>requirepass&lt;/code> 或 Redis 6+ ACL&lt;/li>
&lt;li>&lt;strong>VPC / Security Group&lt;/strong>：自管 Redis 仍需 VPC 隔離，但 security group 規則要自己維護&lt;/li>
&lt;li>&lt;strong>TLS&lt;/strong>：ElastiCache 原生 in-transit encryption，自管要自己配 redis TLS 憑證&lt;/li>
&lt;/ul>
&lt;h3 id="高可用">高可用&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Auto failover&lt;/strong>：ElastiCache 自動偵測 primary failure 並 promote replica。自管用 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA failover&lt;/a> 或 Redis Cluster 內建 failover&lt;/li>
&lt;li>&lt;strong>Cross-AZ replication&lt;/strong>：ElastiCache 自動跨 AZ。自管要自己在不同 AZ 部署 replica&lt;/li>
&lt;/ul>
&lt;h3 id="監控與備份">監控與備份&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>CloudWatch metrics&lt;/strong>：ElastiCache 自動發 &lt;code>CurrConnections&lt;/code>、&lt;code>CacheHitRate&lt;/code>、&lt;code>ReplicationLag&lt;/code> 等。自管用 &lt;code>INFO&lt;/code> 指令 + Prometheus redis_exporter&lt;/li>
&lt;li>&lt;strong>Snapshot&lt;/strong>：ElastiCache 自動 daily snapshot + 手動 snapshot。自管用 &lt;code>BGSAVE&lt;/code> + cron + 外部 storage&lt;/li>
&lt;/ul>
&lt;h3 id="跨-region-replication">跨 region replication&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Global Datastore&lt;/strong>：ElastiCache 支援跨 region active-passive replication。自管 Redis 沒有原生跨 region replication — 若目前使用 Global Datastore，遷出前需要決定是用 application-level replication、第三方工具（Redis Enterprise Active-Active）還是放棄跨 region cache 同步&lt;/li>
&lt;/ul>
&lt;h3 id="升級與維護">升級與維護&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Engine 升級&lt;/strong>：ElastiCache 在維護窗口自動或手動升級。自管要自己做 rolling upgrade&lt;/li>
&lt;li>&lt;strong>Patch&lt;/strong>：安全 patch 由 AWS 負責。自管要自己追蹤 CVE&lt;/li>
&lt;/ul>
&lt;h2 id="階段二建立自管環境">階段二：建立自管環境&lt;/h2>
&lt;h3 id="部署架構">部署架構&lt;/h3>
&lt;p>最小 production 架構：1 primary + 1 replica + 3 Sentinel（或 Redis Cluster 3 primary + 3 replica）。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link 到 <a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">AWS ElastiCache</a>（source）跟 <a href="/blog/backend/02-cache-redis/vendors/redis/" data-link-title="Redis" data-link-desc="OSS in-memory data structure store、cache 主流">Redis</a> / <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（target）。跑 6 維 diff dimension audit 後判定為 <strong>Type C operational redesign hybrid</strong>：engine 層相容（Low）但 operational model 差異大（IAM auth → password/ACL、CloudWatch → 自管監控、auto failover → Sentinel/自建 HA）。</p></blockquote>
<h2 id="為什麼從-managed-遷出">為什麼從 managed 遷出</h2>
<p>ElastiCache 遷出的 driver 通常不是 engine 層問題 — 它跑的就是 Redis 或 Valkey。常見遷出原因：</p>
<ul>
<li><strong>成本</strong>：managed premium 在大規模（數百 GB、多叢集）下比自管 + 運維人力更貴，尤其跨帳戶大量叢集時</li>
<li><strong>跨雲或混合雲</strong>：業務需要在 GCP、Azure 或 on-prem 同時運行 cache 層，ElastiCache 只在 AWS</li>
<li><strong>功能限制</strong>：ElastiCache 不支援所有 Redis module（RediSearch、RedisJSON 等），或 Valkey 8.x 新功能 ElastiCache 尚未上線</li>
<li><strong>控制權</strong>：自管可以自訂 redis.conf、自選 kernel 參數、自決 upgrade 時機</li>
</ul>
<p>資料搬遷用 RDB export + import 就完成，真正的工程量在 operational model 重建 — ElastiCache 幫你管的 HA、monitoring、backup、security，遷出後全要自建。</p>
<h2 id="6-維-diff-dimension-audit">6 維 diff dimension audit</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>同 Redis/Valkey engine、RESP 相容</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>IAM auth → ACL/password、CloudWatch → 自管監控、auto failover → Sentinel 或手動</td>
          <td>High</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>相同（key-value cache）</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>ElastiCache 1 → Redis/Valkey + Sentinel/HA + 監控 + backup 多元件</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>endpoint 換、認證方式換、少量 client config 修改</td>
          <td>Low-Medium</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>RDB 相容、cluster mode 對應 Redis Cluster</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>Operational model 是 High — 這是 Type C 的判定依據。遷移重心在重建 ElastiCache 幫你做的那些事。</p>
<h2 id="階段一盤點-elasticache-依賴">階段一：盤點 ElastiCache 依賴</h2>
<p>在動手之前，先列出 ElastiCache 幫你管的所有東西，每一項都要在自管環境重建或決定不要。</p>
<h3 id="認證與網路">認證與網路</h3>
<ul>
<li><strong>IAM auth</strong>：ElastiCache 支援 IAM auth token（短效 token），自管 Redis 改用 <code>requirepass</code> 或 Redis 6+ ACL</li>
<li><strong>VPC / Security Group</strong>：自管 Redis 仍需 VPC 隔離，但 security group 規則要自己維護</li>
<li><strong>TLS</strong>：ElastiCache 原生 in-transit encryption，自管要自己配 redis TLS 憑證</li>
</ul>
<h3 id="高可用">高可用</h3>
<ul>
<li><strong>Auto failover</strong>：ElastiCache 自動偵測 primary failure 並 promote replica。自管用 <a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA failover</a> 或 Redis Cluster 內建 failover</li>
<li><strong>Cross-AZ replication</strong>：ElastiCache 自動跨 AZ。自管要自己在不同 AZ 部署 replica</li>
</ul>
<h3 id="監控與備份">監控與備份</h3>
<ul>
<li><strong>CloudWatch metrics</strong>：ElastiCache 自動發 <code>CurrConnections</code>、<code>CacheHitRate</code>、<code>ReplicationLag</code> 等。自管用 <code>INFO</code> 指令 + Prometheus redis_exporter</li>
<li><strong>Snapshot</strong>：ElastiCache 自動 daily snapshot + 手動 snapshot。自管用 <code>BGSAVE</code> + cron + 外部 storage</li>
</ul>
<h3 id="跨-region-replication">跨 region replication</h3>
<ul>
<li><strong>Global Datastore</strong>：ElastiCache 支援跨 region active-passive replication。自管 Redis 沒有原生跨 region replication — 若目前使用 Global Datastore，遷出前需要決定是用 application-level replication、第三方工具（Redis Enterprise Active-Active）還是放棄跨 region cache 同步</li>
</ul>
<h3 id="升級與維護">升級與維護</h3>
<ul>
<li><strong>Engine 升級</strong>：ElastiCache 在維護窗口自動或手動升級。自管要自己做 rolling upgrade</li>
<li><strong>Patch</strong>：安全 patch 由 AWS 負責。自管要自己追蹤 CVE</li>
</ul>
<h2 id="階段二建立自管環境">階段二：建立自管環境</h2>
<h3 id="部署架構">部署架構</h3>
<p>最小 production 架構：1 primary + 1 replica + 3 Sentinel（或 Redis Cluster 3 primary + 3 replica）。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Docker Compose 驗證用（production 用 VM 或 K8s）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># Primary</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">docker run -d --name redis-primary -p 6379:6379 redis:7 <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>  redis-server --requirepass <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span> --appendonly yes
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># Replica</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">docker run -d --name redis-replica -p 6380:6379 redis:7 <span class="se">\
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="se"></span>  redis-server --replicaof redis-primary <span class="m">6379</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="se"></span>  --masterauth <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span> --requirepass <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span></span></span></code></pre></div><p>Sentinel 或 Redis Cluster 配置見 <a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Sentinel HA Failover</a>。</p>
<h3 id="監控重建">監控重建</h3>
<p>ElastiCache CloudWatch metrics 對應的自管替代：</p>
<table>
  <thead>
      <tr>
          <th>ElastiCache metric</th>
          <th>自管替代</th>
          <th>來源</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CurrConnections</td>
          <td><code>connected_clients</code></td>
          <td><code>INFO clients</code></td>
      </tr>
      <tr>
          <td>CacheHitRate</td>
          <td><code>keyspace_hits / (keyspace_hits + keyspace_misses)</code></td>
          <td><code>INFO stats</code></td>
      </tr>
      <tr>
          <td>ReplicationLag</td>
          <td><code>master_repl_offset - slave_repl_offset</code></td>
          <td><code>INFO replication</code></td>
      </tr>
      <tr>
          <td>EngineCPUUtilization</td>
          <td><code>used_cpu_sys + used_cpu_user</code></td>
          <td><code>INFO cpu</code></td>
      </tr>
      <tr>
          <td>DatabaseMemoryUsagePercentage</td>
          <td><code>used_memory / maxmemory</code></td>
          <td><code>INFO memory</code></td>
      </tr>
      <tr>
          <td>Evictions</td>
          <td><code>evicted_keys</code></td>
          <td><code>INFO stats</code></td>
      </tr>
  </tbody>
</table>
<p>用 <a href="https://github.com/oliver006/redis_exporter">Prometheus redis_exporter</a> 自動採集，接 Grafana dashboard。</p>
<h3 id="backup-重建">Backup 重建</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># cron job: 每日 BGSAVE + 等完成 + 上傳 S3</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># LASTSAVE 回傳 Unix timestamp，BGSAVE 完成後 LASTSAVE 會更新</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="m">0</span> <span class="m">3</span> * * * <span class="nv">BEFORE</span><span class="o">=</span><span class="k">$(</span>redis-cli -a <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span> LASTSAVE<span class="k">)</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>  redis-cli -a <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span> BGSAVE <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="se"></span>  <span class="k">while</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="k">$(</span>redis-cli -a <span class="s2">&#34;</span><span class="nv">$REDIS_PASSWORD</span><span class="s2">&#34;</span> LASTSAVE<span class="k">)</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="nv">$BEFORE</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">do</span> sleep 5<span class="p">;</span> <span class="k">done</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="se"></span>  aws s3 cp /data/dump.rdb s3://backup-bucket/redis/<span class="k">$(</span>date +<span class="se">\%</span>Y<span class="se">\%</span>m<span class="se">\%</span>d<span class="k">)</span>.rdb</span></span></code></pre></div><p>Production 建議搭配 <a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">persistence fork latency</a> 的監控，確認 BGSAVE 的 fork 不會造成延遲 spike。</p>
<h2 id="階段三資料搬遷與切換">階段三：資料搬遷與切換</h2>
<h3 id="搬遷策略">搬遷策略</h3>
<p>ElastiCache 的資料搬遷有兩條路：</p>
<p><strong>RDB export + import（適合 downtime 可接受的場景）</strong>：</p>
<ol>
<li>ElastiCache 建立手動 snapshot</li>
<li>把 snapshot export 到 S3（ElastiCache console → Export snapshot）</li>
<li>下載 RDB 檔，放到自管 Redis 的資料目錄</li>
<li>重啟自管 Redis 載入 RDB</li>
</ol>
<p><strong>雙寫期間遷移（適合零停機需求）</strong>：</p>
<ol>
<li>Application 同時寫 ElastiCache 和自管 Redis（雙寫）</li>
<li>讀取仍走 ElastiCache</li>
<li>監控自管 Redis 的資料量與命中率追上後，切讀取到自管</li>
<li>移除 ElastiCache 寫入</li>
<li>下線 ElastiCache</li>
</ol>
<p>雙寫的複雜度高於 RDB export。Cache 資料可重建的特性讓第一種策略在多數場景夠用 — 短暫 cache miss 的代價是回源到 DB，通常可接受。</p>
<h3 id="endpoint-切換">Endpoint 切換</h3>
<p>Application 用 endpoint 連 ElastiCache。切換時：</p>
<ol>
<li>把 application config 的 Redis host 改為自管 Redis endpoint</li>
<li>確認 TLS 與認證方式對齊（IAM token → password/ACL）</li>
<li>Rolling restart application</li>
<li>監控 cache hit rate 與 latency 回到 baseline</li>
</ol>
<p>如果用 DNS CNAME 間接指向 ElastiCache endpoint，可以直接改 CNAME 指向自管 Redis，application 不用改 config。</p>
<h2 id="階段四驗證與回退">階段四：驗證與回退</h2>
<h3 id="驗證清單">驗證清單</h3>
<table>
  <thead>
      <tr>
          <th>驗證項目</th>
          <th>通過條件</th>
          <th>工具</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>連線正常</td>
          <td>application 能 PING、無 auth error</td>
          <td>redis-cli + application log</td>
      </tr>
      <tr>
          <td>資料完整</td>
          <td>key count 跟 ElastiCache 一致（容許 TTL 過期差異）</td>
          <td><code>DBSIZE</code> 比對</td>
      </tr>
      <tr>
          <td>效能 baseline</td>
          <td>latency p99 與 hit rate 跟遷移前一致</td>
          <td>Prometheus + Grafana</td>
      </tr>
      <tr>
          <td>HA 測試</td>
          <td>kill primary，Sentinel promote replica，application 自動重連</td>
          <td>手動 failover drill</td>
      </tr>
      <tr>
          <td>Backup 測試</td>
          <td>BGSAVE 產生 RDB、上傳成功、可還原</td>
          <td>還原到測試 instance 驗證</td>
      </tr>
  </tbody>
</table>
<h3 id="回退路徑">回退路徑</h3>
<p>Cache 遷移的回退比 DB 遷移簡單 — cache 資料可重建。回退步驟：</p>
<ol>
<li>Application config 改回 ElastiCache endpoint（或 CNAME 指回）</li>
<li>Rolling restart</li>
<li>Cache miss 回源到 DB，自然 warm up</li>
</ol>
<p>ElastiCache 在遷移期間不要下線，保留 7-14 天作為回退保險。確認自管 Redis 穩定運行後再刪除 ElastiCache cluster。</p>
<h2 id="成本對照">成本對照</h2>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>ElastiCache</th>
          <th>自管 Redis</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Compute</td>
          <td>managed node pricing（含 premium）</td>
          <td>EC2 / K8s 原價</td>
      </tr>
      <tr>
          <td>HA</td>
          <td>auto failover 內建</td>
          <td>Sentinel 或 Cluster 自建</td>
      </tr>
      <tr>
          <td>監控</td>
          <td>CloudWatch 內建</td>
          <td>redis_exporter + Prometheus 自建</td>
      </tr>
      <tr>
          <td>Backup</td>
          <td>自動 snapshot</td>
          <td>cron + S3 自建</td>
      </tr>
      <tr>
          <td>人力</td>
          <td>低（AWS 管）</td>
          <td>高（on-call + upgrade + patch）</td>
      </tr>
      <tr>
          <td>靈活度</td>
          <td>受限（engine version、module）</td>
          <td>完全自控</td>
      </tr>
  </tbody>
</table>
<p>小規模（&lt; 50 GB、&lt; 5 cluster）通常 ElastiCache 的 managed premium 比自管人力便宜。Compute 跟 HA 的差額在小規模可忽略，但監控跟 backup 的自建成本是固定開銷 — 即使只管一個 cluster，redis_exporter + Prometheus + cron backup 的設定跟維護都要做。大規模（數百 GB、多叢集）或跨雲場景下，managed premium 累積到 cluster 數 × node 數的倍數，自管的邊際成本反而更低，遷出 ROI 才成立。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>Source vendor overview：<a href="/blog/backend/02-cache-redis/vendors/aws-elasticache/" data-link-title="AWS ElastiCache" data-link-desc="AWS managed Redis / Valkey / Memcached">AWS ElastiCache</a></li>
<li>Target vendor 操作：<a href="/blog/backend/02-cache-redis/vendors/redis/sentinel-ha-failover/" data-link-title="Redis Sentinel 與 failover 時序：從 master 死掉到 client 重連的每一段" data-link-desc="Redis Sentinel 的 failover 不是一個瞬間動作，是 down 偵測 → quorum 確認 → 選主 → 提升 → 配置廣播 → client 重連的一條時序鏈，每一段都有自己的延遲與失敗模式。本文展開 Sentinel 的判定模型與這條時序、5 個讓 failover 卡住或丟資料的 production 踩坑，以及 Sentinel 撐不住該往 Cluster 或 managed 走的邊界">Redis Sentinel HA</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/cluster-resharding/" data-link-title="Redis Cluster Re-sharding：source = target，但 topology 重劃的 5 段流程" data-link-desc="Redis cluster re-sharding 是 5 type migration 漏類實證 — source / target 同 cluster、無 schema / paradigm 差、但 16384 slot 重分配是核心；本文涵蓋 4 種 re-sharding driver、slot migration 機制、redis-cli --cluster rebalance / reshard 工具、5 個 production 踩雷（cluster busy / replica lag / client cache stale / cross-slot transaction / monitor gap）">Redis Cluster Resharding</a></li>
<li>監控重建：<a href="/blog/backend/02-cache-redis/vendors/redis/memory-eviction-tuning/" data-link-title="Redis 記憶體與淘汰調校：maxmemory-policy、LFU 與碎片化的實戰判讀" data-link-desc="Redis 的記憶體是一條會在半夜爆掉的曲線：maxmemory 設多少、policy 選 LRU 還 LFU、碎片化什麼時候開始吃掉 30% RAM、OOM 時 noeviction 怎麼讓寫入全部失敗。本文展開 Redis 記憶體會計模型、eviction policy 的選型判讀、5 個把記憶體配置寫成 production 事故的踩坑，以及單機記憶體撞牆後該往 cluster 還是 DragonflyDB 走的邊界">Redis Memory Eviction Tuning</a>、<a href="/blog/backend/02-cache-redis/vendors/redis/persistence-fork-latency/" data-link-title="Redis 持久化與 fork latency：AOF、RDB 與那一次卡住整個 cluster 的 fork" data-link-desc="Redis 的 RDB save 與 AOF rewrite 都靠一次 fork()，而 fork 在大記憶體實例上會凍結主執行緒數百毫秒、複製分頁讓記憶體逼近翻倍。本文展開 AOF / RDB 的機制與 fsync 取捨、copy-on-write 的記憶體放大、5 個把持久化寫成延遲尖峰與資料遺失的 production 踩坑，以及 cache 場景到底要不要持久化的邊界">Redis Persistence Fork Latency</a></li>
<li>反向路徑：<a href="/blog/backend/02-cache-redis/vendors/redis/migrate-to-elasticache/" data-link-title="自管 Redis / Valkey → AWS ElastiCache：engine 不變、變的是誰運維" data-link-desc="自管 Redis/Valkey 遷到 ElastiCache 的特殊之處：engine 沒變（Redis 還是 Redis）、data model 沒變、API 沒變——變的只有運維責任歸屬。本文跑 6 維 diff audit 對映 Type C operational hybrid、展開 VPC/安全/cutover 的實際工作、以及『把 failover/patching 交出去、同時交出哪些控制權』的責任邊界，5 個 production 踩坑">Redis → ElastiCache</a></li>
</ul>
]]></content:encoded></item></channel></rss>