redis-best-practices
mindrally/skills
Redis 开发最佳实践:缓存、数据结构及高性能键值操作
...展开全部关于redis-best-practices
“Redis 最佳实践”技能课程为有效使用 Redis 作为内存数据存储提供了全面的指导。课程涵盖了缓存、会话存储、实时分析和消息队列的最佳实践,同时帮助开发者避免常见的陷阱和性能问题。 本技能通过传授正确的数据结构选择、键命名规范以及确保可扩展性和可维护性的架构模式,帮助开发者克服充分发挥 Redis 潜力的挑战。
本技能重点介绍 Redis 的五种核心数据结构——字符串、哈希、列表、集合和有序集合——以及用于事件处理的新功能“流”。针对每种数据结构,本技能提供了实用的代码示例,演示了何时以及如何使用原子操作、批处理命令和阻塞操作。 主要内容包括:实现高效的缓存旁路(cache-aside)模式、使用有序集合设计排行榜、利用列表构建消息队列,以及通过消费者组处理分布式事件。本课程的指导重点在于内存效率、合理的过期策略以及一致的命名规范,这些措施能使 Redis 应用程序更易于调试和扩展。
本技能课程非常适合从事需要快速数据访问的高性能应用程序开发的后端开发人员、设计缓存层的 DevOps 工程师,以及构建实时系统的架构师。 无论您是实现会话管理、构建速率限制器、创建活动信息流,还是设计分布式任务队列,本技能课程都提供了经过实战检验的模式和具体示例,可直接应用于生产系统。
常见问题
排行榜应该使用哪种数据结构?
请使用有序集合(ZADD、ZREVRANGE),其中分数代表玩家排名。有序集合会自动维护顺序,并支持高效的排名查询和基于分数的范围操作。
如何实现正确的缓存过期机制?
使用 SET 命令配合 EX 参数设置过期时间,或使用 SETEX 命令。根据数据变化频率选择 TTL 值——频繁变化的数据应设置较短的 TTL(秒到分钟),静态内容则应设置较长的 TTL(小时到天)。
在什么情况下应使用哈希代替多个字符串键?
存储具有多个字段的对象时,应使用哈希。与为每个字段创建单独的字符串键相比,哈希更节省内存,并且允许进行部分更新而无需检索整个对象。
队列中 RPOP 和 BRPOP 有什么区别?
RPOP 会立即返回一个值,如果列表为空则返回 null。BRPOP 是一种阻塞操作,它会等待最长至指定超时时间,直到项目可用,因此非常适合需要高效等待新任务的工作进程。
如何使用 Redis Streams 处理分布式处理?
使用 XGROUP CREATE 和 XREADGROUP 创建消费者组。多个消费者可以并行处理消息,Redis 会跟踪每个消费者已接收的消息。使用 XACK 确认处理成功。
Redis Best Practices
Core Principles
- Use Redis for caching, session storage, real-time analytics, and message queuing
- Choose appropriate data structures for your use case
- Implement proper key naming conventions and expiration policies
- Design for high availability and persistence requirements
- Monitor memory usage and optimize for performance
Key Naming Conventions
- Use colons as namespace separators
- Include object type and identifier in key names
- Keep keys short but descriptive
- Use consistent naming patterns across your application
# Good key naming examplesuser:1234:profileuser:1234:sessionsorder:5678:itemscache:api:products:listqueue:email:pendingsession:abc123def456rate_limit:api:user:1234Data Structures
Strings
- Use for simple key-value storage, counters, and caching
- Consider using MGET/MSET for batch operations
# Simple cachingSET cache:user:1234 '{"name":"John","email":"[email protected]"}' EX 3600# CountersINCR stats:pageviews:homepageINCRBY stats:downloads:file123 5# Atomic operationsSETNX lock:resource:456 "owner:abc" EX 30Hashes
- Use for objects with multiple fields
- More memory-efficient than multiple string keys
- Supports partial updates
# Store user profileHSET user:1234 name "John Doe" email "[email protected]" created_at "2024-01-15"# Get specific fieldsHGET user:1234 emailHMGET user:1234 name email# Increment numeric fieldsHINCRBY user:1234 login_count 1# Get all fieldsHGETALL user:1234Lists
- Use for queues, recent items, and activity feeds
- Consider blocking operations for queue consumers
# Message queueLPUSH queue:emails '{"to":"[email protected]","subject":"Welcome"}'RPOP queue:emails# Blocking pop for workersBRPOP queue:emails 30# Recent activity (keep last 100)LPUSH user:1234:activity "viewed product 567"LTRIM user:1234:activity 0 99# Get recent itemsLRANGE user:1234:activity 0 9Sets
- Use for unique collections, tags, and relationships
- Supports set operations (union, intersection, difference)
# User tags/interestsSADD user:1234:interests "technology" "music" "travel"# Check membershipSISMEMBER user:1234:interests "music"# Find common interestsSINTER user:1234:interests user:5678:interests# Online users trackingSADD online:users "user:1234"SREM online:users "user:1234"SMEMBERS online:usersSorted Sets
- Use for leaderboards, priority queues, and time-series data
- Elements sorted by score
# LeaderboardZADD leaderboard:game1 1500 "player:123" 2000 "player:456" 1800 "player:789"# Get top 10ZREVRANGE leaderboard:game1 0 9 WITHSCORES# Get player rankZREVRANK leaderboard:game1 "player:123"# Time-based data (score = timestamp)ZADD events:user:1234 1705329600 "login" 1705330000 "purchase"# Get events in time rangeZRANGEBYSCORE events:user:1234 1705329600 1705333200Streams
- Use for event streaming and log data
- Supports consumer groups for distributed processing
# Add events to streamXADD events:orders * customer_id 1234 product_id 567 amount 99.99# Read from streamXREAD COUNT 10 STREAMS events:orders 0# Consumer groupsXGROUP CREATE events:orders order-processors $ MKSTREAMXREADGROUP GROUP order-processors worker1 COUNT 10 STREAMS events:orders ># Acknowledge processed messagesXACK events:orders order-processors 1234567890-0Caching Patterns
Cache-Aside Pattern
# Pseudo-code for cache-asidedef get_user(user_id): # Try cache first cached = redis.get(f"cache:user:{user_id}") if cached: return json.loads(cached) # Cache miss - fetch from database user = database.get_user(user_id) # Store in cache with expiration redis.setex(f"cache:user:{user_id}", 3600, json.dumps(user)) return user
Write-Through Pattern
def update_user(user_id, data): # Update database database.update_user(user_id, data) # Update cache redis.setex(f"cache:user:{user_id}", 3600, json.dumps(data))
Cache Invalidation
# Delete specific cacheDEL cache:user:1234# Delete by pattern (use with caution in production)# Use SCAN instead of KEYS for large datasetsSCAN 0 MATCH cache:user:* COUNT 100# Tag-based invalidation using setsSADD cache:tags:user:1234 "cache:user:1234:profile" "cache:user:1234:orders"# Invalidate all related cachesSMEMBERS cache:tags:user:1234# Then delete each keyExpiration and Memory Management
TTL Best Practices
- Always set TTL on cache keys
- Use jitter to prevent thundering herd
- Consider sliding expiration for session data
# Set with expirationSET cache:data:123 "value" EX 3600# Set expiration on existing keyEXPIRE cache:data:123 3600# Check TTLTTL cache:data:123# Persist key (remove expiration)PERSIST cache:data:123Memory Management
# Check memory usageINFO memory# Get key memory usageMEMORY USAGE cache:large:object# Configure max memory policyCONFIG SET maxmemory 2gbCONFIG SET maxmemory-policy allkeys-lruTransactions and Atomicity
MULTI/EXEC Transactions
# Transaction blockMULTIINCR stats:viewsLPUSH recent:views "page:123"EXEC# Watch for optimistic lockingWATCH user:1234:balancebalance = GET user:1234:balanceMULTISET user:1234:balance (balance - 100)EXECLua Scripts
- Use for complex atomic operations
- Scripts execute atomically
-- Rate limiting scriptlocal key = KEYS[1]local limit = tonumber(ARGV[1])local window = tonumber(ARGV[2])local current = tonumber(redis.call('GET', key) or '0')if current >= limit then return 0endredis.call('INCR', key)if current == 0 then redis.call('EXPIRE', key, window)endreturn 1
# Execute Lua scriptEVAL "return redis.call('GET', KEYS[1])" 1 mykeyPub/Sub and Messaging
# PublisherPUBLISH channel:notifications '{"type":"alert","message":"New order"}'# SubscriberSUBSCRIBE channel:notifications# Pattern subscriptionPSUBSCRIBE channel:*High Availability
Replication
- Use replicas for read scaling
- Configure proper persistence on master
# On replicaREPLICAOF master_host 6379# Check replication statusINFO replicationRedis Sentinel
- Use for automatic failover
- Deploy at least 3 Sentinel instances
Redis Cluster
- Use for horizontal scaling
- Data automatically sharded across nodes
- Use hash tags for related keys
# Hash tags ensure keys go to same slotSET {user:1234}:profile "data"SET {user:1234}:settings "data"Persistence
RDB Snapshots
# Manual snapshotBGSAVE# Configure automatic snapshotsCONFIG SET save "900 1 300 10 60 10000"AOF (Append-Only File)
# Enable AOFCONFIG SET appendonly yesCONFIG SET appendfsync everysec# Rewrite AOFBGREWRITEAOFSecurity
- Require authentication
- Use TLS for connections
- Bind to specific interfaces
- Disable dangerous commands
# Set passwordCONFIG SET requirepass "your_strong_password"# AuthenticateAUTH your_strong_password# Rename dangerous commands (in redis.conf)rename-command FLUSHALL ""rename-command FLUSHDB ""rename-command KEYS ""Monitoring
# Server infoINFO# Memory statsINFO memory# Client connectionsCLIENT LIST# Slow logSLOWLOG GET 10# Monitor commands (debug only)MONITOR# Key count per databaseINFO keyspaceConnection Management
- Use connection pooling
- Set appropriate timeouts
- Handle reconnection gracefully
# Python example with connection poolimport redispool = redis.ConnectionPool( host='localhost', port=6379, max_connections=50, socket_timeout=5, socket_connect_timeout=5)redis_client = redis.Redis(connection_pool=pool)
Performance Tips
- Use pipelining for batch operations
- Avoid large keys (>100KB values)
- Use SCAN instead of KEYS in production
- Monitor and optimize memory usage
- Consider using RedisJSON for complex JSON operations
# Pipeline example (pseudo-code)pipe = redis.pipeline()pipe.get("key1")pipe.get("key2")pipe.set("key3", "value")results = pipe.execute() 




首页
