第15集线上Redis内存占用过大问题
|字数总计:4.5k|阅读时长:22分钟|阅读量:
引言
Redis作为高性能的内存数据库,在生产环境中被广泛使用。然而,随着业务量的增长,Redis内存占用过大成为许多开发者面临的棘手问题。内存占用过高不仅会影响系统性能,还可能导致Redis实例崩溃,进而影响整个应用的稳定性。
本文将深入分析Redis内存占用过大的常见原因,提供系统性的诊断方法和优化策略,帮助开发者有效解决线上Redis内存问题。
Redis内存占用过大的常见原因
1. 数据结构使用不当
Redis提供了丰富的数据结构,但不同数据结构的内存效率差异很大。选择不当的数据结构会导致内存浪费。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class RedisDataStructureAnalysis { public void analyzeDataStructureMemoryUsage() { System.out.println("Redis数据结构内存使用分析:"); System.out.println("1. String:每个key-value对约占用96字节基础开销"); System.out.println("2. Hash:适合存储对象,内存效率较高"); System.out.println("3. List:适合队列场景,但随机访问效率低"); System.out.println("4. Set:适合去重场景,内存开销较大"); System.out.println("5. Sorted Set:适合排行榜场景,内存开销最大"); System.out.println("6. Bitmap:适合位运算场景,内存效率极高"); } public void inefficientStringUsage() { for (int i = 1; i <= 100000; i++) { redisTemplate.opsForValue().set("user:" + i + ":name", "用户" + i); redisTemplate.opsForValue().set("user:" + i + ":age", String.valueOf(20 + i % 50)); redisTemplate.opsForValue().set("user:" + i + ":email", "user" + i + "@example.com"); } } public void efficientHashUsage() { for (int i = 1; i <= 100000; i++) { Map<String, Object> userInfo = new HashMap<>(); userInfo.put("name", "用户" + i); userInfo.put("age", String.valueOf(20 + i % 50)); userInfo.put("email", "user" + i + "@example.com"); redisTemplate.opsForHash().putAll("user:" + i, userInfo); } } }
|
2. 过期策略配置不当
Redis的过期策略配置不当会导致大量过期数据堆积,占用内存空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class RedisExpirationStrategy { public void configureExpirationPolicy() { System.out.println("Redis过期策略配置:"); System.out.println("1. noeviction:不删除任何数据,内存满时报错"); System.out.println("2. allkeys-lru:删除最近最少使用的key"); System.out.println("3. allkeys-lfu:删除最少使用的key"); System.out.println("4. volatile-lru:删除设置了过期时间的最近最少使用的key"); System.out.println("5. volatile-lfu:删除设置了过期时间的最少使用的key"); System.out.println("6. allkeys-random:随机删除key"); System.out.println("7. volatile-random:随机删除设置了过期时间的key"); System.out.println("8. volatile-ttl:删除即将过期的key"); } public void setReasonableExpiration() { redisTemplate.opsForValue().set("session:user123", "sessionData", Duration.ofMinutes(30)); redisTemplate.opsForValue().set("captcha:user123", "123456", Duration.ofMinutes(5)); redisTemplate.opsForValue().set("cache:product:123", "productData", Duration.ofHours(1)); } }
|
3. 内存碎片化
Redis内存碎片化是导致内存使用率低下的重要原因。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class RedisMemoryFragmentation { public void analyzeMemoryFragmentation() { System.out.println("Redis内存碎片化原因:"); System.out.println("1. 频繁的写入和删除操作"); System.out.println("2. 不同大小的key-value对"); System.out.println("3. 过期key的清理不及时"); System.out.println("4. Redis版本的内存分配算法"); } public void reduceFragmentation() { System.out.println("减少内存碎片化策略:"); System.out.println("1. 使用jemalloc内存分配器"); System.out.println("2. 定期执行MEMORY PURGE命令"); System.out.println("3. 避免频繁的key删除操作"); System.out.println("4. 使用合适的数据结构"); System.out.println("5. 定期重启Redis实例"); } }
|
Redis内存监控与诊断
1. 基础监控命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;
@Component public class RedisMemoryMonitor { @Autowired private RedisTemplate<String, Object> redisTemplate; public void getMemoryUsage() { Properties info = redisTemplate.getConnectionFactory() .getConnection().info("memory"); System.out.println("Redis内存使用情况:"); System.out.println("已使用内存:" + info.getProperty("used_memory_human")); System.out.println("峰值内存:" + info.getProperty("used_memory_peak_human")); System.out.println("内存使用率:" + info.getProperty("used_memory_percentage")); System.out.println("内存碎片率:" + info.getProperty("mem_fragmentation_ratio")); } public void getDatabaseSize() { Properties info = redisTemplate.getConnectionFactory() .getConnection().info("keyspace"); System.out.println("数据库大小:"); info.forEach((key, value) -> { System.out.println(key + ": " + value); }); } public void analyzeBigKeys() { System.out.println("分析大key的方法:"); System.out.println("1. 使用redis-cli --bigkeys命令"); System.out.println("2. 使用MEMORY USAGE key命令"); System.out.println("3. 使用SCAN命令遍历所有key"); System.out.println("4. 使用第三方工具如redis-rdb-tools"); } }
|
2. 高级监控工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class AdvancedRedisMonitoring { public void setupRedisStreamMonitoring() { System.out.println("Redis Stream监控设置:"); System.out.println("1. 创建监控流:XADD monitoring:memory * used_memory 1024000"); System.out.println("2. 创建消费者组:XGROUP CREATE monitoring:memory monitor_group $ MKSTREAM"); System.out.println("3. 消费监控数据:XREADGROUP GROUP monitor_group consumer1 COUNT 10 STREAMS monitoring:memory >"); } public String getMemoryMonitoringScript() { return """ local info = redis.call('INFO', 'memory') local keyspace = redis.call('INFO', 'keyspace') local stats = redis.call('INFO', 'stats') local result = {} result['timestamp'] = redis.call('TIME')[1] result['used_memory'] = redis.call('MEMORY', 'USAGE', 'total') result['key_count'] = redis.call('DBSIZE') return cjson.encode(result) """; } }
|
内存优化策略
1. 数据结构优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| @Service public class RedisOptimizationService { @Autowired private RedisTemplate<String, Object> redisTemplate; public void optimizeUserStorage() { Map<String, Object> userInfo = new HashMap<>(); userInfo.put("name", "张三"); userInfo.put("age", "25"); userInfo.put("email", "zhangsan@example.com"); redisTemplate.opsForHash().putAll("user:123", userInfo); String compressedUserInfo = compressUserInfo(userInfo); redisTemplate.opsForValue().set("user:123:compressed", compressedUserInfo); } public void optimizeCounterStorage() { redisTemplate.opsForHyperLogLog().add("unique_visitors", "user1", "user2", "user3"); Long count = redisTemplate.opsForHyperLogLog().size("unique_visitors"); System.out.println("独立访客数:" + count); redisTemplate.opsForValue().setBit("user_login:2024-01-15", 123, true); redisTemplate.opsForValue().setBit("user_login:2024-01-15", 456, true); Boolean isLoggedIn = redisTemplate.opsForValue().getBit("user_login:2024-01-15", 123); } public void optimizeLeaderboardStorage() { redisTemplate.opsForZSet().add("leaderboard", "user1", 1000); redisTemplate.opsForZSet().add("leaderboard", "user2", 2000); redisTemplate.opsForZSet().add("leaderboard", "user3", 1500); Set<ZSetOperations.TypedTuple<Object>> top10 = redisTemplate.opsForZSet() .reverseRangeWithScores("leaderboard", 0, 9); top10.forEach(tuple -> { System.out.println("用户:" + tuple.getValue() + ",分数:" + tuple.getScore()); }); } private String compressUserInfo(Map<String, Object> userInfo) { return userInfo.toString(); } }
|
2. 内存配置优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Configuration public class RedisMemoryConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder() .commandTimeout(Duration.ofSeconds(2)) .shutdownTimeout(Duration.ofMillis(100)) .build(); RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(); serverConfig.setHostName("localhost"); serverConfig.setPort(6379); serverConfig.setPassword("password"); return new LettuceConnectionFactory(serverConfig, clientConfig); } @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(objectMapper); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
|
3. 缓存策略优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| @Service public class CacheStrategyOptimization { @Autowired private RedisTemplate<String, Object> redisTemplate; public Object getWithMultiLevelCache(String key) { Object value = getFromLocalCache(key); if (value != null) { return value; } value = redisTemplate.opsForValue().get(key); if (value != null) { putToLocalCache(key, value); return value; } value = getFromDatabase(key); if (value != null) { putToLocalCache(key, value); redisTemplate.opsForValue().set(key, value, Duration.ofMinutes(30)); } return value; } public void warmUpCache() { System.out.println("开始缓存预热..."); List<Product> hotProducts = getHotProductsFromDatabase(); for (Product product : hotProducts) { String key = "product:" + product.getId(); redisTemplate.opsForValue().set(key, product, Duration.ofHours(1)); } List<User> activeUsers = getActiveUsersFromDatabase(); for (User user : activeUsers) { String key = "user:session:" + user.getId(); redisTemplate.opsForValue().set(key, user, Duration.ofMinutes(30)); } System.out.println("缓存预热完成"); } public Object getWithCacheDegradation(String key) { try { Object value = redisTemplate.opsForValue().get(key); if (value != null) { return value; } return getFromDatabase(key); } catch (Exception e) { Object value = getFromLocalCache(key); if (value != null) { return value; } return getFromDatabase(key); } } private Object getFromLocalCache(String key) { return null; } private void putToLocalCache(String key, Object value) { } private Object getFromDatabase(String key) { return null; } private List<Product> getHotProductsFromDatabase() { return new ArrayList<>(); } private List<User> getActiveUsersFromDatabase() { return new ArrayList<>(); } }
|
实际案例分析
案例1:电商平台商品缓存优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| @Service public class EcommerceCacheOptimization { @Autowired private RedisTemplate<String, Object> redisTemplate; public void originalProductStorage() { for (int i = 1; i <= 100000; i++) { redisTemplate.opsForValue().set("product:" + i + ":name", "商品" + i); redisTemplate.opsForValue().set("product:" + i + ":price", String.valueOf(100 + i)); redisTemplate.opsForValue().set("product:" + i + ":description", "商品描述" + i); redisTemplate.opsForValue().set("product:" + i + ":category", "分类" + (i % 10)); } } public void optimizedProductStorage() { for (int i = 1; i <= 100000; i++) { Map<String, Object> productInfo = new HashMap<>(); productInfo.put("name", "商品" + i); productInfo.put("price", String.valueOf(100 + i)); productInfo.put("description", "商品描述" + i); productInfo.put("category", "分类" + (i % 10)); redisTemplate.opsForHash().putAll("product:" + i, productInfo); redisTemplate.expire("product:" + i, Duration.ofHours(1)); } } public void compressedProductStorage() { for (int i = 1; i <= 100000; i++) { Product product = new Product(); product.setId(i); product.setName("商品" + i); product.setPrice(100 + i); product.setDescription("商品描述" + i); product.setCategory("分类" + (i % 10)); String compressedProduct = compressProduct(product); redisTemplate.opsForValue().set("product:compressed:" + i, compressedProduct, Duration.ofHours(1)); } } private String compressProduct(Product product) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gzos = new GZIPOutputStream(baos); ObjectOutputStream oos = new ObjectOutputStream(gzos); oos.writeObject(product); oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); } catch (IOException e) { throw new RuntimeException("压缩失败", e); } } static class Product { private int id; private String name; private int price; private String description; private String category; } }
|
案例2:用户行为分析优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| @Service public class UserBehaviorAnalysisOptimization { @Autowired private RedisTemplate<String, Object> redisTemplate; public void originalUserBehaviorStorage() { for (int userId = 1; userId <= 10000; userId++) { for (int day = 1; day <= 30; day++) { String key = "user:behavior:" + userId + ":day:" + day; List<String> behaviors = generateUserBehaviors(); redisTemplate.opsForValue().set(key, behaviors, Duration.ofDays(7)); } } } public void optimizedUserBehaviorStorage() { for (int userId = 1; userId <= 10000; userId++) { String loginKey = "user:login:" + userId; for (int day = 1; day <= 30; day++) { boolean isLoggedIn = Math.random() > 0.3; redisTemplate.opsForValue().setBit(loginKey, day, isLoggedIn); } String pageViewKey = "user:pageview:" + userId; for (int i = 0; i < 100; i++) { String pageId = "page:" + (int)(Math.random() * 1000); redisTemplate.opsForHyperLogLog().add(pageViewKey, pageId); } } } public void timeSeriesUserBehaviorStorage() { for (int userId = 1; userId <= 1000; userId++) { String key = "user:behavior:ts:" + userId; for (int i = 0; i < 100; i++) { Map<String, Object> behavior = new HashMap<>(); behavior.put("action", "click"); behavior.put("page", "product:" + (int)(Math.random() * 100)); behavior.put("timestamp", System.currentTimeMillis() + i * 1000); redisTemplate.opsForStream().add(key, behavior); } redisTemplate.expire(key, Duration.ofDays(7)); } } private List<String> generateUserBehaviors() { List<String> behaviors = new ArrayList<>(); for (int i = 0; i < 10; i++) { behaviors.add("behavior_" + i); } return behaviors; } }
|
监控和预警系统
1. 实时监控实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| @Component public class RedisMemoryMonitor { @Autowired private RedisTemplate<String, Object> redisTemplate; @Scheduled(fixedRate = 60000) public void monitorMemoryUsage() { try { Properties memoryInfo = redisTemplate.getConnectionFactory() .getConnection().info("memory"); long usedMemory = Long.parseLong(memoryInfo.getProperty("used_memory")); long maxMemory = Long.parseLong(memoryInfo.getProperty("maxmemory")); double memoryUsageRate = (double) usedMemory / maxMemory; if (memoryUsageRate > 0.8) { sendAlert("Redis内存使用率过高:" + String.format("%.2f%%", memoryUsageRate * 100)); } Map<String, Object> memoryData = new HashMap<>(); memoryData.put("timestamp", System.currentTimeMillis()); memoryData.put("used_memory", usedMemory); memoryData.put("max_memory", maxMemory); memoryData.put("usage_rate", memoryUsageRate); redisTemplate.opsForStream().add("memory:monitor", memoryData); } catch (Exception e) { log.error("监控Redis内存使用情况失败", e); } } private void sendAlert(String message) { System.out.println("告警:" + message); } }
|
2. 内存分析工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| @Service public class RedisMemoryAnalyzer { @Autowired private RedisTemplate<String, Object> redisTemplate; public MemoryAnalysisResult analyzeMemoryUsage() { MemoryAnalysisResult result = new MemoryAnalysisResult(); Properties memoryInfo = redisTemplate.getConnectionFactory() .getConnection().info("memory"); result.setUsedMemory(Long.parseLong(memoryInfo.getProperty("used_memory"))); result.setMaxMemory(Long.parseLong(memoryInfo.getProperty("maxmemory"))); result.setFragmentationRatio(Double.parseDouble(memoryInfo.getProperty("mem_fragmentation_ratio"))); result.setBigKeys(analyzeBigKeys()); result.setExpiredKeys(analyzeExpiredKeys()); return result; } private List<BigKeyInfo> analyzeBigKeys() { List<BigKeyInfo> bigKeys = new ArrayList<>(); Cursor<String> cursor = redisTemplate.scan(ScanOptions.scanOptions() .count(100) .build()); while (cursor.hasNext()) { String key = cursor.next(); try { Long memoryUsage = redisTemplate.getConnectionFactory() .getConnection().memoryUsage(key.getBytes()); if (memoryUsage > 1024 * 1024) { BigKeyInfo bigKey = new BigKeyInfo(); bigKey.setKey(key); bigKey.setMemoryUsage(memoryUsage); bigKey.setType(getKeyType(key)); bigKeys.add(bigKey); } } catch (Exception e) { log.warn("分析key内存使用情况失败:" + key, e); } } return bigKeys; } private List<ExpiredKeyInfo> analyzeExpiredKeys() { List<ExpiredKeyInfo> expiredKeys = new ArrayList<>(); Cursor<String> cursor = redisTemplate.scan(ScanOptions.scanOptions() .count(100) .build()); while (cursor.hasNext()) { String key = cursor.next(); Long ttl = redisTemplate.getExpire(key); if (ttl > 0 && ttl < 3600) { ExpiredKeyInfo expiredKey = new ExpiredKeyInfo(); expiredKey.setKey(key); expiredKey.setTtl(ttl); expiredKeys.add(expiredKey); } } return expiredKeys; } private String getKeyType(String key) { return redisTemplate.type(key).code(); } static class MemoryAnalysisResult { private long usedMemory; private long maxMemory; private double fragmentationRatio; private List<BigKeyInfo> bigKeys; private List<ExpiredKeyInfo> expiredKeys; } static class BigKeyInfo { private String key; private long memoryUsage; private String type; } static class ExpiredKeyInfo { private String key; private long ttl; } }
|
预防措施和最佳实践
1. 开发阶段预防
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| @Service public class RedisBestPractices { @Autowired private RedisTemplate<String, Object> redisTemplate; public void setReasonableExpiration() { redisTemplate.opsForValue().set("session:user123", "sessionData", Duration.ofMinutes(30)); redisTemplate.opsForValue().set("captcha:user123", "123456", Duration.ofMinutes(5)); redisTemplate.opsForValue().set("cache:product:123", "productData", Duration.ofHours(1)); redisTemplate.opsForValue().set("temp:data:123", "tempData", Duration.ofDays(1)); } public void useAppropriateDataStructure() { Map<String, Object> userInfo = new HashMap<>(); userInfo.put("name", "张三"); userInfo.put("age", "25"); userInfo.put("email", "zhangsan@example.com"); redisTemplate.opsForHash().putAll("user:123", userInfo); redisTemplate.opsForList().rightPushAll("user:123:orders", "order1", "order2", "order3"); redisTemplate.opsForSet().add("user:123:tags", "vip", "active", "premium"); redisTemplate.opsForZSet().add("leaderboard", "user123", 1000); } public void useBatchOperations() { Map<String, Object> batchData = new HashMap<>(); for (int i = 1; i <= 1000; i++) { batchData.put("key:" + i, "value:" + i); } redisTemplate.opsForValue().multiSet(batchData); List<String> keys = new ArrayList<>(); for (int i = 1; i <= 1000; i++) { keys.add("key:" + i); } List<Object> values = redisTemplate.opsForValue().multiGet(keys); } public void usePipeline() { List<Object> results = redisTemplate.executePipelined( new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { for (int i = 1; i <= 1000; i++) { connection.set(("key:" + i).getBytes(), ("value:" + i).getBytes()); } return null; } } ); } }
|
2. 运维阶段监控
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| @Configuration @EnableScheduling public class RedisMonitoringConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } @Scheduled(fixedRate = 300000) public void monitorMemoryUsage() { } @Scheduled(fixedRate = 60000) public void monitorPerformance() { } }
|
总结
Redis内存占用过大是生产环境中常见的问题,解决这个问题需要从多个方面入手:
- 数据结构优化:选择合适的数据结构,避免内存浪费
- 过期策略配置:合理设置过期时间,及时清理过期数据
- 内存监控:建立完善的监控体系,及时发现内存问题
- 缓存策略:实现多级缓存,提高缓存命中率
- 预防措施:在开发阶段就考虑内存优化,避免问题发生
通过系统性的分析和优化,可以有效解决Redis内存占用过大的问题,提高系统的稳定性和性能。
参考资料
- 《Redis设计与实现》
- 《Redis开发与运维》
- Redis官方文档
- 《高性能MySQL》
- 《Java性能权威指南》