<?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>Capability-Upgrade on Tarragon</title><link>https://tarrragon.github.io/blog/tags/capability-upgrade/</link><description>Recent content in Capability-Upgrade on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 16 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/capability-upgrade/index.xml" rel="self" type="application/rss+xml"/><item><title>Memcached → Redis：不搬資料、搬存取層的能力升級遷移</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/memcached/migrate-to-redis/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/vendors/memcached/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/memcached/" data-link-title="Memcached" data-link-desc="純記憶體 key-value cache、無持久化">Memcached&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>（target）。跑 &lt;a href="https://tarrragon.github.io/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 6 維 audit&lt;/a> 後對映 Schema/API + application change High、但 &lt;strong>data topology Low（cache 可重建）&lt;/strong>——本文是「能力升級 + 資料層免遷」的 dogfood，跟反向的 &lt;a href="https://tarrragon.github.io/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 &amp;#43; 資料結構 &amp;#43; pub/sub &amp;#43; Lua &amp;#43; streams）退到 pure cache；不是「remove Redis features」、是「重新分配 Redis-specific feature 到對應 specialized 服務」；5 個 production 踩雷 &amp;#43; paradigm reduction 路線">Redis → Memcached（Type E paradigm reduction）&lt;/a> 對位。&lt;/p>&lt;/blockquote>
&lt;h2 id="cache-遷移不搬資料搬存取層">cache 遷移不搬資料、搬存取層&lt;/h2>
&lt;p>一般 migration 最重、最危險的部分是搬資料——schema 要對、一致性要保、cutover 要不丟。Memcached → Redis 把這塊幾乎拿掉了，因為 &lt;strong>cache 的資料本來就是可重建的副本&lt;/strong>。遷移不需要把 Memcached 裡的東西搬到 Redis；讓 Redis 空著上線、cache miss 自然回源、命中率慢慢 warm 起來即可。Memcached 在 warm-up 期間繼續服務，等 Redis 命中率追上來再切。&lt;/p>
&lt;p>這個性質讓 Memcached → Redis 的工作重心完全不同：不在資料層，在&lt;strong>存取層&lt;/strong>（換 client library、換協定）跟&lt;strong>可選的能力升級&lt;/strong>。觸發這個遷移的通常是「outgrew pure KV」——本來只用 Memcached 存 string，後來需要 counter（用 application 層讀-改-寫硬湊、有 race）、需要 session 物件（serialize 整包 JSON、改一個欄位要全寫）、需要 leaderboard（在 app 排序、慢）。這些 Redis 用 INCR / Hash / Sorted Set 原生解，把 application 層硬湊的邏輯收回 cache 層。&lt;/p>
&lt;p>本文跑 diff audit 確認這個形狀、用兩階段結構（先 drop-in、再升級能力）展開遷移與踩坑。&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>Memcached 協定 → Redis RESP、純 string → 可選 data types&lt;/td>
 &lt;td>High&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>Redis 多了 eviction policy / persistence / cluster 決策&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abstraction / paradigm&lt;/td>
 &lt;td>pure cache → data structure store（但可先維持 pure KV 用法）&lt;/td>
 &lt;td>Medium&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>client library 換、可選改用 data types&lt;/td>
 &lt;td>High&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Data topology&lt;/strong>&lt;/td>
 &lt;td>&lt;strong>cache 可重建、不搬資料、re-warm&lt;/strong>&lt;/td>
 &lt;td>&lt;strong>Low&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>主導維度是 Schema/API + application change（存取層），但這個 migration 的特徵是 &lt;strong>data topology Low&lt;/strong>——這是 cache 類遷移獨有的性質。對映 &lt;a href="https://tarrragon.github.io/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 方法論&lt;/a> 的 type：本文是 &lt;strong>cache 類 Type A 的簡化變體&lt;/strong>（phased translation 的存取層翻譯，但因 data topology Low 省掉了資料遷移階段）。結構上採兩階段：&lt;strong>Phase 1 drop-in 替換（維持 pure KV 用法、先把 client 換掉）&lt;/strong>，&lt;strong>Phase 2 漸進採用 data types（把 application 層硬湊的邏輯收回 Redis）&lt;/strong>。Phase 2 是可選的、可以慢慢來。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link <a href="/blog/backend/02-cache-redis/vendors/memcached/" data-link-title="Memcached" data-link-desc="純記憶體 key-value cache、無持久化">Memcached</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>（target）。跑 <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 6 維 audit</a> 後對映 Schema/API + application change High、但 <strong>data topology Low（cache 可重建）</strong>——本文是「能力升級 + 資料層免遷」的 dogfood，跟反向的 <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（Type E paradigm reduction）</a> 對位。</p></blockquote>
<h2 id="cache-遷移不搬資料搬存取層">cache 遷移不搬資料、搬存取層</h2>
<p>一般 migration 最重、最危險的部分是搬資料——schema 要對、一致性要保、cutover 要不丟。Memcached → Redis 把這塊幾乎拿掉了，因為 <strong>cache 的資料本來就是可重建的副本</strong>。遷移不需要把 Memcached 裡的東西搬到 Redis；讓 Redis 空著上線、cache miss 自然回源、命中率慢慢 warm 起來即可。Memcached 在 warm-up 期間繼續服務，等 Redis 命中率追上來再切。</p>
<p>這個性質讓 Memcached → Redis 的工作重心完全不同：不在資料層，在<strong>存取層</strong>（換 client library、換協定）跟<strong>可選的能力升級</strong>。觸發這個遷移的通常是「outgrew pure KV」——本來只用 Memcached 存 string，後來需要 counter（用 application 層讀-改-寫硬湊、有 race）、需要 session 物件（serialize 整包 JSON、改一個欄位要全寫）、需要 leaderboard（在 app 排序、慢）。這些 Redis 用 INCR / Hash / Sorted Set 原生解，把 application 層硬湊的邏輯收回 cache 層。</p>
<p>本文跑 diff audit 確認這個形狀、用兩階段結構（先 drop-in、再升級能力）展開遷移與踩坑。</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>Memcached 協定 → Redis RESP、純 string → 可選 data types</td>
          <td>High</td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>Redis 多了 eviction policy / persistence / cluster 決策</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Abstraction / paradigm</td>
          <td>pure cache → data structure store（但可先維持 pure KV 用法）</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Number of components</td>
          <td>1 → 1</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>client library 換、可選改用 data types</td>
          <td>High</td>
      </tr>
      <tr>
          <td><strong>Data topology</strong></td>
          <td><strong>cache 可重建、不搬資料、re-warm</strong></td>
          <td><strong>Low</strong></td>
      </tr>
  </tbody>
</table>
<p>主導維度是 Schema/API + application change（存取層），但這個 migration 的特徵是 <strong>data topology Low</strong>——這是 cache 類遷移獨有的性質。對映 <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：本文是 <strong>cache 類 Type A 的簡化變體</strong>（phased translation 的存取層翻譯，但因 data topology Low 省掉了資料遷移階段）。結構上採兩階段：<strong>Phase 1 drop-in 替換（維持 pure KV 用法、先把 client 換掉）</strong>，<strong>Phase 2 漸進採用 data types（把 application 層硬湊的邏輯收回 Redis）</strong>。Phase 2 是可選的、可以慢慢來。</p>
<h2 id="phase-1drop-in-替換pure-kv不搬資料">Phase 1：drop-in 替換（pure KV、不搬資料）</h2>
<p>第一階段把 Memcached 換成 Redis，但<strong>只用 Redis 當 pure KV</strong>（GET / SET / DEL + TTL），存取行為跟 Memcached 一樣。這一步風險最低，因為不碰 data model、不搬資料。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">存取層對應（Phase 1 維持 pure KV 語意）：
</span></span><span class="line"><span class="ln">2</span><span class="cl">  Memcached set(key, val, ttl)   →  Redis SET key val EX ttl
</span></span><span class="line"><span class="ln">3</span><span class="cl">  Memcached get(key)             →  Redis GET key
</span></span><span class="line"><span class="ln">4</span><span class="cl">  Memcached delete(key)          →  Redis DEL key
</span></span><span class="line"><span class="ln">5</span><span class="cl">  Memcached incr/decr            →  Redis INCR/DECR（Redis 原生原子、比 Memcached 更穩）</span></span></code></pre></div><p>cutover 流程（cache 可重建、無資料遷移）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">1. 部署 Redis（空的）、設 maxmemory + eviction policy（見記憶體調校）
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. application 改用 Redis client（雙寫期：同時寫 Memcached + Redis，讀仍走 Memcached）
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 讀切到 Redis（cache miss 回源 + 寫回 Redis、命中率逐步 warm up）
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 觀察 Redis 命中率追上 Memcached、origin 壓力無異常
</span></span><span class="line"><span class="ln">5</span><span class="cl">5. 停止寫 Memcached、下線 Memcached</span></span></code></pre></div><p>判讀：</p>
<ul>
<li>不需要資料遷移工具——Redis 空上線、靠 cache-aside 自然 warm（見 <a href="/blog/backend/02-cache-redis/cache-aside/" data-link-title="2.2 cache aside 與失效策略" data-link-desc="整理 read-through 思路、cache miss 與 invalidation">2.2 cache aside</a>）</li>
<li>warm-up 期 origin 壓力會短暫上升（命中率從 0 爬升），低流量時段切、或預熱熱 key</li>
<li>Phase 1 完成後 application 行為跟用 Memcached 時一致，只是底層換 Redis</li>
<li>想保留開源 OSI 授權，target 直接選 <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a>（Redis 相容、BSD）</li>
</ul>
<h2 id="phase-2漸進採用-data-types可選">Phase 2：漸進採用 data types（可選）</h2>
<p>Phase 1 上線穩定後，再把 application 層硬湊的邏輯逐步收回 Redis 的原生 data types。這一階段是能力升級、不是遷移必需，可以一個場景一個場景來。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">application 硬湊 → Redis 原生：
</span></span><span class="line"><span class="ln">2</span><span class="cl">  讀 JSON → 改欄位 → 寫回整包    →  Redis Hash（HSET/HGET 單欄位、免全寫）
</span></span><span class="line"><span class="ln">3</span><span class="cl">  app 端計數 + CAS 重試           →  Redis INCR（原子、無 race）
</span></span><span class="line"><span class="ln">4</span><span class="cl">  app 端排序 leaderboard          →  Redis Sorted Set（ZADD/ZRANGE）
</span></span><span class="line"><span class="ln">5</span><span class="cl">  app 端 set 去重                 →  Redis Set（SADD/SISMEMBER）
</span></span><span class="line"><span class="ln">6</span><span class="cl">  多 key 操作要原子               →  Redis MULTI / Lua（Memcached 只有 CAS）</span></span></code></pre></div><p>判讀：</p>
<ul>
<li>Phase 2 每個改動是獨立的小重構，不必一次到位</li>
<li>收回 data types 的收益是「消除 application 層的 read-modify-write race + 減少網路往返」</li>
<li>不是所有東西都要升級——純 string cache 留在 GET/SET 就好，別為了用而用</li>
</ul>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1warm-up-期-origin-被打爆">Case 1：warm-up 期 origin 被打爆</h3>
<p><strong>徵兆</strong>：切讀到 Redis 的瞬間，origin（DB）QPS 暴增、延遲升高，因為 Redis 還是空的、大量 cache miss 同時回源。</p>
<p><strong>根因</strong>：Redis 空上線、命中率從 0 開始，warm-up 期所有讀都 miss 回源。沒有控制就是一次 origin 衝擊（類似冷啟動 stampede）。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>低流量時段切讀、讓命中率平緩爬升</li>
<li>預熱熱 key（migration 前先把已知熱 key 灌進 Redis）</li>
<li>cache miss 回源加 singleflight / jitter，避免同 key 並發回源（見 <a href="/blog/backend/02-cache-redis/cache-migration-stampede-rollback/" data-link-title="2.9 Cache Migration 與 Stampede Rollback（實作示範）" data-link-desc="以商品詳情與價格快取示範 cache migration 如何交付 evidence package、release gate 與 incident decision log。">2.9 stampede rollback</a>）</li>
<li>雙寫期先讓 Redis 被寫入 warm 一段時間，再切讀</li>
</ol>
<h3 id="case-2把-memcached-的-multi-get-行為直接搬效能不如預期">Case 2：把 Memcached 的 multi-get 行為直接搬、效能不如預期</h3>
<p><strong>徵兆</strong>：Memcached 的 batch get（一次拿多 key）搬到 Redis 後延遲沒改善甚至更差。</p>
<p><strong>根因</strong>：Memcached client 的 multi-get 跟 Redis 的 MGET / pipeline 行為不同。直接一個 key 一個 GET（N 次往返）會比 Memcached 的 batch 慢——Redis 要用 MGET 或 pipeline 才能合併往返（見 <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 踩坑，以及連線模型撞牆的邊界">Redis 連線 / pipeline</a>）。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>Memcached multi-get → Redis MGET（同 slot）或 pipeline</li>
<li>不要把「N 次獨立 GET」當成 multi-get 的等價</li>
<li>cluster 模式下 MGET 跨 slot 會失敗，用 hash tag 或 pipeline 分組</li>
<li>量測往返次數，存取層遷移要保持「一次互動的往返數」不退化</li>
</ol>
<h3 id="case-3ttl-精度與-eviction-行為差異造成命中率變化">Case 3：TTL 精度與 eviction 行為差異造成命中率變化</h3>
<p><strong>徵兆</strong>：遷到 Redis 後命中率跟 Memcached 時期不一樣（可能更高或更低），cache 行為不如預期。</p>
<p><strong>根因</strong>：Memcached 是 LRU + 秒級 lazy expiration + slab 限制；Redis 有 8 種 eviction policy + ms 級 TTL + 不同記憶體模型。沿用 Memcached 的 TTL 與容量設定不會得到一樣的淘汰行為。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>明確設 Redis 的 <code>maxmemory-policy</code>（純 cache 用 allkeys-lru / allkeys-lfu，見 <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>）</li>
<li>不要假設 Memcached 的容量設定直接套用——Redis 記憶體模型不同（無 slab calcification、但有自己的 fragmentation）</li>
<li>觀察 <code>evicted_keys</code> 與命中率，對齊預期 working set</li>
<li>Memcached 的 slab 浪費 vs Redis 的編碼，記憶體佔用會不同，重新算容量</li>
</ol>
<h3 id="case-4以為-redis-一定比-memcached-快--省">Case 4：以為 Redis 一定比 Memcached 快 / 省</h3>
<p><strong>徵兆</strong>：遷到 Redis 後純 string cache 的記憶體佔用或延遲沒有改善，甚至 Redis 單執行緒在高並發純 GET 下不如 Memcached 多執行緒。</p>
<p><strong>根因</strong>：對「純 string KV、高並發」這個 Memcached 的本場，Memcached 的多執行緒可能比 Redis 單執行緒（命令層）更適合。遷 Redis 的收益在 data types / persistence / 生態，不是純 KV 效能。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>釐清遷移動機——是要 data types / persistence（Redis 解）還是純 KV 效能（Memcached 可能更好）</li>
<li>純 KV 高並發要 Redis 的多核走 <a href="/blog/backend/02-cache-redis/vendors/dragonflydb/" data-link-title="DragonflyDB" data-link-desc="高效能 Redis / Memcached 相容替代、多核架構">DragonflyDB</a> / <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> 或 Redis I/O threads</li>
<li>純 cache 紀律本來就是 Memcached 的優勢，遷 Redis 要小心別把 cache 用成 database</li>
<li>沒有 data types / persistence 需求的純 KV，留 Memcached 可能更對</li>
</ol>
<h3 id="case-5把可重建的-cache-當成要搬的資料白做遷移工具">Case 5：把可重建的 cache 當成要搬的資料、白做遷移工具</h3>
<p><strong>徵兆</strong>：團隊花時間寫 Memcached → Redis 的資料遷移腳本、做一致性校驗，結果發現 cache 切換後這些資料本來就會被新值覆蓋。</p>
<p><strong>根因</strong>：用一般 migration 的思維（搬資料 + 校驗）處理 cache 遷移，沒意識到 cache 是可重建副本——搬過去的舊值很快被回源的新值取代，搬資料是白工且可能搬到 stale 值。</p>
<p><strong>修法</strong>：</p>
<ol>
<li>cache 遷移預設不搬資料、靠 re-warm（這是 cache 類遷移的核心簡化）</li>
<li>只有「重建成本極高的 cache」（昂貴計算結果）才考慮搬，且要評估 stale 風險</li>
<li>把精力放在存取層正確性與 warm-up 控制，不是資料搬遷</li>
<li>對照 <a href="/blog/backend/02-cache-redis/cache-copy-freshness-boundary/" data-link-title="2.7 Cache Copy Boundary 與 Freshness" data-link-desc="說明快取何時只是可重建副本，何時會影響交易、權限或配額正確性。">cache copy boundary</a>：cache 是副本、不是 source-of-truth</li>
</ol>
<h2 id="capacity--cost-對照">Capacity / cost 對照</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Memcached（source）</th>
          <th>Redis / Valkey（target）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>資料遷移</td>
          <td>—</td>
          <td>不需要（cache 可重建、re-warm）</td>
      </tr>
      <tr>
          <td>data types</td>
          <td>純 string KV</td>
          <td>6 大 + Stream / Geo</td>
      </tr>
      <tr>
          <td>原子操作</td>
          <td>INCR / DECR / CAS</td>
          <td>100+（INCR / HSET / ZADD / Lua）</td>
      </tr>
      <tr>
          <td>persistence</td>
          <td>無</td>
          <td>RDB / AOF（可選）</td>
      </tr>
      <tr>
          <td>多執行緒</td>
          <td>原生多執行緒</td>
          <td>單執行緒命令 + I/O threads</td>
      </tr>
      <tr>
          <td>eviction</td>
          <td>LRU only</td>
          <td>8 種 policy</td>
      </tr>
      <tr>
          <td>純 KV 高並發效能</td>
          <td>多執行緒、本場強</td>
          <td>單執行緒命令可能略遜（要多核走 fork）</td>
      </tr>
      <tr>
          <td>遷移風險</td>
          <td>—</td>
          <td>低（無資料遷移、存取層 + warm-up）</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：要 data types / persistence / 原子操作 → 遷 Redis（兩階段、低風險）；純 KV + 高並發 + 嚴格 cache 紀律 → 留 Memcached。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<p>Memcached → Redis 是能力升級，它跟 Redis 的調校與選型交織：</p>
<ul>
<li><strong>跟 <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></strong>：遷過去要設對 maxmemory-policy，Redis 記憶體模型跟 Memcached slab 不同。</li>
<li><strong>跟 <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 踩坑，以及連線模型撞牆的邊界">Redis 連線 / pipeline</a></strong>：Memcached multi-get → Redis MGET / pipeline，存取層遷移要保持往返數。</li>
<li><strong>跟反向 <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></strong>：反向是 Type E paradigm reduction（downgrade）；本文是能力升級（upgrade），兩者對位看 cache paradigm 的兩個方向。</li>
<li><strong>跟 <a href="/blog/backend/02-cache-redis/vendors/valkey/" data-link-title="Valkey" data-link-desc="Redis fork、Linux Foundation 託管、BSD 授權">Valkey</a></strong>：要開源 OSI 授權，target 選 Valkey（Redis 相容、BSD），遷移流程一致。</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Source vendor：<a href="/blog/backend/02-cache-redis/vendors/memcached/" data-link-title="Memcached" data-link-desc="純記憶體 key-value cache、無持久化">Memcached</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>反向 migration：<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（Type E paradigm reduction）</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></li>
</ul>
]]></content:encoded></item></channel></rss>