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:
- Cieľ potrebuje rezervu pre migrované dáta plus existujúce dáta
- Migruj v dávkach s pauzami pre stabilizáciu pamäte
- Monitoruj oba nody počas migrácie
- Pred-vyčisti studené kľúče na zníženie veľkosti migrácie
Súvisiace články
- Go Timer Heap Pressure - Okrajové prípady správy pamäte
- Java Native Memory OOMKilled - Container memory limity
Súvisiace články
Java OOMKilled So Stabilným Heapom: Native Memory, Direct Buffers a glibc Arenas
Heap metriky vyzerajú dobre, GC je spokojný, ale kontajner stále umiera. Vinník: native memory z direct buffers, JNI a glibc memory allocator fragmentácia.
JVM Metaspace OOM v Kubernetes: Prečo MaxMetaspaceSize Nestačí
Pod OOMKilled napriek nastavenému MaxMetaspaceSize. Príčina: Metaspace rastie mimo heap, container memory limit nepočíta s tým, a triedy sa neuvoľňujú.
Redis Memory Fragmentácia: Keď maxmemory Nestačí
Váš Redis má 4GB maxmemory ale RSS ukazuje 6GB. OOM killer zasiahne. Vysvetlím jemalloc fragmentáciu s reprodukciou a tuningom activedefrag.
Kubernetes OOM Killer: Prečo Kontajner Zomiera pri 50% Pamäte
Kontajner má 4GB memory limit ale OOM kill pri 2GB used. Kernel buffers, page cache a cgroup accounting triky spôsobujú skoré OOMKills. Tu je celý obraz.
Citujte tento článok
Ak na článok odkazujete, pridajte pôvodnú URL a uveďte autora.