面试题答案
一键面试运用Redis缓存策略减轻MySQL压力及提高性能的方法
- 设置合适的过期时间:
- 策略:根据商品的热度和更新频率设置不同的过期时间。对于热门且不常更新的商品,设置较长的过期时间,如一周;对于更新频繁或相对冷门的商品,设置较短的过期时间,如几小时甚至几分钟。这样既保证了缓存命中率,又能及时更新数据。
- 原因:如果过期时间设置过长,可能导致数据长时间不一致;设置过短,则缓存命中率降低,频繁查询MySQL。
- 缓存穿透处理:
- 策略:
- 布隆过滤器:在Redis中使用布隆过滤器。当查询商品时,先通过布隆过滤器判断商品是否存在。如果布隆过滤器判断不存在,则直接返回,不再查询MySQL。布隆过滤器存在一定的误判率,但可以通过调整参数(如哈希函数个数、位数组大小)来降低误判率。
- 空值缓存:当查询MySQL发现商品不存在时,将空值(如null)缓存到Redis中,并设置较短的过期时间,如几分钟。下次查询同样不存在的商品时,直接从Redis返回空值,避免再次查询MySQL。
- 策略:
具体实现步骤
- 设置过期时间实现:
- 代码示例(以Java为例,使用Jedis客户端):
import redis.clients.jedis.Jedis;
public class RedisCacheExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 假设商品ID为1,商品信息为JSON字符串
String productId = "1";
String productInfo = "{\"name\":\"手机\",\"price\":1999}";
// 设置较长过期时间,如一周(以秒为单位)
int expirationLong = 60 * 60 * 24 * 7;
jedis.setex(productId.getBytes(), expirationLong, productInfo.getBytes());
// 对于更新频繁的商品,假设商品ID为2
String productId2 = "2";
String productInfo2 = "{\"name\":\"耳机\",\"price\":99}";
// 设置较短过期时间,如1小时
int expirationShort = 60 * 60;
jedis.setex(productId2.getBytes(), expirationShort, productInfo2.getBytes());
jedis.close();
}
}
- 缓存穿透处理实现:
- 布隆过滤器实现(以Java为例,使用Google Guava库):
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import redis.clients.jedis.Jedis;
public class BloomFilterExample {
private static final int expectedInsertions = 100000;
private static final double fpp = 0.01;
private static BloomFilter<CharSequence> bloomFilter = BloomFilter.create(Funnels.stringFunnel(), expectedInsertions, fpp);
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
// 初始化布隆过滤器,假设从MySQL加载所有商品ID到布隆过滤器
// 实际应用中可以在系统启动时加载
String[] productIds = {"1", "2", "3"};
for (String id : productIds) {
bloomFilter.put(id);
}
// 查询商品时先通过布隆过滤器判断
String queryProductId = "4";
if (!bloomFilter.mightContain(queryProductId)) {
// 布隆过滤器判断不存在,直接返回
System.out.println("商品不存在,无需查询MySQL");
} else {
// 可能存在,查询Redis或MySQL
byte[] productInfo = jedis.get(queryProductId.getBytes());
if (productInfo != null) {
System.out.println("从Redis获取商品信息: " + new String(productInfo));
} else {
// 查询MySQL,假设这里有查询MySQL的逻辑
// 并将结果缓存到Redis
}
}
jedis.close();
}
}
- 空值缓存实现(以Java为例,使用Jedis客户端):
import redis.clients.jedis.Jedis;
public class NullCacheExample {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String queryProductId = "5";
byte[] productInfo = jedis.get(queryProductId.getBytes());
if (productInfo != null) {
if (productInfo.length == 0) {
System.out.println("商品不存在,从空值缓存返回");
} else {
System.out.println("从Redis获取商品信息: " + new String(productInfo));
}
} else {
// 查询MySQL
// 假设查询结果为空
// 将空值缓存到Redis,设置较短过期时间,如5分钟
int expiration = 60 * 5;
jedis.setex(queryProductId.getBytes(), expiration, new byte[0]);
System.out.println("商品不存在,查询MySQL后缓存空值");
}
jedis.close();
}
}
在实际电商应用中,还需要考虑缓存雪崩(大量缓存同时过期)等问题,可以通过设置随机过期时间等方式来解决。同时,要结合业务场景对缓存策略进行优化。