Späť na blog

Redis Cluster Migrácia Slotov: Dočasná Explózia Pamäte

Slot migration je vacsinou v pohode, kym zrazu nie je. “Redis pod zabitý OOM počas reshardu clustra.” Príčina: pri migrácii slotov medzi nodami Redis kopíruje všetky kľúče do cieľového nodu pred vyčistením zdroja, čo spôsobuje dočasné zdvojnásobenie pamäte.

Prostredie: Redis Cluster 6.0+, Kubernetes s memory limitmi, operácie rebalancingu slotov, migrácie veľkých kľúčov

Problém

OOMKill Počas Rebalancingu

Časová os rebalancingu clustra:

T+0:00   Cluster má 3 mastre, každý ~4GB pamäte (limit 5GB)
         node-0: sloty 0-5460     (4.1GB)
         node-1: sloty 5461-10922 (3.9GB)
         node-2: sloty 10923-16383 (4.0GB)

T+0:05   Štart rebalancingu: presun 1000 slotov z node-0 na node-1

T+0:30   node-1 pamäť: 4.2GB (prijíma kľúče z migrácie)
T+1:00   node-1 pamäť: 4.8GB (prichádzajú ďalšie kľúče)
T+1:15   node-1 pamäť: 5.2GB → OOMKilled!

         Cluster vstupuje do FAIL stavu
         Chaos pri failoveri, nekonzistencia dát

Proces Migrácie

Ako funguje migrácia Redis slotov:

Zdrojový Node (node-0)               Cieľový Node (node-1)
┌──────────────────────┐             ┌──────────────────────┐
│ Slot 5461            │             │                      │
│ ├─ key1: 100MB       │─── KÓPIA ──>│ key1: 100MB (nový)   │
│ ├─ key2: 50MB        │             │                      │
│ └─ key3: 200MB       │             │                      │
│                      │             │                      │
│ (stále tu!)          │             │ (tiež tu!)           │
└──────────────────────┘             └──────────────────────┘

Pamäť počas migrácie:
- Zdroj drží kľúče kým slot nie je plne migrovaný
- Cieľ drží nové kópie
- Celková pamäť dočasne ZDVOJNÁSOBENÁ pre migrované kľúče!

Až po dokončení migrácie slotu:
- Zdroj zmaže kľúče
- Ale počas migrácie oba nody držia kópie

Príčina

Fázy Migrácie

Detailné kroky migrácie slotu:

1. CLUSTER SETSLOT <slot> MIGRATING <destination-id>
   Zdroj označí slot ako "migrating" - stále obsluhuje reads/writes

2. CLUSTER SETSLOT <slot> IMPORTING <source-id>
   Cieľ označí slot ako "importing" - pripravený prijímať

3. Pre KAŽDÝ kľúč v slote:
   MIGRATE <dest-host> <dest-port> "" 0 5000 KEYS <key>
   - Kľúč je SKOPÍROVANÝ do cieľa (alokovaná pamäť)
   - Kľúč zostáva na zdroji kým nie sú migrované všetky kľúče!

4. CLUSTER SETSLOT <slot> NODE <destination-id>
   Vlastníctvo slotu prenesené

5. Zdroj zmaže migrované kľúče (konečne!)

Okno memory spieku: Kroky 3-5
Trvanie: Úmerné veľkosti dát slotu a rýchlosti siete

Prečo Nastáva OOM

# Zjednodušený výpočet

source_memory_before = 4.1 GB
destination_memory_before = 3.9 GB
memory_limit = 5.0 GB

slots_to_migrate = 1000  # z 5461
data_per_migrating_slot = 4.1 GB * (1000/5461) = 750 MB

# Počas migrácie:
destination_memory_during = 3.9 GB + 750 MB = 4.65 GB  # Tesné!

# Ale ak je migrácia pomalá alebo kľúče veľké:
# - Viac kľúčov sa nahromadí na cieli
# - Zdroj ešte neuvoľnil
# - Cieľ OOM pred tým než zdroj môže uvoľniť pamäť

Diagnostika

Monitoruj Pamäť Počas Migrácie

# Sleduj pamäť na oboch nodoch počas rebalancingu
watch -n 1 "redis-cli -h node-0 INFO memory | grep used_memory_human"
watch -n 1 "redis-cli -h node-1 INFO memory | grep used_memory_human"

# Skontroluj stav migrácie slotu
redis-cli -h node-0 CLUSTER INFO | grep migrating
redis-cli -h node-1 CLUSTER INFO | grep importing

# Pozri ktoré kľúče sa migrujú
redis-cli -h node-0 CLUSTER GETKEYSINSLOT 5461 10

Skontroluj Pred Migráciou

#!/bin/bash
# pre_migration_check.sh

SOURCE_NODE=$1
DEST_NODE=$2
SLOTS_TO_MOVE=$3

source_mem=$(redis-cli -h $SOURCE_NODE INFO memory | grep used_memory: | cut -d: -f2)
dest_mem=$(redis-cli -h $DEST_NODE INFO memory | grep used_memory: | cut -d: -f2)
dest_limit=$(redis-cli -h $DEST_NODE CONFIG GET maxmemory | tail -1)

# Odhadni veľkosť dát pre migrované sloty
source_total_keys=$(redis-cli -h $SOURCE_NODE DBSIZE)
source_total_slots=5461  # Pre 3-master cluster

estimated_migration_size=$((source_mem * SLOTS_TO_MOVE / source_total_slots))
projected_dest_mem=$((dest_mem + estimated_migration_size))

echo "Zdrojová pamäť: $source_mem"
echo "Cieľová pamäť: $dest_mem"
echo "Cieľový limit: $dest_limit"
echo "Odhadovaná veľkosť migrácie: $estimated_migration_size"
echo "Projektovaná cieľová pamäť: $projected_dest_mem"

if [ $projected_dest_mem -gt $dest_limit ]; then
    echo "VAROVANIE: Migrácia môže spôsobiť OOM na cieli!"
    exit 1
fi

Identifikuj Veľké Kľúče

# Nájdi veľké kľúče ktoré budú migrované
for slot in $(seq 5461 6461); do
    keys=$(redis-cli -h node-0 CLUSTER GETKEYSINSLOT $slot 100)
    for key in $keys; do
        size=$(redis-cli -h node-0 MEMORY USAGE $key)
        if [ "$size" -gt 10000000 ]; then  # > 10MB
            echo "Veľký kľúč v slote $slot: $key ($size bytes)"
        fi
    done
done

Riešenie

Možnosť 1: Zvýš Pamäťovú Rezervu

# Kubernetes: Daj viac pamäťovej rezervy
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-cluster
spec:
  template:
    spec:
      containers:
      - name: redis
        resources:
          limits:
            memory: "8Gi"   # Bolo 5Gi
          requests:
            memory: "6Gi"

        # Nakonfiguruj Redis aby nechal rezervu
        args:
        - redis-server
        - --maxmemory
        - "5gb"  # 60% limitu, nechaj miesto pre migráciu
        - --maxmemory-policy
        - "noeviction"

Možnosť 2: Migruj Menej Slotov Naraz

#!/bin/bash
# gradual_rebalance.sh - Migruj v malých dávkach

BATCH_SIZE=100  # Slotov na dávku
PAUSE_SECONDS=30

total_slots_to_move=1000
moved=0

while [ $moved -lt $total_slots_to_move ]; do
    echo "Migrácia slotov $moved do $((moved + BATCH_SIZE))"

    redis-cli --cluster reshard node-0:6379 \
        --cluster-from node-0-id \
        --cluster-to node-1-id \
        --cluster-slots $BATCH_SIZE \
        --cluster-yes

    echo "Čakanie na stabilizáciu pamäte..."
    sleep $PAUSE_SECONDS

    # Skontroluj cieľovú pamäť pred pokračovaním
    dest_mem=$(redis-cli -h node-1 INFO memory | grep used_memory_rss: | cut -d: -f2)
    max_mem=$(redis-cli -h node-1 CONFIG GET maxmemory | tail -1)

    if [ $((dest_mem * 100 / max_mem)) -gt 80 ]; then
        echo "Cieľ na ${dest_mem}/${max_mem}, čakám na cleanup..."
        sleep 60
    fi

    moved=$((moved + BATCH_SIZE))
done

Možnosť 3: Pred-zmaž Studené Kľúče

#!/usr/bin/env python3
# pre_migration_cleanup.py - Odstráň studené kľúče pred migráciou

import redis
from datetime import datetime, timedelta

def cleanup_cold_keys(source_host, slots_to_migrate, idle_threshold_days=30):
    r = redis.Redis(host=source_host, port=6379)

    for slot in slots_to_migrate:
        keys = r.cluster('GETKEYSINSLOT', slot, 1000)

        for key in keys:
            # Skontroluj či je kľúč studený (neprístupný nedávno)
            idle_time = r.object('IDLETIME', key)  # Vracia sekundy

            if idle_time > idle_threshold_days * 86400:
                # Kľúč nebol prístupný N dní
                key_size = r.memory_usage(key)
                print(f"Mažem studený kľúč {key} ({key_size} bytes, idle {idle_time}s)")
                r.delete(key)

    # Vynúť defragmentáciu pamäte
    r.config_set('activedefrag', 'yes')

Možnosť 4: Použi MIGRATE REPLACE Opatrne

# Pre veľmi veľké kľúče, zváž DUMP/RESTORE so zmazaním
# Toto je manuálne ale dáva viac kontroly

redis-cli -h source-node DUMP large_key > /tmp/key.dump
redis-cli -h dest-node RESTORE large_key 0 "$(cat /tmp/key.dump)"
redis-cli -h source-node DEL large_key

# Toto zmaže zo zdroja pred migráciou, vyhne sa dvojitej pamäti
# Ale vyžaduje opatrnú správu prechodu slotu

Monitoring

groups:
  - name: redis-cluster
    rules:
      - alert: RedisClusterMigrationMemory
        expr: |
          (redis_memory_used_bytes / redis_memory_max_bytes) > 0.85
          and redis_cluster_slots_migrating > 0
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "Redis node pamäť vysoká počas migrácie slotov"

      - alert: RedisSlotMigrationStuck
        expr: |
          increase(redis_cluster_slots_migrating[10m]) == 0
          and redis_cluster_slots_migrating > 0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Migrácia slotov vyzerá zaseknutá"

Checklist

## Redis Cluster Migrácia Slotov Pamäť

### Pred Migráciou
- [ ] Vypočítaj celkovú veľkosť dát v slotoch na migráciu
- [ ] Over že cieľ má rezervu (dáta + 50%)
- [ ] Identifikuj a ošetri veľké kľúče (>100MB)
- [ ] Zváž zmazanie studených/expirovaných kľúčov najprv
- [ ] Nastav monitoring pamäte na oboch nodoch

### Počas Migrácie
- [ ] Migruj v malých dávkach (100-500 slotov)
- [ ] Pauza medzi dávkami pre stabilizáciu pamäte
- [ ] Monitoruj pamäť zdroja aj cieľa
- [ ] Sleduj zaseknutie/timeout migrácie

### Ak Nastane OOM
- [ ] Skontroluj stav clustra: CLUSTER INFO
- [ ] Identifikuj ktoré sloty boli uprostred migrácie
- [ ] Možno treba abortovať a reštartovať: CLUSTER SETSLOT STABLE
- [ ] Zvýš memory limity pred ďalším pokusom

Záver

Lekcia: Migrácia Redis slotov je proces kopírovania-potom-mazania, nie presun. Plánuj pre dočasné zdvojnásobenie pamäte pri rebalancingu clustrov, hlavne s testnými memory limitmi v Kubernetes.

Kľúčové princípy:

  1. Cieľ potrebuje rezervu pre migrované dáta plus existujúce dáta
  2. Migruj v dávkach s pauzami pre stabilizáciu pamäte
  3. Monitoruj oba nody počas migrácie
  4. Pred-vyčisti studené kľúče na zníženie veľkosti migrácie

Súvisiace články

Súvisiace články

Citujte tento článok

Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.

Michal Drozd. "Redis Cluster Migrácia Slotov: Dočasná Explózia Pamäte". https://www.michal-drozd.com/sk/blog/redis-cluster-slot-migration-memory/ (Publikované 27. januára 2025).