Nodejs并发访问mongodb出错

Nodejs并发访问mongodb出错

我做了一个测试页面,就是从数据库中取出文本然后显示。自己浏览没问题,但是用ab做并发测试时出现问题(参数是-t 20 -c 50)。遇到的问题的大概意思是“MongoDB数据库已经处于打开状态,不能再用open方法打开”。我分析了一下,可能是在并发访问时,一个连接打开数据库正在查询数据,在这个客户端还没有关闭连接时,另一个连接正好调用open方法打开数据库,所以出现错误。请问这个问题如何解决呢?我用的是,mongo-native驱动。

我也参看了http://cnodejs.org/topic/50c145ed637ffa4155c7eaee 这个帖子,不知道出还有没有其他方法?


5 回复

Node.js 并发访问 MongoDB 出错

我在做一个简单的测试页面,功能是从数据库中取出文本并显示。单用户访问时一切正常,但使用 ab 工具进行并发测试(参数为 -t 20 -c 50)时遇到了问题。错误信息大概是:“MongoDB 数据库已经处于打开状态,不能再用 open 方法打开”。

经过分析,我认为可能是由于并发访问时,一个连接正在查询数据,而此时另一个连接尝试再次打开数据库导致了冲突。我在使用的是 mongo-native 驱动。

解决方案

为了确保在并发情况下每个请求都能正确地访问数据库,我们需要管理好数据库连接。具体来说,应该避免在每次请求时都重新打开数据库连接。我们可以使用连接池来管理数据库连接,这样可以确保每个请求都可以从连接池中获取到可用的连接,而不是每次都创建新的连接。

以下是具体的解决方案示例:

const MongoClient = require('mongodb').MongoClient;

// 使用连接字符串连接到 MongoDB
const uri = "mongodb+srv://<username>:<password>@cluster0.mongodb.net/test?retryWrites=true&w=majority";

// 创建一个 MongoClient 实例
let db;

async function connectToDatabase() {
    if (!db) {
        const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true });
        await client.connect();
        db = client.db("test");
    }
    return db;
}

// 处理 HTTP 请求
const express = require('express');
const app = express();

app.get('/', async (req, res) => {
    const db = await connectToDatabase();
    const collection = db.collection("documents");
    const result = await collection.findOne({ /* 查询条件 */ });
    res.send(result);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});

解释

  1. 连接池管理

    • 我们定义了一个 connectToDatabase 函数,它负责连接到数据库并返回数据库实例。
    • 如果数据库连接已经存在,则直接返回现有的连接;否则,创建一个新的连接。
  2. HTTP 请求处理

    • 在处理 HTTP 请求时,我们通过调用 connectToDatabase 函数来获取数据库连接,而不是每次都重新连接。
    • 这样可以确保即使在高并发情况下,每个请求都能正确地访问数据库,而不会因为重复打开连接而产生错误。

通过这种方式,我们可以有效地管理数据库连接,并确保在并发环境下也能稳定运行。


可以参考 http://cnodejs.org/topic/5044c3cd8bd986103c00c312

推荐mongoskin: https://github.com/kissjs/node-mongoskin p.s. 此版本与mongodb 2.4使用时会发生错误, 可以暂时使用 https://github.com/rkatti/node-mongoskin

谢谢,我来学习一下

我用的mongooose也可以。

你遇到的问题是因为在并发访问 MongoDB 时,多个请求尝试同时打开同一个数据库连接导致的。使用 mongo-native 驱动时,你应该避免重复打开数据库连接。

解决方案

  1. 使用全局连接对象:确保在整个应用中只创建一个数据库连接,并复用它。
  2. 使用连接池mongo-native 驱动本身支持连接池,你可以配置它来管理连接。

示例代码

const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017/test';

let db;

MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, client) => {
    if (err) throw err;
    console.log("Database connected!");
    db = client.db();
});

async function getData() {
    try {
        const collection = db.collection('your_collection_name');
        const result = await collection.find().toArray();
        return result;
    } catch (error) {
        console.error(error);
        throw error;
    }
}

module.exports = { getData };

解释

  • 全局变量 db:在整个应用中共享同一个数据库连接。
  • 异步函数 getData:使用 await 确保查询操作完成后再返回结果。
  • 错误处理:使用 try-catch 捕获并处理可能的错误。

使用连接池

如果你希望更细粒度地控制连接池,可以这样配置:

const options = {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    poolSize: 10, // 设置连接池大小
    autoReconnect: true
};

MongoClient.connect(url, options, (err, client) => {
    if (err) throw err;
    console.log("Database connected!");
    db = client.db();
});

通过以上方法,可以有效地避免在高并发情况下出现的重复打开数据库连接的问题。

回到顶部