標籤:
在傳統的項目中,我們經常會用到緩衝來最佳化資料庫的讀取,比如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.0var 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
mongodb(mongoose-redis-cache)