Redis 中如何解决缓存穿透问题
缓存穿透通常指的是一个查询数据的请求,但是这个请求所使用的 key 在缓存中找不到数据,于是请求绕过了缓存而直接查询了数据库,这样的请求会对数据库的性能带来很大压力,甚至可能会导致数据库宕机。这种情况,就被称为“缓存穿透”。
解决缓存穿透的问题,尤其是在 Redis 中,是非常重要的。因为 Redis 是一个高性能的缓存数据库,如果无法正确解决缓存穿透,则无法充分发挥 Redis 的优势。
以下是几种解决缓存穿透的方法:
- 对热门 key 实现预热 对于热门的请求,我们可以提前在 Redis 中缓存好对应的数据,然后将预热的 key 加入到一个 list 中,在系统启动时进行预热操作。这样,对于这些热门 key,系统在响应用户请求时,就可以直接从 Redis 中获取数据。
示例代码:
// 预热热门 key let hotKeys = ['key1', 'key2', 'key3']; for (let i = 0; i < hotKeys.length; i++) { let data = getDataFromDb(hotKeys[i]); // 缓存 60 秒 redis.set(hotKeys[i], data, 'EX', 60); }
- 对非法请求进行拦截 非法请求通常是指请求中携带了无效参数或者非法字符。在请求到达后端服务之前,我们可以在缓存层级进行拦截,只允许合法的请求访问。这样,在 Redis 中就不存在无效的 key,所以也就避免了缓存穿透。
示例代码:
// 判断请求中是否合法 function isRequestValid(req) { if (req.params.key === '' || typeof req.params.key === 'undefined') { return false; } // 非法字符检测 let key = req.params.key.replace(/[\s\n\x00-\x1f\x7f-\xff]/g, ''); if (key === '') { return false; } return true; }
// 在缓存层级中拦截非法请求 app.get('/getData', function(req, res) { let key = req.params.key; // 判断是否合法 if (!isRequestValid(req)) { res.send('Invalid request!'); } // 判断 Redis 缓存中是否存在 key redis.get(key, function(err, result) { if (err) { res.send(err); } if (result) { res.send(result); } else { let data = getDataFromDb(key); redis.set(key, data, 'EX', 60); res.send(data); } }); });
- 加入布隆过滤器 布隆过滤器是一种高效的数据结构,用于快速查询某个元素是否存在于集合中。我们可以使用布隆过滤器来过滤掉非法的 key,从而避免对数据库的影响。
示例代码:
const {BloomFilter} = require('bloom-filters'); const filter = BloomFilter.create(keyCount, errorRate);
// 对 key 进行过滤 function filterKeys(keys) { let legalKeys = []; let illegalKeys = []; for (let i = 0; i < keys.length; i++) { if (filter.has(keys[i])) { legalKeys.push(keys[i]); } else { illegalKeys.push(keys[i]); } } return { legalKeys: legalKeys, illegalKeys: illegalKeys } }
// 在 get 方法中过滤 key app.get('/getData', function(req, res) { let key = req.params.key; // 判断 Redis 缓存中是否存在 key redis.get(key, function(err, result) { if (err) { res.send(err); } if (result) { res.send(result); } else { let legal = filter.has(key); // 验证合法性 if (legal) { let data = getDataFromDb(key); redis.set(key, data, 'EX', 60); res.send(data); } else { // 非法 key,直接返回 null res.send(null); } } }); });
结语 解决缓存穿透的问题,是一个全面考虑的问题。通过实现上述的解决方案,可以大大降低缓存穿透的风险。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67ba6b35306f20b3a6923621