🗄️ Database
Redis Caching & Data Store
Last updated: 2025-09-25 02:29:54
Redis In-Memory Data Store
Redis is an in-memory data structure store used as a database, cache, and message broker.
Basic Redis Operations
// Node.js Redis client
const redis = require('redis');
const client = redis.createClient({
host: 'localhost',
port: 6379,
password: 'your-password', // if auth is enabled
db: 0 // database number
});
client.on('connect', () => {
console.log('Connected to Redis');
});
client.on('error', (err) => {
console.error('Redis error:', err);
});
// String operations
async function stringOperations() {
// Set a key-value pair
await client.set('user:1:name', 'John Doe');
// Set with expiration (in seconds)
await client.setex('session:abc123', 3600, JSON.stringify({
userId: 1,
loginTime: Date.now()
}));
// Get a value
const userName = await client.get('user:1:name');
console.log('User name:', userName);
// Increment/decrement counters
await client.incr('page:views');
await client.incrby('user:1:score', 10);
// Check if key exists
const exists = await client.exists('user:1:name');
console.log('Key exists:', exists);
// Delete a key
await client.del('temp:data');
// Set multiple keys at once
await client.mset('key1', 'value1', 'key2', 'value2');
// Get multiple keys
const values = await client.mget('key1', 'key2');
console.log('Multiple values:', values);
}
// Hash operations
async function hashOperations() {
const userKey = 'user:123';
// Set hash fields
await client.hset(userKey, 'name', 'Alice Smith');
await client.hset(userKey, 'email', 'alice@example.com');
await client.hset(userKey, 'age', '30');
// Set multiple hash fields
await client.hmset(userKey, {
'city': 'New York',
'country': 'USA',
'occupation': 'Developer'
});
// Get hash field
const name = await client.hget(userKey, 'name');
console.log('User name:', name);
// Get multiple hash fields
const userInfo = await client.hmget(userKey, 'name', 'email', 'city');
console.log('User info:', userInfo);
// Get all hash fields and values
const allUserData = await client.hgetall(userKey);
console.log('All user data:', allUserData);
// Increment hash field
await client.hincrby(userKey, 'login_count', 1);
// Check if hash field exists
const hasEmail = await client.hexists(userKey, 'email');
console.log('Has email:', hasEmail);
}
// List operations
async function listOperations() {
const listKey = 'messages';
// Push elements to list
await client.lpush(listKey, 'message1', 'message2'); // Left push
await client.rpush(listKey, 'message3', 'message4'); // Right push
// Get list length
const length = await client.llen(listKey);
console.log('List length:', length);
// Get list elements
const messages = await client.lrange(listKey, 0, -1); // Get all
console.log('All messages:', messages);
// Get specific range
const firstTwo = await client.lrange(listKey, 0, 1);
console.log('First two messages:', firstTwo);
// Pop elements
const leftPop = await client.lpop(listKey);
const rightPop = await client.rpop(listKey);
console.log('Popped from left:', leftPop, 'from right:', rightPop);
// Blocking pop (waits for element)
// const blocked = await client.blpop(listKey, 10); // Wait 10 seconds
}
Advanced Data Structures
// Set operations
async function setOperations() {
const setKey1 = 'tags:user:1';
const setKey2 = 'tags:user:2';
// Add members to set
await client.sadd(setKey1, 'javascript', 'react', 'nodejs');
await client.sadd(setKey2, 'python', 'django', 'javascript');
// Get all set members
const tags1 = await client.smembers(setKey1);
console.log('User 1 tags:', tags1);
// Check if member exists
const hasReact = await client.sismember(setKey1, 'react');
console.log('Has react:', hasReact);
// Set operations
const intersection = await client.sinter(setKey1, setKey2); // Common elements
const union = await client.sunion(setKey1, setKey2); // All unique elements
const difference = await client.sdiff(setKey1, setKey2); // Elements in set1 but not set2
console.log('Intersection:', intersection);
console.log('Union:', union);
console.log('Difference:', difference);
// Get random member
const randomTag = await client.srandmember(setKey1);
console.log('Random tag:', randomTag);
// Remove member
await client.srem(setKey1, 'nodejs');
}
// Sorted Set operations
async function sortedSetOperations() {
const leaderboardKey = 'game:leaderboard';
// Add members with scores
await client.zadd(leaderboardKey, 100, 'player1');
await client.zadd(leaderboardKey, 150, 'player2');
await client.zadd(leaderboardKey, 120, 'player3');
await client.zadd(leaderboardKey, 200, 'player4');
// Get leaderboard (highest to lowest)
const topPlayers = await client.zrevrange(leaderboardKey, 0, 2, 'WITHSCORES');
console.log('Top 3 players:', topPlayers);
// Get rank of player (0-based, lowest score = rank 0)
const rank = await client.zrank(leaderboardKey, 'player2');
const revRank = await client.zrevrank(leaderboardKey, 'player2'); // Highest score = rank 0
console.log('Player2 rank:', rank, 'reverse rank:', revRank);
// Get score of player
const score = await client.zscore(leaderboardKey, 'player2');
console.log('Player2 score:', score);
// Increment score
await client.zincrby(leaderboardKey, 25, 'player1');
// Get players by score range
const midRangePlayers = await client.zrangebyscore(leaderboardKey, 100, 150, 'WITHSCORES');
console.log('Players with scores 100-150:', midRangePlayers);
// Count elements in score range
const count = await client.zcount(leaderboardKey, 100, 200);
console.log('Players with scores 100-200:', count);
}
// Pub/Sub messaging
async function pubSubExample() {
// Publisher
const publisher = redis.createClient();
// Subscriber
const subscriber = redis.createClient();
subscriber.on('message', (channel, message) => {
console.log(`Received message from ${channel}: ${message}`);
});
subscriber.on('subscribe', (channel, count) => {
console.log(`Subscribed to ${channel}. Total subscriptions: ${count}`);
});
// Subscribe to channels
await subscriber.subscribe('news', 'updates');
// Publish messages
setTimeout(() => {
publisher.publish('news', 'Breaking news: Redis is awesome!');
publisher.publish('updates', 'New features available');
}, 1000);
// Pattern subscribe
await subscriber.psubscribe('user:*');
setTimeout(() => {
publisher.publish('user:123:notifications', 'You have a new message');
}, 2000);
}
Redis with Express.js
// Express.js caching middleware
const express = require('express');
const redis = require('redis');
const app = express();
const client = redis.createClient();
// Caching middleware
function cache(duration = 300) {
return async (req, res, next) => {
const key = `cache:${req.originalUrl || req.url}`;
try {
const cachedData = await client.get(key);
if (cachedData) {
console.log('Cache hit');
return res.json(JSON.parse(cachedData));
}
console.log('Cache miss');
// Store original res.json function
const originalJson = res.json;
// Override res.json to cache the response
res.json = function(data) {
// Cache the response
client.setex(key, duration, JSON.stringify(data));
// Send the response
return originalJson.call(this, data);
};
next();
} catch (error) {
console.error('Cache error:', error);
next();
}
};
}
// Session management with Redis
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({ client: client }),
secret: 'your-secret-key',
resave: false,
saveUninitialized: false,
cookie: {
secure: false, // Set to true in production with HTTPS
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 // 24 hours
}
}));
// Rate limiting with Redis
function rateLimit(windowMs, maxRequests) {
return async (req, res, next) => {
const key = `rate_limit:${req.ip}`;
const current = await client.incr(key);
if (current === 1) {
await client.expire(key, Math.ceil(windowMs / 1000));
}
if (current > maxRequests) {
return res.status(429).json({
error: 'Too many requests',
resetTime: await client.ttl(key)
});
}
res.set({
'X-RateLimit-Limit': maxRequests,
'X-RateLimit-Remaining': maxRequests - current,
'X-RateLimit-Reset': Date.now() + (await client.ttl(key) * 1000)
});
next();
};
}
// Routes with caching and rate limiting
app.get('/api/users',
rateLimit(60000, 100), // 100 requests per minute
cache(600), // Cache for 10 minutes
async (req, res) => {
// Simulate database query
const users = await getUsersFromDatabase();
res.json(users);
}
);
app.get('/api/user/:id',
rateLimit(60000, 200),
cache(300),
async (req, res) => {
const userId = req.params.id;
const user = await getUserById(userId);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
}
);
// Clear cache endpoint
app.delete('/api/cache/:pattern', async (req, res) => {
const pattern = req.params.pattern;
const keys = await client.keys(`cache:*${pattern}*`);
if (keys.length > 0) {
await client.del(keys);
}
res.json({ message: `Cleared ${keys.length} cache entries` });
});
async function getUsersFromDatabase() {
// Simulate database delay
await new Promise(resolve => setTimeout(resolve, 1000));
return [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
}
async function getUserById(id) {
const users = await getUsersFromDatabase();
return users.find(user => user.id == id);
}