在 Node.js 项目中实现数据库查询以读取和写入 MySQL 副本的正确方法?

Ann*_*hko 3 mysql database-replication node.js

我有一个node.js Express项目,它使用MySQL 5.6数据库和mysql客户端连接到它。项目中没有 ORM,原始查询针对数据库运行。现在,我需要更改代码,将SELECT查询(不在事务内)路由到 MySQL 只读副本。由于我以前从未在任何其他项目上做过类似的事情,所以我想知道这样做的最佳实践是什么(在 Node.js 中)。

我发现该sequelize库具有允许单独指定读取和写入副本的配置选项,然后不同的查询将被路由到不同的数据库,但我没有在客户端库中找到任何执行此操作的选项mysql。我尝试使用sequelize,但似乎对于 RAW 查询不支持此功能。

除此之外,我想知道从架构的角度来看最好的解决方案是什么?目前,我看到的唯一选择是拥有一些具有query方法的数据库包装服务,该服务反过来封装mysql connection.query并根据提供给它的 sql 决定使用哪个数据库连接 - 读取或写入(除非它是事务 - 然后我想一切都应该在同一个数据库中完成)。这是一个相当愚蠢的解决方案,但这是我现在唯一的想法。

有人可以分享一些这方面的经验吗?不仅在 Node.js 项目中 - 欢迎任何架构想法。也许有某种方法可以在请求到达主机之前对其进行代理?感谢您提前的任何答复。

Ann*_*hko 7

因此,最终,我以一种相当简单的方式解决了这个问题:为读取和写入数据库副本创建单独的连接池,并在我需要的地方直接使用它们:

const writePool = mysql.createPool(writeConnectionString);
const readPool = mysql.createPool(readConnectionString);

function getWriteConnection() {
    return writePool.getConnection();
};

async function getReadConnection() {
    const connection = await readPool.getConnection();
    connection.isRead = true;
    const originalQuery = connection.query;
    connection.query = function (...args) {
        if(!isSelectQuery(args[0])) {
            throw new Error('Read connection cannot be used for the commands other than SELECT.');
        }

        return originalQuery.apply(this, args);
    };

    return connection;
};

// checking if query is SELECT
function isSelectQuery(query) {
    return query.replace(/\s+/mi, '').toLowerCase().startsWith('select');
}

// release function
function release(connection) {
    if(connection.isRead) {
        readPool.releaseConnection(connection)
    } else {
        writePool.releaseConnection(connection);
    }
};
Run Code Online (Sandbox Code Playgroud)

然后在代码中:

const connection = await database.getReadConnection();
await connection.query('SELECT * FROM ...');
....
database.release(connection);
Run Code Online (Sandbox Code Playgroud)

这远不是解决问题的最优雅的方法,但它相对较快地以最小的更改和随后的错误解决了我的问题。