Redis is omnipresent in production infrastructure — caching, sessions, queues, pub/sub, rate limiting. Being able to inspect and manipulate Redis from the shell is essential for debugging, operations, and building lightweight coordination tools between services.
1
redis-cli basics and scripting patterns
BASH
# ── Connection and auth ───────────────────────────────────
redis-cli # localhost:6379
redis-cli -h redis.prod.internal -p 6380
redis-cli -h redis.prod.internal -a "${REDIS_PASS}"
redis-cli -u "redis://:${REDIS_PASS}@redis.prod.internal:6380"
# ── Suppress interactive output for scripting ─────────────
redis-cli --no-auth-warning -a "${REDIS_PASS}" PING
# ── Wrapper function ──────────────────────────────────────
redis() {
redis-cli --no-auth-warning \
-h "${REDIS_HOST:-localhost}" \
-p "${REDIS_PORT:-6379}" \
-a "${REDIS_PASS:-}" \
-n "${REDIS_DB:-0}" \
"$@"
}
# ── String operations ─────────────────────────────────────
redis SET mykey "hello"
redis GET mykey
redis SETEX session:abc123 3600 '{"user_id":42}' # with 1h TTL
redis TTL session:abc123 # seconds remaining
redis INCR counter:page_views
redis INCRBY counter:bytes_transferred 1024
# ── Hash operations ────────────────────────────────────────
redis HSET user:42 name "Alice" email "alice@example.com" plan pro
redis HGET user:42 email
redis HGETALL user:42
redis HINCRBY user:42 login_count 1
# ── List operations ───────────────────────────────────────
redis LPUSH queue:emails '{"to":"user@example.com"}'
redis RPOP queue:emails # dequeue from right
redis LLEN queue:emails # queue depth
2
Distributed lock and rate limiting
BASH
# ── Distributed lock (mutex) ──────────────────────────────
acquire_lock() {
local key="lock:${1}"
local ttl="${2:-30}" # seconds
local token="${HOSTNAME}:$$:$(date +%s%N)"
# SET NX EX: set only if not exists, with TTL
result=$(redis SET "${key}" "${token}" NX EX "${ttl}")
[[ "${result}" == "OK" ]] && echo "${token}" && return 0
return 1 # lock already held
}
release_lock() {
local key="lock:${1}" token="${2}"
# Lua: only delete if the token matches (atomic check-and-delete)
redis EVAL "if redis.call('GET',KEYS[1])==ARGV[1] then
return redis.call('DEL',KEYS[1]) else return 0 end" \
1 "${key}" "${token}"
}
# Usage: serialise a cron job
if TOKEN=$(acquire_lock "daily_report" 300); then
trap "release_lock daily_report ${TOKEN}" EXIT
generate_daily_report
else
echo "Another instance is running, skipping"
exit 0
fi
# ── Rate limiting: sliding window counter ─────────────────
check_rate_limit() {
local key="ratelimit:${1}"
local max="${2:-100}" # max requests
local window="${3:-60}" # per N seconds
count=$(redis EVAL "
local current = redis.call('INCR', KEYS[1])
if current == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end
return current
" 1 "${key}" "${window}")
(( count > max )) && return 1 # rate limited
return 0
}
3
Redis health monitoring script
BASH
#!/usr/bin/env bash
# redis_health.sh — Comprehensive Redis health check
set -euo pipefail
redis() { redis-cli --no-auth-warning -a "${REDIS_PASS:-}" "$@" 2>/dev/null; }
# ── Connectivity ──────────────────────────────────────────
redis PING | grep -q PONG || { echo "CRITICAL: Redis not responding"; exit 1; }
# ── Memory usage ─────────────────────────────────────────
MEM_USED=$(redis INFO memory | awk -F: '/^used_memory_human/{print $2}' | tr -d '\r')
MEM_PEAK=$(redis INFO memory | awk -F: '/^used_memory_peak_human/{print $2}' | tr -d '\r')
MEM_PCT=$(redis INFO memory | awk -F: '/^mem_fragmentation_ratio/{printf "%.0f", $2*100}')
printf " Memory: %s used / peak %s (frag ratio: %s%%)\n" \
"${MEM_USED}" "${MEM_PEAK}" "${MEM_PCT}"
# ── Connected clients ─────────────────────────────────────
CLIENTS=$(redis INFO clients | awk -F: '/^connected_clients/{print $2}' | tr -d '\r')
printf " Clients: %s connected\n" "${CLIENTS}"
# ── Key statistics ────────────────────────────────────────
KEYS=$(redis DBSIZE)
HITS=$(redis INFO stats | awk -F: '/^keyspace_hits/{print $2}' | tr -d '\r')
MISSES=$(redis INFO stats | awk -F: '/^keyspace_misses/{print $2}' | tr -d '\r')
TOTAL=$(( HITS + MISSES ))
HIT_RATE=$(( TOTAL > 0 ? HITS * 100 / TOTAL : 0 ))
printf " Keys: %s | Hit rate: %s%%\n" "${KEYS}" "${HIT_RATE}"
# ── Replication status ────────────────────────────────────
ROLE=$(redis INFO replication | awk -F: '/^role/{print $2}' | tr -d '\r')
printf " Role: %s\n" "${ROLE}"
[[ "${ROLE}" == "slave" ]] && {
LAG=$(redis INFO replication | awk -F: '/^master_last_io_seconds_ago/{print $2}')
(( LAG > 10 )) && echo " WARN: Replication lag ${LAG}s"
}
# ── Top 5 largest keys ────────────────────────────────────
echo " Largest keys:"
redis --bigkeys 2>/dev/null | grep '^Biggest' | head -5 | \
awk '{printf " %-30s %s bytes\n", $NF, $(NF-2)}'
vriddh@prod-01:~/scripts$./redis_health.sh
Memory: 842.12M used / peak 1.10G (frag ratio: 112%)
Clients: 84 connected
Keys: 142831 | Hit rate: 94%
Role: master
Largest keys:
cache:homepage_v2 2487312 bytes
█
✔ Redis from shell — Use
SET key value NX EX ttl for atomic lock acquisition. Use Lua scripts via EVAL for atomic check-and-act operations that must not race. Always use --no-auth-warning when passing -a to suppress the security warning in logs. Parse redis-cli INFO section output with AWK using -F: and pattern matching. Use redis-cli --pipe for bulk import via the Redis Inline Protocol.