在传统的项目中,我们经常会用到缓存来优化数据库的读取,比如java中,我们利用spring的AOP能力,在读写数据库前增加对缓存的操作。
在node与mongodb的项目中也仍然会存在类似问题,本文参考了mongoose-redis-cache这个插件。
https://github.com/conancat/mongoose-redis-cache
该插件还不太完善,但基本的思路是很简单的,初始化一个redis客户端,然后重写mongoose的exec方法,将exec的参数设置为redis的key,将数据库返回的结果设置为对应的value。
每次操作时优先读取redis。
代码如下:
// Generated by CoffeeScript 1.5.0 var mongooseRedisCache, redis, _; redis = require("redis"); _ = require("underscore"); mongooseRedisCache = function(mongoose, options, callback) { var client, host, pass, port, redisOptions; if (options == null) { options = {}; } host = options.host || ""; port = options.port || ""; pass = options.pass || ""; redisOptions = options.options || {}; mongoose.redisClient = client = redis.createClient(port, host, redisOptions); if (pass.length > 0) { client.auth(pass, function(err) { if (callback) { return callback(err); } }); }
//这里做了改动,原来是execFind,在我当前的Mongoose版本下无法使用 mongoose.Query.prototype._exec = mongoose.Query.prototype.exec; mongoose.Query.prototype.exec = function(callback) { var cb, expires, fields, key, model, query, schemaOptions, self; self = this; model = this.model; query = this._conditions; options = this._optionsForExec(model); fields = _.clone(this._fields); schemaOptions = model.schema.options; expires = schemaOptions.expires || 60; if (!schemaOptions.redisCache && options.lean) { return mongoose.Query.prototype._exec.apply(self, arguments); } key = JSON.stringify(query) + JSON.stringify(options) + JSON.stringify(fields); cb = function(err, result) { var docs; if (err) { return callback(err); } if (!result) { return mongoose.Query.prototype._exec.call(self, function(err, docs) { var str; if (err) { return callback(err); } str = JSON.stringify(docs); client.set(key, str); client.expire(key, expires); return callback(null, docs); }); } else { docs = JSON.parse(result); return callback(null, docs); } }; client.get(key, cb); return this; }; }; module.exports = mongooseRedisCache;
写测试用例如下:
var mongoose = require('mongoose'); var async = require('async'); var mongooseRedisCache = require("mongoose-redis-cache"); mongooseRedisCache(mongoose); var Schema = mongoose.Schema; mongoose.connect('mongodb://localhost/cachetest'); var user = new Schema({ username:{ type:String, index:true }, password:String }); user.set('redisCache', true); var userModel = mongoose.model('user', user); var entity = new userModel({ username:'fredric', password:'sinny' }); var users = []; for(var i = 0; i < 1000; i++){ users.push({ username:'fredric' + i, password:'sinny' + i }); } function testCache(){ function datainit(item,cb){ var entity = new userModel(item); entity.save(function(err){ cb(err); }); } async.mapSeries(users, datainit, function(err, results){ console.log('datainit finished'); var timestamp = new Date().valueOf(); var round = []; for(var i = 0; i < 2000; i++){ round.push(i); } //利用缓存 function test(item,cb){ query = userModel.find({'username':'fredric101'}); query.lean(); query.exec(function(err, result){ cb(err); }); } //不利用缓存 function test_nocache(item,cb){ query = userModel.find({}).setOptions({nocache: true}); query.where("username", "fredric101"); query.exec(function(err, result){ cb(err); }); } async.mapSeries(round, test_nocache, function(err, results){ console.log(new Date().valueOf() - timestamp); }); }); } testCache();
测试结果还是比较明显的,在我本地笔记本上(安装redis + mongodb),上述测试用例执行:
1、无缓存、无索引:21501ms
2、无缓存、有索引:1966ms
3、有缓存、有索引:281ms