<?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>Ttl on Tarragon</title><link>https://tarrragon.github.io/blog/tags/ttl/</link><description>Recent content in Ttl on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Tue, 02 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/ttl/index.xml" rel="self" type="application/rss+xml"/><item><title>2.3 TTL 與 eviction</title><link>https://tarrragon.github.io/blog/backend/02-cache-redis/ttl-eviction/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/02-cache-redis/ttl-eviction/</guid><description>&lt;p>存活時間與淘汰策略（TTL and eviction）的核心責任是把快取資源分配成可預期策略。TTL 決定資料可存活多久，eviction 決定容量壓力下誰先被移除；兩者共同定義快取的新鮮度、命中率與回源風險。&lt;/p>
&lt;h2 id="ttl-是新鮮度預算">TTL 是新鮮度預算&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ttl/" data-link-title="TTL" data-link-desc="說明資料過期時間如何影響快取新鮮度、成本與一致性">TTL&lt;/a> 是資料類型的新鮮度預算，用單一時間常數理解它會漏掉關鍵差異。商品描述、推薦列表、活動文案可容忍較長 TTL；價格、庫存、配額、權限則需要更短 TTL 或事件失效。&lt;/p>
&lt;p>TTL 設計要連到業務代價。可容忍舊資料的欄位可用長 TTL 降回源壓力；不可容忍錯誤結果的欄位要搭配事件失效與版本控制，讓 TTL 只作為保底機制。&lt;/p>
&lt;h2 id="eviction-是容量分流策略">eviction 是容量分流策略&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/eviction/" data-link-title="Eviction" data-link-desc="說明快取容量不足時哪些資料會被淘汰，以及淘汰如何影響服務">eviction&lt;/a> 的責任是當記憶體不足時，優先保留最有價值資料。常見策略如 LRU、LFU、TTL-based eviction，各自偏好不同存取型態。&lt;/p>
&lt;p>策略選擇重點在流量形狀，演算法名稱是次要的：高重複讀取場景偏向保留高頻資料；大量一次性讀取場景需要避免短期噪音擠掉核心 key。快取層若同時承載多種資料，應分 key space 或分叢集管理，避免策略互相干擾。&lt;/p>
&lt;h2 id="hot--cold-data-的容量節奏">hot / cold data 的容量節奏&lt;/h2>
&lt;p>hot data 與 cold data 的差異不只在存取次數，也在回源成本與業務風險。熱資料 miss 會直接放大來源壓力，冷資料 miss 多半只影響單次延遲。容量規劃要先保護熱資料，再決定冷資料淘汰節奏。&lt;/p>
&lt;p>在促銷或重大活動期間，流量分布常快速改變。TTL 與 eviction 需要具備活動模式：預熱核心 key、分散過期時間、限制單批失效，讓來源系統不被同時回源壓垮。&lt;/p>
&lt;h2 id="分層快取的容量跟成本曲線">分層快取的容量跟成本曲線&lt;/h2>
&lt;p>當熱資料集合超過 DRAM 經濟範圍、單層快取會同時遇到成本跟命中率瓶頸、要把 cache 結構擴展到分層管理。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/cases/meta-cachelib-kangaroo-tiered-cache/" data-link-title="2.C4 Meta：CacheLib / Kangaroo 分層快取" data-link-desc="快取從 DRAM-only 轉向分層快取架構的實務案例。">2.C4 Meta CacheLib / Kangaroo&lt;/a> — Meta 把快取結構從 DRAM-only 擴展到 DRAM + flash 分層、改善容量跟成本平衡。當「全部熱資料塞 DRAM」變太貴、把次熱資料推到 flash、保留 DRAM 給最熱的子集。&lt;/p>
&lt;p>&lt;strong>分層快取的相對特性&lt;/strong>（具體 size / latency / cost 視硬體配置跟業務 workload）：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>L1 (DRAM)&lt;/strong>：容量最小、延遲最低、單位成本最高、放最熱的子集 — Meta CacheLib 用這層保留熱度最高的 working set&lt;/li>
&lt;li>&lt;strong>L2 (flash / NVMe)&lt;/strong>：容量比 L1 大、延遲比 L1 高、單位成本比 L1 低 — Meta Kangaroo 在這層處理次熱資料&lt;/li>
&lt;li>&lt;strong>L3 (持久 KV)&lt;/strong>：容量最大、延遲最高、單位成本最低、放冷資料跟 fallback&lt;/li>
&lt;/ul>
&lt;p>落層策略要看 &lt;em>資料熱度分布&lt;/em>。Zipfian 分布（80/20 法則）下、L1 放最熱 20% 就能命中大部分；如果分布更平、要把 L1 擴大或接受更低命中率。具體 L1 / L2 大小比例要實測 workload 才能定。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/02-cache-redis/cases/cloudflare-cache-reserve-tiered-storage/" data-link-title="2.C7 Cloudflare：Cache Reserve 分層儲存快取" data-link-desc="邊緣快取延伸到持久層以降低回源壓力的案例。">2.C7 Cloudflare Cache Reserve&lt;/a> — edge cache 跟 persistent reserve 的分層、長尾資料用 reserve 接住、降低 origin 回源。這是 &lt;em>同類設計思維&lt;/em> 在 CDN 場景的應用、但分層語意不同（edge cache 是地理分散的、Meta 分層是垂直記憶體 / flash 層）— 兩者都用「冷熱分離降低總成本」、實作機制差異需依場景區分。&lt;/p>
&lt;p>&lt;strong>Eviction 跟回補延遲要納入共同指標&lt;/strong>：分層 cache 的訊號不只看 L1 命中率、要看 L1 evict 到 L2 的速率、L2 回補到 L1 的延遲、L3 回源到 L2 的尾巴延遲。混合 metric 才能判斷分層策略是否健康。&lt;/p>
&lt;p>判讀重點：分層 cache 屬規模觸發的設計、要從 working set 大小判斷。Working set 在 DRAM 經濟範圍內、單層即可；working set 顯著超過 DRAM 容量、需分層讓 DRAM 集中放最熱子集、其餘走 flash 或更下層。&lt;/p></description><content:encoded><![CDATA[<p>存活時間與淘汰策略（TTL and eviction）的核心責任是把快取資源分配成可預期策略。TTL 決定資料可存活多久，eviction 決定容量壓力下誰先被移除；兩者共同定義快取的新鮮度、命中率與回源風險。</p>
<h2 id="ttl-是新鮮度預算">TTL 是新鮮度預算</h2>
<p><a href="/blog/backend/knowledge-cards/ttl/" data-link-title="TTL" data-link-desc="說明資料過期時間如何影響快取新鮮度、成本與一致性">TTL</a> 是資料類型的新鮮度預算，用單一時間常數理解它會漏掉關鍵差異。商品描述、推薦列表、活動文案可容忍較長 TTL；價格、庫存、配額、權限則需要更短 TTL 或事件失效。</p>
<p>TTL 設計要連到業務代價。可容忍舊資料的欄位可用長 TTL 降回源壓力；不可容忍錯誤結果的欄位要搭配事件失效與版本控制，讓 TTL 只作為保底機制。</p>
<h2 id="eviction-是容量分流策略">eviction 是容量分流策略</h2>
<p><a href="/blog/backend/knowledge-cards/eviction/" data-link-title="Eviction" data-link-desc="說明快取容量不足時哪些資料會被淘汰，以及淘汰如何影響服務">eviction</a> 的責任是當記憶體不足時，優先保留最有價值資料。常見策略如 LRU、LFU、TTL-based eviction，各自偏好不同存取型態。</p>
<p>策略選擇重點在流量形狀，演算法名稱是次要的：高重複讀取場景偏向保留高頻資料；大量一次性讀取場景需要避免短期噪音擠掉核心 key。快取層若同時承載多種資料，應分 key space 或分叢集管理，避免策略互相干擾。</p>
<h2 id="hot--cold-data-的容量節奏">hot / cold data 的容量節奏</h2>
<p>hot data 與 cold data 的差異不只在存取次數，也在回源成本與業務風險。熱資料 miss 會直接放大來源壓力，冷資料 miss 多半只影響單次延遲。容量規劃要先保護熱資料，再決定冷資料淘汰節奏。</p>
<p>在促銷或重大活動期間，流量分布常快速改變。TTL 與 eviction 需要具備活動模式：預熱核心 key、分散過期時間、限制單批失效，讓來源系統不被同時回源壓垮。</p>
<h2 id="分層快取的容量跟成本曲線">分層快取的容量跟成本曲線</h2>
<p>當熱資料集合超過 DRAM 經濟範圍、單層快取會同時遇到成本跟命中率瓶頸、要把 cache 結構擴展到分層管理。</p>
<p>對應 <a href="/blog/backend/02-cache-redis/cases/meta-cachelib-kangaroo-tiered-cache/" data-link-title="2.C4 Meta：CacheLib / Kangaroo 分層快取" data-link-desc="快取從 DRAM-only 轉向分層快取架構的實務案例。">2.C4 Meta CacheLib / Kangaroo</a> — Meta 把快取結構從 DRAM-only 擴展到 DRAM + flash 分層、改善容量跟成本平衡。當「全部熱資料塞 DRAM」變太貴、把次熱資料推到 flash、保留 DRAM 給最熱的子集。</p>
<p><strong>分層快取的相對特性</strong>（具體 size / latency / cost 視硬體配置跟業務 workload）：</p>
<ul>
<li><strong>L1 (DRAM)</strong>：容量最小、延遲最低、單位成本最高、放最熱的子集 — Meta CacheLib 用這層保留熱度最高的 working set</li>
<li><strong>L2 (flash / NVMe)</strong>：容量比 L1 大、延遲比 L1 高、單位成本比 L1 低 — Meta Kangaroo 在這層處理次熱資料</li>
<li><strong>L3 (持久 KV)</strong>：容量最大、延遲最高、單位成本最低、放冷資料跟 fallback</li>
</ul>
<p>落層策略要看 <em>資料熱度分布</em>。Zipfian 分布（80/20 法則）下、L1 放最熱 20% 就能命中大部分；如果分布更平、要把 L1 擴大或接受更低命中率。具體 L1 / L2 大小比例要實測 workload 才能定。</p>
<p>對應 <a href="/blog/backend/02-cache-redis/cases/cloudflare-cache-reserve-tiered-storage/" data-link-title="2.C7 Cloudflare：Cache Reserve 分層儲存快取" data-link-desc="邊緣快取延伸到持久層以降低回源壓力的案例。">2.C7 Cloudflare Cache Reserve</a> — edge cache 跟 persistent reserve 的分層、長尾資料用 reserve 接住、降低 origin 回源。這是 <em>同類設計思維</em> 在 CDN 場景的應用、但分層語意不同（edge cache 是地理分散的、Meta 分層是垂直記憶體 / flash 層）— 兩者都用「冷熱分離降低總成本」、實作機制差異需依場景區分。</p>
<p><strong>Eviction 跟回補延遲要納入共同指標</strong>：分層 cache 的訊號不只看 L1 命中率、要看 L1 evict 到 L2 的速率、L2 回補到 L1 的延遲、L3 回源到 L2 的尾巴延遲。混合 metric 才能判斷分層策略是否健康。</p>
<p>判讀重點：分層 cache 屬規模觸發的設計、要從 working set 大小判斷。Working set 在 DRAM 經濟範圍內、單層即可；working set 顯著超過 DRAM 容量、需分層讓 DRAM 集中放最熱子集、其餘走 flash 或更下層。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>eviction rate 持續上升</td>
          <td>容量不足或 key/value 體積失控</td>
          <td>調整策略、拆分 key space、補容量</td>
      </tr>
      <tr>
          <td>hit rate 下降且 origin QPS 同步上升</td>
          <td>TTL 設定過短或過期同步爆發</td>
          <td>拉長部分 TTL、加入 jitter、分批更新</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/knowledge-cards/stale-read/" data-link-title="Stale Read" data-link-desc="讀取到落後於最新寫入版本的舊資料">stale read</a> 事件上升</td>
          <td>TTL 過長或失效機制不足</td>
          <td>縮短關鍵欄位 TTL、補事件失效</td>
      </tr>
      <tr>
          <td>熱門 key 在尖峰時段頻繁 miss</td>
          <td>熱資料未被優先保留</td>
          <td>預熱 hot set、調整 eviction 權重</td>
      </tr>
      <tr>
          <td>記憶體穩定但業務錯誤增加</td>
          <td>值語意失真，非容量問題</td>
          <td>檢查序列化版本、補新鮮度監控與驗證</td>
      </tr>
  </tbody>
</table>
<h2 id="常見誤區">常見誤區</h2>
<p>把 TTL 統一設定成同一數值，會掩蓋資料語意差異。快取策略應反映資料的重要性與可容忍延遲，而不是單一預設。</p>
<p>把 eviction 視為平台預設值即可，也常導致壓力失真。策略與流量形狀不對齊時，命中率看似可接受，來源系統仍可能在尖峰被回源壓垮。</p>
<h2 id="案例回寫">案例回寫</h2>
<p>TTL/eviction 的容量節奏可用 <a href="/blog/backend/02-cache-redis/cases/failure-cache-stampede-rollout-regression/" data-link-title="2.C9 反例：快取切換引發 Stampede 回歸" data-link-desc="快取策略切換若缺乏保護，會導致回源壓力與錯誤率連鎖上升。">2.C9 反例</a> 回寫。先看事件中的過期同步與回源尖峰，再回到本章檢查 TTL 分布、淘汰策略與熱資料保護是否同時成立。
這個案例主要支撐的是「容量淘汰與過期波形」判讀，不直接支撐資料庫交易切分或部署切流策略；若事件核心在交易提交或 rollout 批次，應轉到 1.3 或 5.2。</p>
<p>當 eviction 上升但命中率未明顯下降時，先補 value size 與 key 分布監控，再把量測定義回寫到 <a href="/blog/backend/04-observability/telemetry-data-quality/" data-link-title="4.17 Telemetry Data Quality" data-link-desc="把 missing signal、schema drift、sampling bias 與 timestamp skew 變成資料品質問題">4.17 Telemetry Data Quality</a>。</p>
<h2 id="跨模組路由">跨模組路由</h2>
<p>TTL 與 eviction 設計會直接影響觀測、驗證與事故處理。</p>
<ol>
<li>與 2.2 的交接：讀寫失效流程落在 <a href="/blog/backend/02-cache-redis/cache-aside/" data-link-title="2.2 cache aside 與失效策略" data-link-desc="整理 read-through 思路、cache miss 與 invalidation">cache aside</a>。</li>
<li>與 4.17 的交接：新鮮度與容量訊號進入 <a href="/blog/backend/04-observability/telemetry-data-quality/" data-link-title="4.17 Telemetry Data Quality" data-link-desc="把 missing signal、schema drift、sampling bias 與 timestamp skew 變成資料品質問題">Telemetry Data Quality</a>。</li>
<li>與 6.20 的交接：尖峰演練與停損邊界進入 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">Experiment Safety Boundary</a>。</li>
<li>與 8.22 的交接：容量失配與快取事故教訓回寫 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">Incident Evidence Write-back</a>。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<p>要把 TTL/eviction 放進失效流程，接著讀 <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>。要看容量與策略失配案例，接著讀 <a href="/blog/backend/02-cache-redis/cases/failure-cache-stampede-rollout-regression/" data-link-title="2.C9 反例：快取切換引發 Stampede 回歸" data-link-desc="快取策略切換若缺乏保護，會導致回源壓力與錯誤率連鎖上升。">2.C9 反例</a>。</p>
]]></content:encoded></item><item><title>DynamoDB TTL 資料生命週期：自動過期、48 小時刪除延遲、過期仍可讀與 storage 成本</title><link>https://tarrragon.github.io/blog/backend/01-database/vendors/dynamodb/ttl-data-lifecycle/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/01-database/vendors/dynamodb/ttl-data-lifecycle/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/vendors/dynamodb/" data-link-title="DynamoDB" data-link-desc="AWS managed key-value、partition-based scaling、9000 萬 RPS sustained 實戰證據">DynamoDB&lt;/a> overview 的 implementation-layer deep article。寫作參照 &lt;a href="https://tarrragon.github.io/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 deep article methodology&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;p>訊息系統的 storage bill 每月穩定上漲、查 table 發現裡面堆了三年份的過期通知、沒人清。team 設了 TTL「自動清理」、結果兩個新問題冒出來：第一、設了 TTL 之後 storage 還是沒馬上降、過了好幾小時才開始掉；第二、有個報表 query 把「已過期但還沒被刪」的 item 也撈進來、算錯數字。兩個痛點揭露 DynamoDB TTL 的真實語意 — 它是 &lt;em>最終會刪除&lt;/em> 的背景機制、不是即時刪除、也不是查詢層的過濾器。本文展開 TTL 的 epoch 語意、刪除延遲特性、過期可讀陷阱與 storage 成本判讀。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>生命週期前提：先確認 workload 適配 DynamoDB&lt;/strong>：資料生命週期管理是 &lt;em>已選 DynamoDB&lt;/em> 之後才浮現的議題 — TTL 解的是「資料存進來之後怎麼自動退場」、而非「資料該不該存進 DynamoDB」。後者由 4 軸前置判讀決定：PK 天然均勻 / control plane vs data plane / consistency 可接受 eventual / access pattern 穩定、判讀軸詳見 &lt;a href="../single-table-design-pattern/#dynamodb-%e9%81%a9%e7%94%a8%e5%ba%a6%e5%89%8d%e7%bd%ae%e5%88%a4%e8%ae%804-%e8%bb%b8">single-table-design-pattern 開頭 4 軸前置判讀&lt;/a>。本文承接該前提、聚焦用 TTL 管理資料生命週期與 storage 成本。&lt;/p>&lt;/blockquote>
&lt;h2 id="核心機制ttl-attribute-與背景刪除">核心機制：TTL attribute 與背景刪除&lt;/h2>
&lt;p>DynamoDB TTL 讓 item 在指定時間後自動被刪除、不消耗寫容量。機制很簡單但語意有三個容易踩的邊界。&lt;/p>
&lt;p>&lt;strong>設定方式&lt;/strong>：在 item 上放一個數值 attribute、值是 &lt;em>Unix epoch 秒數&lt;/em>（不是毫秒、不是 ISO 字串）、並在 table 啟用 TTL 指向該 attribute：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="n">table&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">put_item&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Item&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;PK&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;MSG#&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">msg_id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;SK&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;META&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;body&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;...&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;expireAt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">int&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">30&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">86400&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 30 天後過期、epoch 秒&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="p">})&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>三個關鍵語意&lt;/strong>：&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>刪除非即時&lt;/td>
 &lt;td>過期後由 AWS 背景程序刪除、通常 48 小時內、不保證準時&lt;/td>
 &lt;td>不能用 TTL 做即時失效邏輯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>過期仍可讀&lt;/td>
 &lt;td>過期但尚未被刪的 item 仍出現在 GetItem / Query / Scan 結果&lt;/td>
 &lt;td>read 路徑要 application 端 filter&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>刪除免 WCU&lt;/td>
 &lt;td>TTL 刪除不消耗 write capacity&lt;/td>
 &lt;td>大量過期清理不增寫成本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>第二列是報表算錯的根因：&lt;strong>TTL 不是查詢過濾器&lt;/strong>。過期到實際刪除之間有一段窗口、這期間 item 還在、還會被讀到。需要「過期立刻不可見」的、application 必須在讀取後自己比對 &lt;code>expireAt&lt;/code> 過濾。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Scope warning&lt;/strong>：「TTL 通常 48 小時內刪除」屬 AWS vendor 規格描述、AWS 不保證準時、實際延遲視 table 大小與背景負載而定、實作時 cross-verify 官方 doc。&lt;code>9.C26 PayPay&lt;/code> case 揭露「TTL 機制可自動清理過期訊息」的 &lt;em>用途&lt;/em>、未揭露刪除延遲的具體數字。&lt;/p>&lt;/blockquote>
&lt;p>對應 knowledge card：&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ttl/" data-link-title="TTL" data-link-desc="說明資料過期時間如何影響快取新鮮度、成本與一致性">ttl&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/soft-ttl/" data-link-title="Soft TTL" data-link-desc="說明資料進入刷新期後仍可短暫使用以降低 stampede">soft-ttl&lt;/a>。&lt;/p>
&lt;h2 id="刪除延遲與過期可讀兩個必須處理的窗口">刪除延遲與過期可讀：兩個必須處理的窗口&lt;/h2>
&lt;p>TTL 的「最終刪除」特性製造兩個 application 必須意識的窗口。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/01-database/vendors/dynamodb/" data-link-title="DynamoDB" data-link-desc="AWS managed key-value、partition-based scaling、9000 萬 RPS sustained 實戰證據">DynamoDB</a> overview 的 implementation-layer deep article。寫作參照 <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 deep article methodology</a>。</p></blockquote>
<p>訊息系統的 storage bill 每月穩定上漲、查 table 發現裡面堆了三年份的過期通知、沒人清。team 設了 TTL「自動清理」、結果兩個新問題冒出來：第一、設了 TTL 之後 storage 還是沒馬上降、過了好幾小時才開始掉；第二、有個報表 query 把「已過期但還沒被刪」的 item 也撈進來、算錯數字。兩個痛點揭露 DynamoDB TTL 的真實語意 — 它是 <em>最終會刪除</em> 的背景機制、不是即時刪除、也不是查詢層的過濾器。本文展開 TTL 的 epoch 語意、刪除延遲特性、過期可讀陷阱與 storage 成本判讀。</p>
<blockquote>
<p><strong>生命週期前提：先確認 workload 適配 DynamoDB</strong>：資料生命週期管理是 <em>已選 DynamoDB</em> 之後才浮現的議題 — TTL 解的是「資料存進來之後怎麼自動退場」、而非「資料該不該存進 DynamoDB」。後者由 4 軸前置判讀決定：PK 天然均勻 / control plane vs data plane / consistency 可接受 eventual / access pattern 穩定、判讀軸詳見 <a href="../single-table-design-pattern/#dynamodb-%e9%81%a9%e7%94%a8%e5%ba%a6%e5%89%8d%e7%bd%ae%e5%88%a4%e8%ae%804-%e8%bb%b8">single-table-design-pattern 開頭 4 軸前置判讀</a>。本文承接該前提、聚焦用 TTL 管理資料生命週期與 storage 成本。</p></blockquote>
<h2 id="核心機制ttl-attribute-與背景刪除">核心機制：TTL attribute 與背景刪除</h2>
<p>DynamoDB TTL 讓 item 在指定時間後自動被刪除、不消耗寫容量。機制很簡單但語意有三個容易踩的邊界。</p>
<p><strong>設定方式</strong>：在 item 上放一個數值 attribute、值是 <em>Unix epoch 秒數</em>（不是毫秒、不是 ISO 字串）、並在 table 啟用 TTL 指向該 attribute：</p>





<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="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">table</span><span class="o">.</span><span class="n">put_item</span><span class="p">(</span><span class="n">Item</span><span class="o">=</span><span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;PK&#34;</span><span class="p">:</span> <span class="sa">f</span><span class="s2">&#34;MSG#</span><span class="si">{</span><span class="n">msg_id</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s2">&#34;SK&#34;</span><span class="p">:</span> <span class="s2">&#34;META&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="s2">&#34;body&#34;</span><span class="p">:</span> <span class="s2">&#34;...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="s2">&#34;expireAt&#34;</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span> <span class="o">+</span> <span class="mi">30</span> <span class="o">*</span> <span class="mi">86400</span><span class="p">,</span>  <span class="c1"># 30 天後過期、epoch 秒</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p><strong>三個關鍵語意</strong>：</p>
<table>
  <thead>
      <tr>
          <th>語意</th>
          <th>內容</th>
          <th>後果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>刪除非即時</td>
          <td>過期後由 AWS 背景程序刪除、通常 48 小時內、不保證準時</td>
          <td>不能用 TTL 做即時失效邏輯</td>
      </tr>
      <tr>
          <td>過期仍可讀</td>
          <td>過期但尚未被刪的 item 仍出現在 GetItem / Query / Scan 結果</td>
          <td>read 路徑要 application 端 filter</td>
      </tr>
      <tr>
          <td>刪除免 WCU</td>
          <td>TTL 刪除不消耗 write capacity</td>
          <td>大量過期清理不增寫成本</td>
      </tr>
  </tbody>
</table>
<p>第二列是報表算錯的根因：<strong>TTL 不是查詢過濾器</strong>。過期到實際刪除之間有一段窗口、這期間 item 還在、還會被讀到。需要「過期立刻不可見」的、application 必須在讀取後自己比對 <code>expireAt</code> 過濾。</p>
<blockquote>
<p><strong>Scope warning</strong>：「TTL 通常 48 小時內刪除」屬 AWS vendor 規格描述、AWS 不保證準時、實際延遲視 table 大小與背景負載而定、實作時 cross-verify 官方 doc。<code>9.C26 PayPay</code> case 揭露「TTL 機制可自動清理過期訊息」的 <em>用途</em>、未揭露刪除延遲的具體數字。</p></blockquote>
<p>對應 knowledge card：<a href="/blog/backend/knowledge-cards/ttl/" data-link-title="TTL" data-link-desc="說明資料過期時間如何影響快取新鮮度、成本與一致性">ttl</a>、<a href="/blog/backend/knowledge-cards/soft-ttl/" data-link-title="Soft TTL" data-link-desc="說明資料進入刷新期後仍可短暫使用以降低 stampede">soft-ttl</a>。</p>
<h2 id="刪除延遲與過期可讀兩個必須處理的窗口">刪除延遲與過期可讀：兩個必須處理的窗口</h2>
<p>TTL 的「最終刪除」特性製造兩個 application 必須意識的窗口。</p>
<p><strong>窗口一：過期 → 實際刪除（可讀窗口）</strong>：</p>
<p>item 的 <code>expireAt</code> 已過、但背景程序還沒刪。這段時間 item：</p>
<ul>
<li>仍會被 <code>Query</code> / <code>Scan</code> / <code>GetItem</code> 撈到</li>
<li>仍佔 storage、仍計 storage 費</li>
<li>仍會被 secondary index 索引到</li>
</ul>
<p>application 若依賴「過期就消失」、會在這個窗口讀到 stale 資料。正確做法是 read 後 filter：</p>





<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="kn">import</span> <span class="nn">time</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">now</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">())</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">items</span> <span class="o">=</span> <span class="p">[</span><span class="n">it</span> <span class="k">for</span> <span class="n">it</span> <span class="ow">in</span> <span class="n">response</span><span class="p">[</span><span class="s2">&#34;Items&#34;</span><span class="p">]</span> <span class="k">if</span> <span class="n">it</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;expireAt&#34;</span><span class="p">,</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">62</span><span class="p">)</span> <span class="o">&gt;</span> <span class="n">now</span><span class="p">]</span></span></span></code></pre></div><p>或在 query 加 <code>FilterExpression</code> 排除過期 item（注意 filter 在讀取後套用、仍消耗讀容量）。</p>
<p><strong>窗口二：TTL 刪除 → stream record</strong>：</p>
<p>TTL 刪除會在 stream 產生一筆 <code>REMOVE</code> record、且 <code>userIdentity</code> 標記為 DynamoDB 服務本身（principal <code>dynamodb.amazonaws.com</code>）。這讓「過期歸檔」成為可能 — 下游 Lambda 收到 TTL 刪除事件、把 item 寫進冷儲存（S3）再讓它從 hot table 消失：</p>





<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="k">def</span> <span class="nf">handler</span><span class="p">(</span><span class="n">event</span><span class="p">,</span> <span class="n">context</span><span class="p">):</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="k">for</span> <span class="n">record</span> <span class="ow">in</span> <span class="n">event</span><span class="p">[</span><span class="s2">&#34;Records&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="k">if</span> <span class="n">record</span><span class="p">[</span><span class="s2">&#34;eventName&#34;</span><span class="p">]</span> <span class="o">==</span> <span class="s2">&#34;REMOVE&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">            <span class="n">principal</span> <span class="o">=</span> <span class="n">record</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;userIdentity&#34;</span><span class="p">,</span> <span class="p">{})</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;principalId&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">            <span class="k">if</span> <span class="n">principal</span> <span class="o">==</span> <span class="s2">&#34;dynamodb.amazonaws.com&#34;</span><span class="p">:</span>  <span class="c1"># TTL 刪除、非 application 刪除</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">                <span class="n">archive_to_s3</span><span class="p">(</span><span class="n">record</span><span class="p">[</span><span class="s2">&#34;dynamodb&#34;</span><span class="p">][</span><span class="s2">&#34;OldImage&#34;</span><span class="p">])</span></span></span></code></pre></div><p>區分「TTL 自動刪除」vs「application 主動刪除」靠 <code>userIdentity</code> — 兩者都是 <code>REMOVE</code> record、但只有 TTL 刪除帶服務 principal。對應 <a href="/blog/backend/01-database/vendors/dynamodb/streams-lambda-event-driven/" data-link-title="DynamoDB Streams 與 Lambda 事件驅動：CDC、shard 順序保證、消費模式與失敗處理" data-link-desc="DynamoDB Streams 不是免費的可靠事件流；本文展開 stream record 的四種 view type、shard 對應 partition 的順序保證邊界、Lambda event source mapping vs Kinesis 消費模式、at-least-once 下游冪等需求，以及 batch 失敗時的 bisect / DLQ 處理">streams-lambda-event-driven</a>。</p>
<blockquote>
<p><strong>Scope warning</strong>：stream record 的 <code>userIdentity</code> 標記屬 vendor 規格、欄位細節 cross-verify 官方 doc；本段機制描述非 production case 揭露。</p></blockquote>
<h2 id="操作流程">操作流程</h2>
<p>從生命週期需求到上線的 6 步流程。</p>
<h4 id="step-1判斷資料是否適合-ttl-管理">Step 1：判斷資料是否適合 TTL 管理</h4>
<p>適合 TTL 的資料有「自然過期時間」：session、訊息通知、暫存 token、event log、合規保留期到期的資料。不適合的：需要精確即時刪除的、需要刪除前審批的、永久保存的。</p>
<h4 id="step-2設計-expireat-計算">Step 2：設計 expireAt 計算</h4>
<p>寫入時算好 epoch 秒數的 <code>expireAt</code>；不同資料類型可不同保留期（通知 30 天、session 1 天、audit 依合規要求）。</p>
<h4 id="step-3啟用-table-ttl">Step 3：啟用 table TTL</h4>





<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">aws dynamodb update-time-to-live <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --table-name messages <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --time-to-live-specification <span class="s2">&#34;Enabled=true, AttributeName=expireAt&#34;</span></span></span></code></pre></div><h4 id="step-4read-路徑加過期過濾">Step 4：read 路徑加過期過濾</h4>
<p>所有面向用戶的讀取、在 application 端比對 <code>expireAt</code>（或加 FilterExpression）；不要假設過期 item 已消失。</p>
<h4 id="step-5可選接-ttl-刪除歸檔">Step 5：（可選）接 TTL 刪除歸檔</h4>
<p>需要保留過期資料的、接 stream Lambda、用 <code>userIdentity</code> 辨識 TTL 刪除、歸檔到 S3。</p>
<h4 id="step-6驗證點">Step 6：驗證點</h4>





<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"># 寫一筆短 TTL item、等過期後確認：</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 1. 過期但未刪窗口內仍可讀到（驗證需要 filter）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 2. 數小時後背景刪除生效、storage 下降</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 3. 若接歸檔、確認 S3 收到對應 OldImage</span></span></span></code></pre></div><p><strong>Rollback boundary</strong>：關閉 TTL 即停止自動刪除、已刪除的 item 不可恢復（除非有歸檔）；啟用 TTL 前先確認 <code>expireAt</code> 計算正確、避免誤設過短把活躍資料刪掉。</p>
<h2 id="失敗模式">失敗模式</h2>
<p>production 常見的 5 個踩雷：</p>
<h4 id="case-1expireat-用毫秒或-iso-字串">Case 1：expireAt 用毫秒或 ISO 字串</h4>
<p>TTL 只認 Unix epoch 秒；填毫秒（多三位數）會讓過期時間落在遙遠未來、item 永不過期；填字串 TTL 直接不生效。修法：統一用 <code>int(time.time()) + seconds</code>、寫測試驗證 attribute 是秒級數值。</p>
<h4 id="case-2以為-ttl-是即時刪除做即時失效邏輯">Case 2：以為 TTL 是即時刪除、做即時失效邏輯</h4>
<p>用 TTL 當「到點立刻不可用」的開關（如優惠券到期）、實際過期後幾小時還能用。修法：即時失效靠 application 邏輯比對時間、TTL 只負責 <em>清理 storage</em>、兩者分開。</p>
<h4 id="case-3報表--對帳撈到過期未刪-item">Case 3：報表 / 對帳撈到過期未刪 item</h4>
<p>聚合 query 沒過濾過期 item、把可讀窗口內的殘留資料算進去。修法：所有讀取路徑一致地過濾 <code>expireAt</code>；對帳查詢明確排除過期。</p>
<h4 id="case-4誤設過短保留期刪掉活躍資料">Case 4：誤設過短保留期刪掉活躍資料</h4>
<p>這個 case 跟前三個的失敗代價層級不同。前面的踩雷多半可回復 — storage 緩漲可回填、過期未刪可在讀取路徑加 filter、index 殘留會隨背景刪除自然消退。誤設過短保留期則是 <em>不可逆</em> 的：<code>expireAt</code> 計算 bug（少乘 86400、用錯時區基準）把保留期算成幾小時、背景程序把仍在使用的活躍資料當成過期 item 刪除、而 TTL 刪除不寫 undo log、刪掉就沒有從 DynamoDB 端救回的途徑、只能靠外部備份（PITR / 另存的 stream archive）回灌、且回灌期間資料缺口已經對線上服務造成影響。</p>
<p>代價的關鍵在於計算錯誤的爆炸半徑：一個錯誤常數會同時套用到所有新寫入 item、刪除是持續發生的背景行為、發現時往往已刪掉大批資料。修法的重心因此放在 <em>上線前驗證</em> 而非事後補救：上線前在 staging 用短週期資料驗證 <code>expireAt</code> 算出的絕對時間點符合預期、TTL 啟用初期把 <code>TimeToLiveDeletedItemCount</code> 跟預估刪除量對照、刪除量明顯偏高就立即停用 TTL 並排查計算、不等 storage 趨勢確認。對保留期敏感的 table 先開 PITR 當不可逆操作的最後防線。</p>
<h4 id="case-5過期-item-仍被-gsi-索引推高-index-成本">Case 5：過期 item 仍被 GSI 索引、推高 index 成本</h4>
<p>過期未刪 item 仍佔 GSI storage；大量過期堆積時 GSI 成本沒因「邏輯過期」下降。修法：理解 GSI 跟著 base item 生命週期、storage 降要等實際刪除；對成本敏感的 sparse index 設計可讓過期 item 不進 GSI（對應 <a href="/blog/backend/01-database/vendors/dynamodb/gsi-lsi-design/" data-link-title="DynamoDB GSI 與 LSI 設計：access pattern 補位、projection、consistency 跟 DAX 補位" data-link-desc="GSI / LSI 是 single-table 沒覆蓋的 access pattern 補位、不是萬靈丹；本文涵蓋 projection 三型選擇、sparse index、GSI 自己會 hot partition、DAX 讀峰值補位的觸發條件（含 Capcom 是 derive vs Lemino 是 case fact 的分層）">gsi-lsi-design sparse index</a>）。</p>
<p><strong>Anti-recommendation</strong>：資料量小、storage 成本可忽略、或刪除需要審批/合規記錄 → 不必用 TTL；手動或排程刪除更可控。TTL 的價值在「大量有自然過期時間的資料、要低成本自動清理」（如 PayPay 式每日上億訊息）。</p>
<h2 id="容量與觀測">容量與觀測</h2>
<p>CloudWatch metric：</p>
<ul>
<li><code>TimeToLiveDeletedItemCount</code>：TTL 背景刪除的 item 數、確認 TTL 真的在運作</li>
<li>table <code>ItemCount</code> / storage size：長期趨勢、確認過期清理讓 storage 趨於穩態</li>
<li>過期未刪比例：自行用 <code>expireAt &lt; now</code> 的 item 數估算可讀窗口殘留量</li>
</ul>
<p><strong>判讀</strong>：</p>
<ul>
<li><code>TimeToLiveDeletedItemCount</code> 為零但有設過期資料 → TTL 沒生效（attribute 名稱錯 / 值格式錯）</li>
<li>storage 持續上漲且 TTL 刪除量遠小於寫入量 → 保留期設太長、或寫入遠超過期速度、要重估保留策略</li>
<li>大量過期未刪堆積 → 背景刪除跟不上寫入、storage 成本被殘留拉高</li>
</ul>
<blockquote>
<p><strong>Scope warning</strong>：<code>9.C26 PayPay</code> 的「3 億/天 × 30 天 = 90 億筆」是 PayPay case 文章（9.C26）的策略段推算、非 PayPay 官方揭露的精確 item 數；引用時當量級壓力 anchor、不當精確數字。</p></blockquote>
<p>接回 <a href="/blog/backend/09-performance-capacity/capacity-planning/" data-link-title="9.6 容量規劃模型" data-link-desc="peak forecast、headroom budget、growth curve、autoscaling sizing">9.6 容量規劃模型</a>、<a href="/blog/backend/01-database/kv-document-capacity-planning/" data-link-title="1.10 KV / Document DB 容量規劃" data-link-desc="DynamoDB / Cosmos DB / Bigtable / MongoDB 等 KV / Document DB 的容量設計、partition key 取捨、capacity mode 選擇">1.10 KV / Document DB 容量規劃</a>。</p>
<h2 id="邊界與整合">邊界與整合</h2>
<h3 id="ttl-vs-cache-ttl-vs-合規保留">TTL vs cache TTL vs 合規保留</h3>
<p>「TTL」這個詞在不同層意義不同、不要混用：</p>
<ul>
<li><strong>DynamoDB TTL</strong>：主資料的生命週期管理、最終刪除、本篇主寫</li>
<li><strong>cache TTL</strong>（如 DAX item / query cache、Redis TTL）：快取副本的新鮮度邊界、過期是「重新回源」不是「刪除主資料」、主寫於 <a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">02 快取模組</a> 與 <a href="/blog/backend/01-database/vendors/dynamodb/dax-caching-strategy/" data-link-title="DynamoDB DAX 快取策略：cluster 架構、item/query cache、write-through 與 invalidation 邊界" data-link-desc="DAX 不是「加上去就變快」的開關；本文展開 DAX cluster 架構、item cache vs query cache 兩種快取、write-through 一致性語意、query cache 只靠 TTL 失效的陷阱，以及 strongly consistent read 繞過 cache 的邊界，含 Lemino 讀峰值補位 case fact 與 gsi-lsi-design 的 SSoT 切分">dax-caching-strategy</a></li>
<li><strong>合規保留期</strong>：法規要求的最短/最長保存、可用 TTL 實作到期清理、但刪除前的稽核記錄要另外保留（對應 <a href="/blog/backend/07-security-data-protection/audit-trail-and-accountability-boundary/" data-link-title="7.7 稽核追蹤與責任邊界" data-link-desc="以問題驅動方式整理高風險操作追蹤、可回查與責任切分">7.7 audit trail</a>）</li>
</ul>
<h3 id="sibling-與-cross-link">Sibling 與 cross-link</h3>
<ul>
<li><a href="/blog/backend/01-database/vendors/dynamodb/streams-lambda-event-driven/" data-link-title="DynamoDB Streams 與 Lambda 事件驅動：CDC、shard 順序保證、消費模式與失敗處理" data-link-desc="DynamoDB Streams 不是免費的可靠事件流；本文展開 stream record 的四種 view type、shard 對應 partition 的順序保證邊界、Lambda event source mapping vs Kinesis 消費模式、at-least-once 下游冪等需求，以及 batch 失敗時的 bisect / DLQ 處理">streams-lambda-event-driven</a> — TTL 刪除觸發 stream REMOVE record、用 userIdentity 辨識、可做過期歸檔</li>
<li><a href="/blog/backend/01-database/vendors/dynamodb/single-table-design-pattern/" data-link-title="DynamoDB Single-Table Design：從適用度前置判讀到 access pattern 反推 PK/SK" data-link-desc="DynamoDB single-table 設計不是「資料表越少越好」，而是 access pattern 反推 PK/SK 跟 GSI；本文先做 DynamoDB 適用度 4 軸前置判讀（PK 天然均勻 / control plane vs data plane / consistency / access pattern 穩定），再展開設計流程、failure modes 與 durable queue 正向用例">single-table-design-pattern</a> — single-table 下不同 entity 用不同 expireAt 保留期</li>
<li><a href="/blog/backend/01-database/vendors/dynamodb/gsi-lsi-design/" data-link-title="DynamoDB GSI 與 LSI 設計：access pattern 補位、projection、consistency 跟 DAX 補位" data-link-desc="GSI / LSI 是 single-table 沒覆蓋的 access pattern 補位、不是萬靈丹；本文涵蓋 projection 三型選擇、sparse index、GSI 自己會 hot partition、DAX 讀峰值補位的觸發條件（含 Capcom 是 derive vs Lemino 是 case fact 的分層）">gsi-lsi-design</a> — 過期未刪 item 仍佔 GSI、sparse index 可讓過期不進 GSI</li>
<li><a href="/blog/backend/01-database/vendors/dynamodb/on-demand-vs-provisioned/" data-link-title="DynamoDB On-Demand vs Provisioned：6 軸決策、auto-scaling 邊界與 cost crossover" data-link-desc="capacity mode 選擇不是單軸 peak/avg ratio；本文展開 6 軸決策（peak/avg / 讀寫比 trend / surge 暫時 vs 永久 baseline / predictable-peak vs flash-sale / DBA 工時釋放 / vendor vs 自管 cost crossover），含 Zomato 50% 成本下降、Zoom 30x permanent surge、Amazon Ads sustained workload 等 case 分軸 anchor">on-demand-vs-provisioned</a> — TTL 刪除免 WCU、不影響寫容量規劃、但 storage 成本要靠 TTL 控制</li>
<li>替代路由：快取副本新鮮度 → <a href="/blog/backend/02-cache-redis/" data-link-title="模組二：快取與 Redis" data-link-desc="整理快取策略、Redis 資料型別與分散式狀態輔助能力">02 快取模組</a>；合規稽核 → <a href="/blog/backend/07-security-data-protection/audit-trail-and-accountability-boundary/" data-link-title="7.7 稽核追蹤與責任邊界" data-link-desc="以問題驅動方式整理高風險操作追蹤、可回查與責任切分">7.7 audit trail</a></li>
<li>跟 <a href="/blog/backend/09-performance-capacity/cases/paypay-mobile-payment-messaging/" data-link-title="9.C26 PayPay：行動支付每日 3 億訊息的 DynamoDB 後端" data-link-desc="日本最大行動支付 PayPay 每日 3 億訊息、用 DynamoDB 處理通知與訊息功能、支撐次秒級反應">PayPay 9.C26</a> 互引：每日上億訊息用 TTL 自動清理避免 storage 爆炸的 case anchor</li>
</ul>
]]></content:encoded></item></channel></rss>