redis-expert
Included with Lifetime
$97 forever
Expert-level Redis for caching, pub/sub, data structures, and high-performance applications
datarediscachepubsubinmemorykeyvaluenosql
What this skill does
# Redis Expert
Expert guidance for Redis - the in-memory data structure store used as cache, message broker, and database with microsecond latency.
## Core Concepts
### Data Structures
- Strings (binary-safe, up to 512MB)
- Lists (linked lists)
- Sets (unordered unique strings)
- Sorted Sets (sets ordered by score)
- Hashes (field-value pairs)
- Streams (append-only log)
- Bitmaps and HyperLogLog
- Geospatial indexes
### Key Features
- In-memory storage with persistence
- Pub/Sub messaging
- Transactions
- Lua scripting
- Pipelining
- Master-Replica replication
- Redis Sentinel (high availability)
- Redis Cluster (horizontal scaling)
### Use Cases
- Caching layer
- Session storage
- Real-time analytics
- Message queues
- Rate limiting
- Leaderboards
- Geospatial queries
## Installation and Configuration
### Docker Setup
```bash
# Development
docker run --name redis -p 6379:6379 -d redis:7-alpine
# Production with persistence
docker run --name redis \
-p 6379:6379 \
-v redis-data:/data \
-d redis:7-alpine \
redis-server --appendonly yes --requirepass strongpassword
# Redis with config file
docker run --name redis \
-p 6379:6379 \
-v ./redis.conf:/usr/local/etc/redis/redis.conf \
-d redis:7-alpine \
redis-server /usr/local/etc/redis/redis.conf
```
### Configuration (redis.conf)
```conf
# Network
bind 0.0.0.0
port 6379
protected-mode yes
# Security
requirepass strongpassword
# Memory
maxmemory 2gb
maxmemory-policy allkeys-lru
# Persistence
save 900 1 # Save after 900s if 1 key changed
save 300 10 # Save after 300s if 10 keys changed
save 60 10000 # Save after 60s if 10000 keys changed
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
# Replication
replica-read-only yes
repl-diskless-sync yes
# Performance
tcp-backlog 511
timeout 0
tcp-keepalive 300
```
## Node.js Client (ioredis)
### Basic Operations
```typescript
import Redis from 'ioredis';
const redis = new Redis({
host: 'localhost',
port: 6379,
password: 'strongpassword',
db: 0,
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000);
return delay;
},
});
// Strings
await redis.set('user:1000:name', 'Alice');
await redis.set('counter', 42);
await redis.get('user:1000:name'); // 'Alice'
// Expiration (TTL)
await redis.setex('session:abc123', 3600, JSON.stringify({ userId: 1000 }));
await redis.expire('user:1000:name', 300); // 5 minutes
await redis.ttl('user:1000:name'); // Returns remaining seconds
// Atomic operations
await redis.incr('page:views'); // 1
await redis.incr('page:views'); // 2
await redis.incrby('score', 10); // Increment by 10
await redis.decr('inventory:item123');
// Hashes (objects)
await redis.hset('user:1000', {
name: 'Alice',
email: '[email protected]',
age: 30,
});
await redis.hget('user:1000', 'name'); // 'Alice'
await redis.hgetall('user:1000'); // { name: 'Alice', email: '...', age: '30' }
await redis.hincrby('user:1000', 'loginCount', 1);
// Lists (queues, stacks)
await redis.lpush('queue:jobs', 'job1', 'job2', 'job3'); // Push to left
await redis.rpush('queue:jobs', 'job4'); // Push to right
await redis.lpop('queue:jobs'); // Pop from left (FIFO)
await redis.rpop('queue:jobs'); // Pop from right (LIFO)
await redis.lrange('queue:jobs', 0, -1); // Get all items
// Sets (unique values)
await redis.sadd('tags:post:1', 'javascript', 'nodejs', 'redis');
await redis.smembers('tags:post:1'); // ['javascript', 'nodejs', 'redis']
await redis.sismember('tags:post:1', 'nodejs'); // 1 (true)
await redis.scard('tags:post:1'); // 3 (count)
// Set operations
await redis.sadd('tags:post:2', 'nodejs', 'typescript', 'docker');
await redis.sinter('tags:post:1', 'tags:post:2'); // ['nodejs'] (intersection)
await redis.sunion('tags:post:1', 'tags:post:2'); // All unique tags
await redis.sdiff('tags:post:1', 'tags:post:2'); // ['javascript', 'redis']
// Sorted Sets (leaderboards)
await redis.zadd('leaderboard', 1000, 'player1', 1500, 'player2', 800, 'player3');
await redis.zrange('leaderboard', 0, -1, 'WITHSCORES'); // Ascending
await redis.zrevrange('leaderboard', 0, 9); // Top 10 (descending)
await redis.zincrby('leaderboard', 50, 'player1'); // Add to score
await redis.zrank('leaderboard', 'player1'); // Get rank (0-indexed)
await redis.zscore('leaderboard', 'player1'); // Get score
```
### Advanced Patterns
#### Caching with JSON
```typescript
// Cache helper
class CacheService {
constructor(private redis: Redis) {}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(key);
return data ? JSON.parse(data) : null;
}
async set(key: string, value: any, ttl: number = 3600): Promise<void> {
await this.redis.setex(key, ttl, JSON.stringify(value));
}
async delete(key: string): Promise<void> {
await this.redis.del(key);
}
async getOrSet<T>(
key: string,
factory: () => Promise<T>,
ttl: number = 3600
): Promise<T> {
const cached = await this.get<T>(key);
if (cached) return cached;
const fresh = await factory();
await this.set(key, fresh, ttl);
return fresh;
}
}
// Usage
const cache = new CacheService(redis);
const user = await cache.getOrSet(
'user:1000',
async () => await db.user.findById(1000),
3600
);
```
#### Rate Limiting
```typescript
class RateLimiter {
constructor(private redis: Redis) {}
async checkRateLimit(
key: string,
limit: number,
window: number
): Promise<{ allowed: boolean; remaining: number }> {
const current = await this.redis.incr(key);
if (current === 1) {
await this.redis.expire(key, window);
}
return {
allowed: current <= limit,
remaining: Math.max(0, limit - current),
};
}
}
// Usage: 100 requests per hour per IP
const limiter = new RateLimiter(redis);
const result = await limiter.checkRateLimit(`ratelimit:${ip}`, 100, 3600);
if (!result.allowed) {
return res.status(429).json({ error: 'Too many requests' });
}
```
#### Sliding Window Rate Limiting
```typescript
async function slidingWindowRateLimit(
redis: Redis,
key: string,
limit: number,
window: number
): Promise<boolean> {
const now = Date.now();
const windowStart = now - window * 1000;
// Remove old entries
await redis.zremrangebyscore(key, 0, windowStart);
// Count requests in window
const count = await redis.zcard(key);
if (count < limit) {
// Add current request
await redis.zadd(key, now, `${now}-${Math.random()}`);
await redis.expire(key, window);
return true;
}
return false;
}
```
#### Distributed Locking
```typescript
class RedisLock {
constructor(private redis: Redis) {}
async acquire(
resource: string,
ttl: number = 10000,
retryDelay: number = 50,
retryCount: number = 100
): Promise<string | null> {
const lockKey = `lock:${resource}`;
const lockValue = crypto.randomUUID();
for (let i = 0; i < retryCount; i++) {
const acquired = await this.redis.set(
lockKey,
lockValue,
'PX',
ttl,
'NX'
);
if (acquired === 'OK') {
return lockValue;
}
await new Promise((resolve) => setTimeout(resolve, retryDelay));
}
return null;
}
async release(resource: string, lockValue: string): Promise<boolean> {
const lockKey = `lock:${resource}`;
// Use Lua script to ensure atomicity
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
const result = await this.redis.eval(script, 1, lockKey, lockValue);
return result === 1;
}
async withLock<T>(
resource: string,
fn: () => Promise<T>,
ttl: number = 10000
): Promise<T> {
const lockValue = await this.acquire(resource, ttl);
if (!lockValue) {
throw new Error('Failed to acquire lock');
}
try {
return await fn();
} finally {
await this.release(resource, lockValRelated in data
monte-carlo-push-ingestion
IncludedExpert guide for pushing metadata, lineage, and query logs to Monte Carlo from any data warehouse.
datascripts
php-database
IncludedPHP database mastery - PDO, Eloquent, Doctrine, query optimization, and migrations
datascripts
monte-carlo-validation-notebook
IncludedGenerates SQL validation notebooks for dbt PR changes with before/after comparison queries.
datascripts
monte-carlo-monitor-creation
IncludedGuides creation of Monte Carlo monitors via MCP tools, producing monitors-as-code YAML for CI/CD deployment.
data
monte-carlo-prevent
IncludedSurfaces Monte Carlo data observability context (table health, alerts, lineage, blast radius) before SQL/dbt edits.
data
data-mesh-expert
IncludedExpert-level data mesh architecture, domain-oriented ownership, data products, federated governance, and self-serve platforms
data