阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 全局配置与最佳实践

全局配置与最佳实践

作者:陈川 阅读数:42311人阅读 分类: MongoDB

全局配置与最佳实践

Mongoose 的全局配置允许开发者统一管理连接行为、模型定义和查询逻辑。合理使用这些配置能显著提升代码可维护性,避免重复设置。以下从连接配置、模型选项到插件机制展开说明核心配置项及其应用场景。

连接池与性能调优

连接池配置直接影响数据库操作的吞吐量。默认情况下Mongoose创建5个连接,高并发场景需要调整:

mongoose.connect('mongodb://localhost:27017/mydb', {
  poolSize: 20, // 最大连接数
  socketTimeoutMS: 45000, // socket超时
  connectTimeoutMS: 30000, // 连接超时
  serverSelectionTimeoutMS: 5000 // 服务器选择超时
});

关键参数说明:

  • maxPoolSize:替换旧版poolSize参数,建议设置为应用线程数的1-2倍
  • waitQueueTimeoutMS:连接请求在队列中的最长等待时间
  • heartbeatFrequencyMS:心跳检测间隔,默认10秒

生产环境推荐配合retry机制:

const options = {
  autoIndex: false, // 生产环境禁用自动索引
  retryWrites: true,
  retryReads: true,
  retryAttempts: 3,
  retryDelay: 1000 
};

Schema级别配置

通过Schema定义可以预设模型行为,避免在每个查询中重复声明:

const userSchema = new mongoose.Schema({
  username: String,
  email: String
}, {
  timestamps: true, // 自动添加createdAt/updatedAt
  minimize: false, // 保留空对象
  toJSON: { 
    virtuals: true,
    transform: (doc, ret) => {
      ret.id = ret._id;
      delete ret._id;
      delete ret.__v;
      return ret;
    }
  },
  optimisticConcurrency: true // 启用乐观锁
});

特别有用的配置项:

  • strictQuery: 控制查询时的字段过滤,建议设为true
  • id: 是否将_id映射为id,默认true
  • versionKey: 修改__v字段名或禁用版本控制

全局插件注册

通过全局插件可统一扩展所有模型功能。典型场景如自动设置更新时间戳:

mongoose.plugin((schema) => {
  schema.pre('save', function(next) {
    this.updatedAt = new Date();
    next();
  });
  
  schema.post('find', function(docs) {
    console.log(`查询返回 ${docs.length} 条记录`);
  });
});

实际项目常用插件组合:

  1. mongoose-autopopulate 自动填充引用字段
  2. mongoose-lean-virtuals 为lean查询添加虚拟字段
  3. mongoose-paginate-v2 分页插件
// 分页插件配置示例
mongoose.plugin(require('mongoose-paginate-v2'), {
  limit: 10,
  customLabels: {
    totalDocs: 'itemCount',
    docs: 'itemsList'
  }
});

查询中间件优化

全局查询钩子能实现AOP风格的逻辑复用:

mongoose.set('debug', process.env.NODE_ENV === 'development');

// 所有查询添加基础过滤
mongoose.plugin((schema) => {
  schema.pre(/^find/, function() {
    if (!this.getFilter().isDeleted) {
      this.where({ isDeleted: false });
    }
  });
});

// 自动转换ID查询
mongoose.plugin((schema) => {
  schema.pre('find', function() {
    const { _id } = this.getQuery();
    if (_id && typeof _id === 'string') {
      this.where({ _id: new mongoose.Types.ObjectId(_id) });
    }
  });
});

多租户处理策略

在SaaS应用中,全局配置可简化租户隔离:

// 动态数据库连接
const tenantConnections = {};

function getTenantConnection(tenantId) {
  if (!tenantConnections[tenantId]) {
    tenantConnections[tenantId] = mongoose.createConnection(
      `mongodb://localhost/${tenantId}`,
      { useNewUrlParser: true }
    );
  }
  return tenantConnections[tenantId];
}

// 模型注册中间件
mongoose.plugin((schema) => {
  schema.statics.byTenant = function(tenantId) {
    const conn = getTenantConnection(tenantId);
    return conn.model(this.modelName, this.schema);
  };
});

// 使用示例
const Product = mongoose.model('Product', productSchema);
const tenantAProducts = Product.byTenant('tenantA').find();

性能监控集成

通过全局配置集成监控工具:

const statsDClient = require('hot-shots');

mongoose.plugin((schema) => {
  const metrics = new statsDClient();
  
  schema.post('save', function(doc) {
    metrics.increment('mongoose.save_operations');
  });
  
  schema.post('find', function(docs) {
    metrics.timing('mongoose.query_time', this._mongooseOptions.executionTime);
  });
});

结合APM工具实现更细粒度监控:

const apm = require('elastic-apm-node');

mongoose.set('debug', function(collectionName, methodName, ...methodArgs) {
  const span = apm.startSpan(`MongoDB ${collectionName}.${methodName}`);
  // ...执行逻辑
  span?.end();
});

类型转换与校验

全局配置自定义类型处理:

// 自定义Decimal128转换
mongoose.Schema.Types.Decimal128.set('toJSON', {
  transform: (val) => val ? parseFloat(val.toString()) : null
});

// 全局验证器
mongoose.plugin((schema) => {
  schema.pre('validate', function(next) {
    if (this.email && !/.+@.+\..+/.test(this.email)) {
      this.invalidate('email', 'Invalid email format');
    }
    next();
  });
});

// 枚举类型增强
mongoose.Schema.Types.String.set('validate', {
  validator: function(val) {
    if (this.enumValues && !this.enumValues.includes(val)) {
      return false;
    }
    return true;
  },
  message: props => `${props.value} 不是有效枚举值`
});

多语言支持方案

通过全局插件实现模型多语言:

mongoose.plugin((schema) => {
  const languages = ['en', 'zh', 'ja'];
  
  languages.forEach(lang => {
    schema.add({
      [`name_${lang}`]: String,
      [`description_${lang}`]: String
    });
  });
  
  schema.methods.getLocalized = function(field, lang = 'en') {
    return this[`${field}_${lang}`] || this[field];
  };
});

// 使用示例
const productSchema = new mongoose.Schema({ baseName: String });
const Product = mongoose.model('Product', productSchema);
const p = new Product({ 
  baseName: 'Default',
  name_zh: '默认名称',
  name_ja: 'デフォルト'
});
console.log(p.getLocalized('name', 'zh')); // 输出: 默认名称

缓存策略实现

全局查询缓存示例:

const cache = new Map();

mongoose.plugin((schema) => {
  schema.statics.cachedFind = async function(query, ttl = 60) {
    const key = JSON.stringify({ 
      model: this.modelName, 
      query 
    });
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    const result = await this.find(query);
    cache.set(key, result);
    setTimeout(() => cache.delete(key), ttl * 1000);
    return result;
  };
});

// 使用示例
const User = mongoose.model('User');
const users = await User.cachedFind({ active: true }, 300);

安全防护措施

全局安全相关配置:

// 防止查询注入
mongoose.set('sanitizeFilter', true);
mongoose.set('sanitizeProjection', true);

// 字段级加密
const encryptedFields = require('mongoose-encrypted-field');

mongoose.plugin(encryptedFields, {
  secret: process.env.ENCRYPTION_KEY,
  fields: ['creditCard', 'ssn']
});

// 审计日志
mongoose.plugin((schema) => {
  schema.add({ 
    createdBy: mongoose.Schema.Types.ObjectId,
    updatedBy: mongoose.Schema.Types.ObjectId 
  });
  
  schema.pre('save', function() {
    if (this.isNew) {
      this.createdBy = currentUser;
    }
    this.updatedBy = currentUser;
  });
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌