Heroku Redis #
Redis 概述 #
Heroku Redis 是 Heroku 提供的托管 Redis 服务,用于缓存、会话存储、消息队列等场景。
特点 #
| 特点 | 说明 |
|---|---|
| 完全托管 | 自动更新、监控 |
| 高可用 | 主从复制 |
| 持久化 | 支持 RDB 和 AOF |
| 安全 | TLS 加密连接 |
| 兼容 | 标准 Redis 协议 |
计划类型 #
| 计划 | 内存 | 连接数 | 价格/月 |
|---|---|---|---|
| Mini | 25MB | 20 | $15 |
| Premium-0 | 1GB | 40 | $30 |
| Premium-1 | 2.5GB | 80 | $60 |
| Premium-2 | 6GB | 160 | $120 |
| Premium-3 | 12GB | 320 | $240 |
| Premium-5 | 50GB | 500 | $500 |
创建 Redis 实例 #
使用 CLI 创建 #
bash
# 创建 Mini Redis
heroku addons:create heroku-redis:mini
# 创建指定计划
heroku addons:create heroku-redis:premium-0
# 指定应用
heroku addons:create heroku-redis:mini --app myapp
查看 Redis 信息 #
bash
# 查看 Redis 信息
heroku redis:info
# 输出示例
# === redis-spherical-12345 (HEROKU_REDIS_MAROON_URL)
# Plan: Mini
# Status: Available
# Connections: 1/20
# Memory: 5MB/25MB
# Persistence: Off
# Eviction Policy: volatile-lru
# Created: 2024-01-15 10:00 UTC
# Version: 7.2.4
Redis URL #
bash
# 查看 Redis URL
heroku config:get REDIS_URL
# 输出格式
# redis://user:password@host:port
# 多个 Redis 实例
heroku config | grep REDIS
连接 Redis #
Node.js 连接 #
javascript
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL, {
tls: {
rejectUnauthorized: false
}
});
// 基本操作
await redis.set('key', 'value');
const value = await redis.get('key');
// 设置过期时间
await redis.set('key', 'value', 'EX', 3600); // 1小时后过期
// 删除键
await redis.del('key');
// 关闭连接
redis.quit();
Python 连接 #
python
import os
import redis
# 连接 Redis
r = redis.from_url(
os.environ.get('REDIS_URL'),
ssl_cert_reqs=None
)
# 基本操作
r.set('key', 'value')
value = r.get('key')
# 设置过期时间
r.setex('key', 3600, 'value') # 1小时后过期
# 删除键
r.delete('key')
Ruby 连接 #
ruby
require 'redis'
# 连接 Redis
redis = Redis.new(url: ENV['REDIS_URL'], ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
# 基本操作
redis.set('key', 'value')
value = redis.get('key')
# 设置过期时间
redis.setex('key', 3600, 'value')
# 删除键
redis.del('key')
缓存使用 #
基本缓存模式 #
javascript
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
async function getCachedData(key, fetchFn, ttl = 3600) {
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
const data = await fetchFn();
await redis.set(key, JSON.stringify(data), 'EX', ttl);
return data;
}
// 使用示例
const user = await getCachedData(
`user:${userId}`,
() => fetchUserFromDB(userId),
300 // 5分钟缓存
);
缓存装饰器 #
javascript
function cacheable(keyPrefix, ttl = 3600) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args) {
const key = `${keyPrefix}:${JSON.stringify(args)}`;
const cached = await redis.get(key);
if (cached) {
return JSON.parse(cached);
}
const result = await originalMethod.apply(this, args);
await redis.set(key, JSON.stringify(result), 'EX', ttl);
return result;
};
return descriptor;
};
}
// 使用示例
class UserService {
@cacheable('user', 300)
async getUser(id) {
return await db.query('SELECT * FROM users WHERE id = $1', [id]);
}
}
缓存失效 #
javascript
// 主动失效
async function updateUser(userId, data) {
await db.query('UPDATE users SET ... WHERE id = $1', [userId]);
await redis.del(`user:${userId}`);
}
// 批量失效
async function invalidateUserCache(userId) {
const keys = await redis.keys(`user:${userId}:*`);
if (keys.length > 0) {
await redis.del(...keys);
}
}
// 模式匹配删除
async function deleteByPattern(pattern) {
let cursor = '0';
do {
const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
if (keys.length > 0) {
await redis.del(...keys);
}
cursor = nextCursor;
} while (cursor !== '0');
}
会话存储 #
Express Session #
javascript
const express = require('express');
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const Redis = require('ioredis');
const app = express();
const redisClient = new Redis(process.env.REDIS_URL);
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true,
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 7 // 7天
}
}));
Flask Session #
python
from flask import Flask
from flask_session import Session
import redis
app = Flask(__name__)
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.from_url(
os.environ.get('REDIS_URL'),
ssl_cert_reqs=None
)
Session(app)
Rails Session #
ruby
# config/initializers/session_store.rb
Rails.application.config.session_store :redis_store,
servers: ENV['REDIS_URL'],
expire_after: 7.days,
key: '_myapp_session',
tls: { verify_mode: OpenSSL::SSL::VERIFY_NONE }
消息队列 #
发布订阅 #
javascript
// 发布者
const publisher = new Redis(process.env.REDIS_URL);
await publisher.publish('notifications', JSON.stringify({
type: 'new_message',
data: { userId: 123, message: 'Hello' }
}));
// 订阅者
const subscriber = new Redis(process.env.REDIS_URL);
subscriber.subscribe('notifications');
subscriber.on('message', (channel, message) => {
const data = JSON.parse(message);
console.log(`Received: ${JSON.stringify(data)}`);
});
任务队列 #
javascript
const { Queue, Worker } = require('bullmq');
// 创建队列
const emailQueue = new Queue('emails', {
connection: new Redis(process.env.REDIS_URL)
});
// 添加任务
await emailQueue.add('send-email', {
to: 'user@example.com',
subject: 'Welcome',
body: 'Hello!'
});
// 处理任务
const worker = new Worker('emails', async job => {
console.log(`Processing job ${job.id}`);
await sendEmail(job.data);
}, {
connection: new Redis(process.env.REDIS_URL)
});
worker.on('completed', job => {
console.log(`Job ${job.id} completed`);
});
worker.on('failed', (job, err) => {
console.error(`Job ${job.id} failed:`, err);
});
延迟任务 #
javascript
// 延迟执行
await emailQueue.add('send-email', data, {
delay: 5 * 60 * 1000 // 5分钟后执行
});
// 定时执行
await emailQueue.add('send-email', data, {
repeat: {
every: 60 * 60 * 1000 // 每小时执行
}
});
高级功能 #
原子操作 #
javascript
// 计数器
const count = await redis.incr('page:views');
// 带过期时间的计数器
await redis.multi()
.incr('api:rate-limit:user:123')
.expire('api:rate-limit:user:123', 60)
.exec();
// 限流
async function rateLimit(key, limit, window) {
const count = await redis.incr(key);
if (count === 1) {
await redis.expire(key, window);
}
return count <= limit;
}
分布式锁 #
javascript
const Redlock = require('redlock');
const redlock = new Redlock([new Redis(process.env.REDIS_URL)]);
async function withLock(resource, ttl, fn) {
const lock = await redlock.acquire([resource], ttl);
try {
return await fn();
} finally {
await lock.release();
}
}
// 使用示例
await withLock('user:123:email', 5000, async () => {
// 执行需要锁保护的操作
await sendEmail();
});
排行榜 #
javascript
// 添加分数
await redis.zadd('leaderboard', 100, 'user1');
await redis.zadd('leaderboard', 200, 'user2');
await redis.zadd('leaderboard', 150, 'user3');
// 获取排名
const rank = await redis.zrevrank('leaderboard', 'user1');
// 获取排行榜
const top10 = await redis.zrevrange('leaderboard', 0, 9, 'WITHSCORES');
Redis 管理 #
访问 Redis CLI #
bash
# 连接 Redis CLI
heroku redis:cli
# 执行命令
heroku redis:cli INFO
heroku redis:cli DBSIZE
heroku redis:cli KEYS "*"
监控 Redis #
bash
# 查看 Redis 信息
heroku redis:info
# 查看连接
heroku redis:connections
# 查看内存使用
heroku redis:cli INFO memory
清空数据 #
bash
# 清空当前数据库
heroku redis:cli FLUSHDB
# 清空所有数据库(谨慎使用)
heroku redis:cli FLUSHALL
性能优化 #
连接池配置 #
javascript
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL, {
tls: { rejectUnauthorized: false },
maxRetriesPerRequest: 3,
enableReadyCheck: true,
enableOfflineQueue: true,
connectionPoolSize: 10
});
批量操作 #
javascript
// 使用 Pipeline
const pipeline = redis.pipeline();
for (let i = 0; i < 100; i++) {
pipeline.set(`key:${i}`, `value:${i}`);
}
await pipeline.exec();
// 使用 Multi(事务)
await redis.multi()
.set('key1', 'value1')
.set('key2', 'value2')
.get('key1')
.exec();
内存优化 #
javascript
// 使用 Hash 代替多个 Key
// 不好:多个 Key
await redis.set('user:1:name', 'John');
await redis.set('user:1:email', 'john@example.com');
// 好:使用 Hash
await redis.hset('user:1', 'name', 'John', 'email', 'john@example.com');
// 使用压缩
const zlib = require('zlib');
const compressed = zlib.deflateSync(JSON.stringify(data));
await redis.set('large-data', compressed.toString('base64'));
故障排查 #
连接问题 #
bash
# 检查 Redis 状态
heroku redis:info
# 检查连接数
heroku redis:cli INFO clients
# 查看日志
heroku logs --tail | grep redis
内存问题 #
bash
# 查看内存使用
heroku redis:cli INFO memory
# 查看大 Key
heroku redis:cli --bigkeys
# 查看键空间
heroku redis:cli INFO keyspace
性能问题 #
bash
# 查看慢查询
heroku redis:cli SLOWLOG GET 10
# 监控命令
heroku redis:cli MONITOR
下一步 #
Redis 掌握后,接下来学习 文件存储 了解如何处理静态文件!
最后更新:2026-03-28