Redis(80)如何解决Redis的缓存穿透问题?

解决Redis缓存穿透问题的方法有多种,具体的解决方案可以根据实际情况选择。以下是几种常见的解决方法及详细的代码示例,包括缓存空结果、使用布隆过滤器以及参数校验。

1. 缓存空结果

当查询数据库返回空结果时,也将其缓存起来,并设置一个较短的过期时间,比如5分钟。这样即使请求数据不存在,也不需要每次都访问数据库。

示例代码:

java 复制代码
import redis.clients.jedis.Jedis;
import com.fasterxml.jackson.databind.ObjectMapper;

public class CachePenetrationExample {
    private Jedis jedis;
    private static final ObjectMapper objectMapper = new ObjectMapper();

    public CachePenetrationExample(Jedis jedis) {
        this.jedis = jedis;
    }

    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
        try {
            String cacheValue = jedis.get(key);
            if (cacheValue != null) {
                if (cacheValue.equals("null")) {
                    return null;
                }
                return objectMapper.readValue(cacheValue, clazz);
            }

            T data = provider.getData();
            if (data == null) {
                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
            } else {
                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public interface DataProvider<T> {
        T getData();
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        CachePenetrationExample cache = new CachePenetrationExample(jedis);

        String userId = "nonexistent";
        String cacheKey = "user:" + userId;
        int cacheTime = 3600; // 缓存 1 小时

        User user = cache.getCachedData(cacheKey, User.class, () -> {
            // 模拟数据库查询,返回null表示数据不存在
            return null;
        }, cacheTime);

        if (user == null) {
            System.out.println("User not found.");
        } else {
            System.out.println("User: " + user);
        }

        jedis.close();
    }

    static class User {
        private String id;
        private String name;
        private String email;

        // Getters and Setters

        @Override
        public String toString() {
            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
        }
    }
}

2. 布隆过滤器

使用布隆过滤器快速判断数据是否存在。布隆过滤器是一种概率型数据结构,它能以较低的内存消耗判断一个元素是否存在某个集合中。虽然存在误判的可能,但不会漏判。

示例代码:

java 复制代码
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import redis.clients.jedis.Jedis;
import java.nio.charset.Charset;

public class BloomFilterExample {
    private Jedis jedis;
    private BloomFilter<String> bloomFilter;

    public BloomFilterExample(Jedis jedis, int expectedInsertions, double falsePositiveProbability) {
        this.jedis = jedis;
        this.bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, falsePositiveProbability);
        // 初始化布隆过滤器,通常从数据库加载已有数据
        initializeBloomFilter();
    }

    private void initializeBloomFilter() {
        // 模拟加载数据
        bloomFilter.put("existingUser1");
        bloomFilter.put("existingUser2");
        // 真实环境中应从数据库加载
    }

    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
        try {
            if (!bloomFilter.mightContain(key)) {
                return null;
            }

            String cacheValue = jedis.get(key);
            if (cacheValue != null) {
                if (cacheValue.equals("null")) {
                    return null;
                }
                return objectMapper.readValue(cacheValue, clazz);
            }

            T data = provider.getData();
            if (data == null) {
                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
            } else {
                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public interface DataProvider<T> {
        T getData();
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        BloomFilterExample cache = new BloomFilterExample(jedis, 10000, 0.01);

        String userId = "nonexistent"; // 修改这个值来测试存在和不存在的情况
        String cacheKey = "user:" + userId;
        int cacheTime = 3600; // 缓存 1 小时

        User user = cache.getCachedData(cacheKey, User.class, () -> {
            // 模拟数据库查询,返回null表示数据不存在
            return null;
        }, cacheTime);

        if (user == null) {
            System.out.println("User not found.");
        } else {
            System.out.println("User: " + user);
        }

        jedis.close();
    }

    static class User {
        private String id;
        private String name;
        private String email;

        // Getters and Setters

        @Override
        public String toString() {
            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
        }
    }
}

3. 参数校验

对请求参数进行严格校验,过滤掉明显无效的请求。例如,用户ID应为正整数,可以在程序中对用户ID进行校验,过滤掉不符合规则的请求。

示例代码:

java 复制代码
import redis.clients.jedis.Jedis;

public class ParameterValidationExample {
    private Jedis jedis;

    public ParameterValidationExample(Jedis jedis) {
        this.jedis = jedis;
    }

    public <T> T getCachedData(String key, Class<T> clazz, DataProvider<T> provider, int cacheTime) {
        try {
            if (!isValidKey(key)) {
                return null;
            }

            String cacheValue = jedis.get(key);
            if (cacheValue != null) {
                if (cacheValue.equals("null")) {
                    return null;
                }
                return objectMapper.readValue(cacheValue, clazz);
            }

            T data = provider.getData();
            if (data == null) {
                jedis.setex(key, 300, "null"); // 缓存空结果5分钟
            } else {
                jedis.setex(key, cacheTime, objectMapper.writeValueAsString(data));
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private boolean isValidKey(String key) {
        // 仅示例性校验,实际中应视具体情况进行校验
        return key != null && key.matches("^user:\\d+$");
    }

    public interface DataProvider<T> {
        T getData();
    }

    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);
        ParameterValidationExample cache = new ParameterValidationExample(jedis);

        String userId = "123"; // 正确的用户ID
        String cacheKey = "user:" + userId;
        int cacheTime = 3600; // 缓存 1 小时

        User user = cache.getCachedData(cacheKey, User.class, () -> {
            // 模拟数据库查询
            return getUserFromDatabase(userId);
        }, cacheTime);

        if (user == null) {
            System.out.println("User not found.");
        } else {
            System.out.println("User: " + user);
        }

        jedis.close();
    }

    private static User getUserFromDatabase(String userId) {
        // 模拟数据库查询
        User user = new User();
        user.setId(userId);
        user.setName("John Doe");
        user.setEmail("john.doe@example.com");
        return user;
    }

    static class User {
        private String id;
        private String name;
        private String email;

        // Getters and Setters

        @Override
        public String toString() {
            return "User{id='" + id + "', name='" + name + "', email='" + email + "'}";
        }
    }
}

总结

通过以上示例代码,您可以分别使用缓存空结果、布隆过滤器和参数校验的方法来解决Redis的缓存穿透问题。选择合适的方法可以有效地减少对数据库的无效访问,提高系统的性能和稳定性。

相关推荐
fangzhanpeng1688 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心10 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552716 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552717 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝18 天前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u01030552718 天前
Java图像处理实战指南
人工智能·1024程序员节
u01030552719 天前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u01030552721 天前
Java实用类应用精讲
人工智能·1024程序员节
u01030552721 天前
Java I/O核心操作实战
人工智能·1024程序员节
u01030552722 天前
ArrayList操作详解与实战应用
人工智能·1024程序员节