B システム設計/インフラ — Memorystore for Redis キャッシュ層(STANDARD_HA 自動フェイルオーバー × PRIVATE_SERVICE_ACCESS × AUTH/TLS × maxmemory-policy=allkeys-lru × コネクションプール × キャッシュスタンピード対策(分散ロック) × TTLジッター + Circuit Breaker フォールバック)(MOps フラッシュセール在庫参照API Bad→Good)

2026-07-21 (Day 107) 火曜 B: システム設計/インフラ ★★★★☆ Memorystore for Redis 7.2 / Cloud Run v2 STANDARD_HA / キャッシュスタンピード対策 / Circuit Breaker

概要

🛡️

STANDARD_HA + PRIVATE_SERVICE_ACCESS

tier = "BASIC"(単一ノード)を STANDARD_HA に変更しプライマリ+レプリカの自動フェイルオーバー(SLA 99.9%)を得る。接続方式もレガシーな DIRECT_PEERING から、Cloud SQL と同じ PRIVATE_SERVICE_ACCESS(Service Networking)に統一する。

🔐

AUTH/TLS + maxmemory-policy

auth_enabled = true + transit_encryption_mode = SERVER_AUTHENTICATION で平文・無認証接続を廃止し、AUTH文字列は Secret Manager で管理する。デフォルト noeviction はキャッシュ用途では書き込みエラーの温床になるため allkeys-lru に変更する。

🔒

キャッシュスタンピード対策(分散ロック)

キャッシュミス時に全Podが同時にDBへ問い合わせる「キャッシュスタンピード」を、SET key value NX EX ttl による分散ロックで1リクエストに絞り込む。ロックを取得できなかったPodは短間隔でキャッシュをポーリングして結果を待つ。

⏱️

TTLジッター + Circuit Breaker

固定TTLによる同時失効(Thundering Herd)を防ぐため乱数ジッターを加える。Redis呼び出しの連続失敗を CircuitBreakerState(値オブジェクト)で追跡し、閾値超過で一定時間Redisを迂回しDBへ自動フォールバックする。

問題

ECサイト MOps チームでは、フラッシュセール開催時に「残り在庫数」をリアルタイム表示する在庫参照APIを Cloud Run 上で運用している。セール開始直後は同一商品への読み取りが秒間数千リクエストに達し、毎回 Cloud SQL に問い合わせるとプライマリDBの負荷が急上昇するため、Memorystore for Redis をキャッシュ層として導入することになった。以下の Terraform コードと Python アプリケーションコードには 7つの設計上の問題 が潜んでいる。

問題点を全て洗い出しSTANDARD_HA(自動フェイルオーバー)× PRIVATE_SERVICE_ACCESS 接続 × AUTH/TLS × maxmemory-policy(allkeys-lru) × コネクションプール × キャッシュスタンピード対策(分散ロック)× TTLジッター + Circuit Breaker フォールバック を考慮した Bad→Good リファクタリングを行ってください。

制約・前提条件

  • Terraform 1.8+(google プロバイダー 5.x)、Memorystore for Redis 7.2、リージョン asia-northeast1
  • Cloud Run v2 サービスから Direct VPC Egress で Redis に接続する想定
  • キャッシュキーは stock:{product_id}、在庫データの正は Cloud SQL、Redis は read-through cache として使う
  • セール開始直後は同一商品への読み取りが秒間数千リクエストに達し、キャッシュミス時に多数の Pod が同時に DB へ問い合わせる懸念がある
  • 現状は BASIC ティア(単一ノード・フェイルオーバーなし)・AUTH/TLS 無効・maxmemory-policy 未設定(デフォルト noeviction)・固定TTL・コネクションプールなし・Redis 障害時にアプリがそのままエラーになる
期待する回答形式: 問題点の列挙(番号付き)+ 改善後 Terraform コード + 改善後 Python コード + 設計意図の説明

悪いコード (Before)

このコードには 7つの設計上の問題 が隠れています。見つけてみてください。
bad_stock_cache.tf — BASIC単一ノード・DIRECT_PEERING・AUTH/TLS無効・maxmemory-policy未設定
resource "google_redis_instance" "stock_cache" {
  name           = "mops-stock-cache"
  region         = "asia-northeast1"
  memory_size_gb = 4
  redis_version  = "REDIS_7_2"

  # 問題①: 単一ノード、フェイルオーバーなし
  tier = "BASIC"

  # 問題②: レガシー接続方式(VPCルート漏洩・IPレンジ管理の負荷大)
  connect_mode        = "DIRECT_PEERING"
  authorized_network  = google_compute_network.vpc.id

  # 問題③: 認証なし・平文接続
  auth_enabled            = false
  transit_encryption_mode = "DISABLED"

  # 問題④: redis_configs 未指定 → maxmemory-policy はデフォルト noeviction
}
bad_stock_cache.py — プールなし・スタンピード対策なし・固定TTL・フォールバックなし
import redis

def get_stock(product_id: str) -> int:
    # 問題⑤: リクエストのたびに新規クライアント生成(コネクションプールなし)
    r = redis.Redis(host="10.1.2.3", port=6379)

    cached = r.get(f"stock:{product_id}")
    if cached is not None:
        return int(cached)

    # 問題⑥: キャッシュミス時、全Podが同時にDBへ問い合わせる
    #        (キャッシュスタンピード)
    stock = query_stock_from_db(product_id)

    # 問題⑦: 固定TTL(同時失効→Thundering Herd)。
    #        Redis障害時のフォールバックもなくエラーがそのまま伝播する
    r.set(f"stock:{product_id}", stock, ex=60)
    return stock
問題点サマリー(7点)
1tier = BASIC(単一ノード) — フェイルオーバーなし。STANDARD_HA
2connect_mode = DIRECT_PEERING(レガシー)PRIVATE_SERVICE_ACCESS
3AUTH/TLS 無効 — 有効化 + Secret Manager でAUTH文字列管理
4maxmemory-policy 未設定(noeviction)allkeys-lru
5コネクションプールなしredis.ConnectionPool で共有
6キャッシュスタンピード対策なしSET NX EX 分散ロック
7固定TTL + フォールバックなし — TTLジッター + Circuit Breaker

ヒント(段階的開示)

ヒント1 — 方向性
Memorystore for Redis をキャッシュ層に導入する際の問題は3層に分類できる。(1) インフラ/可用性BASIC ティア(単一ノード)・DIRECT_PEERING(レガシー接続)・AUTH/TLS 無効、(2) メモリ管理maxmemory-policy 未設定によるキャッシュ用途でのOOM書き込みエラー、(3) アプリケーション/耐障害性 — コネクションプールなし・キャッシュスタンピード(トラフィック急増時に多数のPodが同時にDBへ殺到)・固定TTLによる同時失効(Thundering Herd)・Redis障害時のフォールバック欠如。Memorystore for Redis は Cloud SQL の Private IP と同様に PRIVATE_SERVICE_ACCESS(Service Networking)を使うのが現在の推奨接続方式。
ヒント2 — アプローチ
  • 問題①: tier = "BASIC"(単一ノード、フェイルオーバーなし)→ STANDARD_HA(プライマリ+レプリカ、自動フェイルオーバー、SLA 99.9%)
  • 問題②: connect_mode = "DIRECT_PEERING"(レガシー、VPCルート漏洩リスク)→ PRIVATE_SERVICE_ACCESS(Service Networking 経由)
  • 問題③: auth_enabled = false + transit_encryption_mode = "DISABLED"(平文・無認証)→ 両方有効化し、AUTH文字列を Secret Manager で管理
  • 問題④: redis_configs 未設定(デフォルト noeviction はメモリ逼迫時に書き込みエラー)→ maxmemory-policy = "allkeys-lru"
  • 問題⑤: リクエストごとに redis.Redis() 新規生成 → redis.ConnectionPool をモジュールレベルで共有
  • 問題⑥: キャッシュミス時、全Podが同時にDBへ問い合わせ → SET NX EX の分散ロックで1リクエストに絞り込み、他は結果をポーリング待機
  • 問題⑦: 固定TTL(同時失効→Thundering Herd)+ Redis障害時のフォールバック欠如 → TTLに乱数ジッターを加え、簡易サーキットブレーカーでDB自動フォールバック
ヒント3 — コードの骨格
# Memorystore for Redis スケルトン
resource "google_redis_instance" "stock_cache" {
  tier                    = "STANDARD_HA"              # 修正①
  connect_mode            = "PRIVATE_SERVICE_ACCESS"   # 修正②
  auth_enabled            = true                       # 修正③
  transit_encryption_mode = "SERVER_AUTHENTICATION"    # 修正③
  redis_configs = {
    maxmemory-policy = "allkeys-lru"                   # 修正④
  }
}
# キャッシュスタンピード対策 + TTLジッター スケルトン
def get_stock(product_id: str) -> int:
    if _is_breaker_open():                 # 修正⑦: 障害時はDB直行
        return query_stock_from_db(product_id)
    try:
        client = _redis_client()           # 修正⑤: プールから取得
        cached = client.get(f"stock:{product_id}")
        if cached is not None:
            return int(cached)
        return _fetch_with_lock(client, product_id)  # 修正⑥: 分散ロック
    except RedisError:
        _record_failure()
        return query_stock_from_db(product_id)

問題点分析(7点)

#問題点分類改善方法
1tier = BASIC(単一ノード)可用性STANDARD_HA(自動フェイルオーバー、SLA 99.9%)
2connect_mode = DIRECT_PEERING(レガシー)ネットワークPRIVATE_SERVICE_ACCESS(Service Networking)
3AUTH/TLS 無効(平文・無認証)セキュリティauth_enabled + transit_encryption_mode + Secret Manager
4maxmemory-policy 未設定(noeviction)メモリ管理redis_configs { maxmemory-policy = allkeys-lru }
5コネクションプールなし(毎回新規接続)接続管理redis.ConnectionPool の共有
6キャッシュスタンピード対策なし負荷集中SET NX EX による分散ロック
7固定TTL + フォールバック欠如耐障害性TTLジッター + 簡易 Circuit Breaker

アーキテクチャ図 — Bad vs Good の変換フロー

Bad(変更前)— 単一ノード・無認証・スタンピード無防備 Cloud Run Pod × N(フラッシュセール流入) 問題⑤: リクエストごとに redis.Redis() 新規生成(プールなし) 問題⑥: キャッシュミス時、全Podが同時にDBへ問い合わせ 問題⑦: 固定TTL(ex=60) → 同時失効でThundering Herd Redis障害時はそのまま例外が伝播しAPIがエラーを返す Memorystore for Redis(単一ノード) 問題①: tier=BASIC(フェイルオーバーなし) 問題②: connect_mode=DIRECT_PEERING(レガシー接続) 問題③: auth_enabled=false・平文接続(VPC内から無認証アクセス可) 問題④: maxmemory-policy未設定 → noeviction でOOM書き込みエラー Cloud SQL(在庫マスタ) キャッシュミス時の同時殺到でキャッシュ導入前と同じ負荷が再現 × ノード障害でキャッシュ層が丸ごと停止 × 認証なしでVPC内から在庫データにアクセス可能 × メモリ逼迫で新規キャッシュ書き込みがエラーになる × キャッシュミスの瞬間に元のDB負荷がそのまま再現 × TTL一斉失効でThundering Herd、Redis障害でAPI全断 Good(変更後)— HA・認証・分散ロック・フォールバック Cloud Run Pod × N(共有 ConnectionPool) 修正⑤: redis.ConnectionPool を共有(TCP接続を使い回す) 修正⑥: SET NX EX 分散ロック → 1リクエストのみDBへ、他はポーリング待機 修正⑦: TTL = 60 + random(0,10) でジッター付与 修正⑦: CircuitBreakerState で連続失敗検知 → OPEN中はDB直行 Memorystore for Redis(STANDARD_HA) 修正①: プライマリ+レプリカ、自動フェイルオーバー(SLA 99.9%) 修正②: PRIVATE_SERVICE_ACCESS(Service Networking 経由) 修正③: auth_enabled=true + TLS(AUTH文字列は Secret Manager) 修正④: maxmemory-policy=allkeys-lru(自動追い出しでOOM回避) ブレーカーOPEN時は迂回 Cloud SQL(在庫マスタ) 分散ロックにより問い合わせが1リクエストに集約される ✓ ノード障害でも自動フェイルオーバーでキャッシュ継続 ✓ AUTH + TLS で認証済み・暗号化された接続のみ許可 ✓ allkeys-lru でメモリ逼迫時も書き込みエラーにならない ✓ 分散ロックでキャッシュミス時のDB負荷を1リクエストに抑制 ✓ TTLジッター + Circuit Breakerで一斉失効・Redis障害に強い 修正

模範解答

Before — BASIC・DIRECT_PEERING・AUTH/TLS無効・maxmemory-policy未設定
resource "google_redis_instance" "stock_cache" {
  name           = "mops-stock-cache"
  region         = "asia-northeast1"
  memory_size_gb = 4
  redis_version  = "REDIS_7_2"

  tier = "BASIC"                          # 問題①

  connect_mode       = "DIRECT_PEERING"   # 問題②
  authorized_network = google_compute_network.vpc.id

  auth_enabled            = false         # 問題③
  transit_encryption_mode = "DISABLED"    # 問題③

  # 問題④: redis_configs なし(maxmemory-policy = noeviction)
}
After — STANDARD_HA + PRIVATE_SERVICE_ACCESS + AUTH/TLS + allkeys-lru
# ── Private Service Access(Cloud SQL と同じ仕組み)────────────
resource "google_compute_global_address" "redis_private_ip_range" {
  name          = "mops-redis-private-ip"
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 20
  network       = google_compute_network.vpc.id
}
resource "google_service_networking_connection" "private_vpc" {
  network                 = google_compute_network.vpc.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.redis_private_ip_range.name]
}

# ── Memorystore for Redis(HA + Private + AUTH/TLS)───────────
resource "google_redis_instance" "stock_cache" {
  name           = "mops-stock-cache"
  region         = "asia-northeast1"
  memory_size_gb = 5
  redis_version  = "REDIS_7_2"

  tier = "STANDARD_HA"                            # 修正①: 自動フェイルオーバー

  connect_mode        = "PRIVATE_SERVICE_ACCESS"  # 修正②
  authorized_network  = google_compute_network.vpc.id
  depends_on          = [google_service_networking_connection.private_vpc]

  auth_enabled            = true                            # 修正③
  transit_encryption_mode = "SERVER_AUTHENTICATION"          # 修正③

  redis_configs = {
    maxmemory-policy = "allkeys-lru"                         # 修正④
  }

  replica_count      = 1
  read_replicas_mode = "READ_REPLICAS_DISABLED"

  maintenance_policy {
    weekly_maintenance_window {
      day = "SUNDAY"
      start_time { hours = 18 minutes = 0 }
    }
  }
}

# AUTH文字列を Secret Manager に保存
resource "google_secret_manager_secret" "redis_auth" {
  secret_id = "mops-stock-cache-auth"
  replication { auto {} }
}
resource "google_secret_manager_secret_version" "redis_auth_value" {
  secret      = google_secret_manager_secret.redis_auth.id
  secret_data = google_redis_instance.stock_cache.auth_string
}

# ── Cloud Run v2(Direct VPC Egress)───────────────────────────
resource "google_cloud_run_v2_service" "stock_api" {
  name     = "mops-stock-api"
  location = "asia-northeast1"

  template {
    vpc_access {
      network_interfaces {
        network    = google_compute_network.vpc.id
        subnetwork = google_compute_subnetwork.run_subnet.id
      }
      egress = "PRIVATE_RANGES_ONLY"
    }
    containers {
      image = "asia-northeast1-docker.pkg.dev/PROJECT_ID/mops/stock-api@sha256:abc123"
      env {
        name  = "REDIS_HOST"
        value = google_redis_instance.stock_cache.host
      }
      env {
        name = "REDIS_AUTH"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.redis_auth.secret_id
            version = "latest"
          }
        }
      }
    }
  }
}
Before — プールなし・スタンピード無防備・固定TTL・フォールバックなし
import redis

def get_stock(product_id: str) -> int:
    r = redis.Redis(host="10.1.2.3", port=6379)  # 問題⑤

    cached = r.get(f"stock:{product_id}")
    if cached is not None:
        return int(cached)

    stock = query_stock_from_db(product_id)       # 問題⑥

    r.set(f"stock:{product_id}", stock, ex=60)     # 問題⑦
    return stock
After — ConnectionPool + 分散ロック + TTLジッター + Circuit Breaker
"""stock_cache.py — フラッシュセール在庫数キャッシュ(Redis cache-aside)"""
from __future__ import annotations

import random
import time
from dataclasses import dataclass, replace

import redis
from redis.exceptions import RedisError

CACHE_KEY_PREFIX = "stock"
BASE_TTL_SECONDS = 60
TTL_JITTER_SECONDS = 10           # 修正⑦: 同時失効を防ぐジッター幅
LOCK_TTL_SECONDS = 5
LOCK_WAIT_TIMEOUT_SECONDS = 3.0
LOCK_POLL_INTERVAL_SECONDS = 0.05
FAILURE_THRESHOLD = 5
OPEN_DURATION_SECONDS = 10.0

# 修正⑤: プロセス内で共有するコネクションプール
_pool = redis.ConnectionPool(
    host="REDIS_HOST_PLACEHOLDER",
    port=6379,
    password="REDIS_AUTH_PLACEHOLDER",
    ssl=True,
    max_connections=50,
    socket_timeout=1.0,
    socket_connect_timeout=1.0,
)


@dataclass(frozen=True, slots=True)
class CircuitBreakerState:
    """Redis障害検知の簡易サーキットブレーカー状態(値オブジェクト)。"""

    failure_count: int = 0
    opened_at: float | None = None


_breaker = CircuitBreakerState()


def _redis_client() -> redis.Redis:
    return redis.Redis(connection_pool=_pool)


def _is_breaker_open() -> bool:
    if _breaker.opened_at is None:
        return False
    return (time.monotonic() - _breaker.opened_at) < OPEN_DURATION_SECONDS


def _record_failure() -> None:
    global _breaker
    new_count = _breaker.failure_count + 1
    opened_at = time.monotonic() if new_count >= FAILURE_THRESHOLD else _breaker.opened_at
    _breaker = replace(_breaker, failure_count=new_count, opened_at=opened_at)


def _record_success() -> None:
    global _breaker
    _breaker = CircuitBreakerState()


def get_stock(product_id: str, db_lookup=query_stock_from_db) -> int:
    """在庫数を取得する(Redis優先、障害時はCloud SQLへフォールバック)。

    Args:
        product_id: 商品ID。
        db_lookup: DB問い合わせ関数(テスト時に差し替え可能)。

    Returns:
        在庫数。
    """
    cache_key = f"{CACHE_KEY_PREFIX}:{product_id}"

    if _is_breaker_open():                       # 修正⑦
        return db_lookup(product_id)

    try:
        client = _redis_client()                 # 修正⑤
        cached = client.get(cache_key)
        if cached is not None:
            _record_success()
            return int(cached)

        stock = _fetch_with_stampede_protection(  # 修正⑥
            client, product_id, cache_key, db_lookup
        )
        _record_success()
        return stock

    except RedisError:
        _record_failure()                         # 修正⑦
        return db_lookup(product_id)


def _fetch_with_stampede_protection(
    client: redis.Redis, product_id: str, cache_key: str, db_lookup
) -> int:
    """分散ロックでDB問い合わせを1リクエストに絞り込む。"""
    lock_key = f"lock:{cache_key}"
    deadline = time.monotonic() + LOCK_WAIT_TIMEOUT_SECONDS

    while time.monotonic() < deadline:
        acquired = client.set(lock_key, "1", nx=True, ex=LOCK_TTL_SECONDS)
        if acquired:
            try:
                stock = db_lookup(product_id)
                ttl = BASE_TTL_SECONDS + random.randint(0, TTL_JITTER_SECONDS)
                client.set(cache_key, stock, ex=ttl)
                return stock
            finally:
                client.delete(lock_key)

        time.sleep(LOCK_POLL_INTERVAL_SECONDS)
        cached = client.get(cache_key)
        if cached is not None:
            return int(cached)

    return db_lookup(product_id)


def query_stock_from_db(product_id: str) -> int:
    """Cloud SQL から在庫数を取得する(実装は省略)。"""
    ...
問題修正内容効果
① tier=BASICSTANDARD_HA(プライマリ+レプリカ)ノード障害時も自動フェイルオーバーでキャッシュ層を継続
② DIRECT_PEERINGPRIVATE_SERVICE_ACCESSCloud SQLと同じ接続方式に統一し運用負荷を削減
③ AUTH/TLS無効auth_enabled + transit_encryption + Secret Manager認証・暗号化された接続のみ許可
④ maxmemory-policy未設定allkeys-lruメモリ逼迫時も書き込みエラーにならず自動追い出し
⑤ コネクションプールなしredis.ConnectionPool 共有TCPハンドシェイクのオーバーヘッドを削減
⑥ スタンピード対策なしSET NX EX 分散ロックキャッシュミス時のDB問い合わせを1リクエストに集約
⑦ 固定TTL/フォールバックなしTTLジッター + Circuit Breaker同時失効を分散、Redis障害時もAPIを継続

ポイント解説

1キャッシュ層はDBよりも先に負荷が集中しやすい — フラッシュセールのようなスパイクトラフィックは、DBよりも先にキャッシュ層(コネクション数・メモリ)に負荷が集中する。BASIC ティアのような単一ノード構成は、最も守りたい瞬間に単一障害点になりやすい。
2noeviction はキャッシュ用途では事故りやすいデフォルト値 — Memorystore for Redis のデフォルト maxmemory-policy は Redis本家のデフォルトを踏襲した noeviction。永続化データストアとしては妥当だが、キャッシュ用途では書き込みエラーの温床になるため明示的に allkeys-lru 等へ変更する意識が必要。
3キャッシュスタンピードは「キャッシュを入れたのに負荷が減らない」典型パターン — キャッシュミス時の挙動を設計しないと、ヒット率が高くても「ミスした瞬間」に元のDB負荷がそのまま再現される。分散ロック(SETNX)・確率的早期再計算・シングルフライトのいずれかで対策する。
4TTLジッターは大量発行したキャッシュで特に重要 — 一括インポートやキャンペーン開始時の一斉書き込みなど、多数のキーが同じタイミングで生成される場面ではTTL固定値が思わぬThundering Herdを引き起こす。乱数幅は「ベースTTLの10〜20%」を目安にする。
5サーキットブレーカーは「キャッシュはあくまで最適化」という原則を守る仕組み — Redisはあくまでレイテンシ最適化のためのレイヤーであり、正はDBにある。Redis障害時にアプリ全体がエラーになる設計は「キャッシュがないと動かないシステム」になっており、フォールバックの実装がキャッシュ導入とセットで必要。
6google_redis_instance のティア変更は再作成を伴うBASICSTANDARD_HA の変更はTerraform上でインスタンスの再作成(ダウンタイムを伴う)になる場合がある。本番導入時は新規インスタンスを作成しアプリ側の接続先を段階的に切り替える方が安全。

実務への応用

MOps チームのフラッシュセール在庫APIのように「読み取りが一時的に跳ね上がるが正のデータはDBにある」ワークロードでは、キャッシュ導入そのものよりも「キャッシュミス時の挙動」の設計がスケーラビリティを左右する。分散ロックによるスタンピード対策は実装コストがあるため、まずはトラフィック予測(過去のセール実績)から必要性を判断し、被害が大きい商品(在庫僅少の目玉商品)から段階的に適用するのが現実的。

Redis AUTH文字列を Secret Manager 経由で配布する構成は、Cloud SQL の IAM DB 認証ほど強力ではないが、静的パスワードをTerraformコードやtfstateに平文で残さないという点で最低限のセキュリティラインになる。

他機能への横展開: キャッシュスタンピード対策の分散ロックは、在庫APIだけでなく「ランキング集計」「クーポン残数表示」など、書き込み頻度が低く読み取りが集中する他のMOps機能にも同じパターンで適用できる。

今日のまとめ

Memorystore for Redis キャッシュ層導入の7点チェックリスト: ① STANDARD_HA(自動フェイルオーバー、SLA 99.9%)② PRIVATE_SERVICE_ACCESS(Service Networking 経由の接続)③ AUTH + TLS(Secret Manager でのAUTH文字列管理)④ maxmemory-policy=allkeys-lru(noeviction による書き込みエラー回避)⑤ コネクションプール(毎リクエスト新規接続の排除)⑥ キャッシュスタンピード対策(SET NX EX 分散ロック)⑦ TTLジッター + サーキットブレーカー(同時失効防止 + Redis障害時のDBフォールバック)。 キャッシュ導入は「ヒット時の速さ」だけでなく「ミスした瞬間」「障害が起きた瞬間」の挙動を設計して初めて負荷対策として機能する。

次のステップ

  • 発展問題: allkeys-lru の代わりに allkeys-lfu(アクセス頻度ベースの追い出し)を使った場合の挙動の違いを検証し、フラッシュセールのような「短時間に特定キーへアクセスが集中する」ワークロードでどちらが適切かを比較せよ。加えて、分散ロックを SETNX の自前実装ではなく Redlock アルゴリズム(複数Redisノードでの合意)に置き換える必要があるケースを考察せよ
  • 参考: Memorystore for Redis Tier 比較(Basic/Standard)ドキュメント / Private Service Access(VPC ピアリング)/ Redis maxmemory-policy 公式ドキュメント / キャッシュスタンピード対策パターン(Mutex Lock / Probabilistic Early Expiration)/ Cloud Run Direct VPC Egress

自己評価(あとで記入)