每个用户一个连接
One connection per user
我知道这个问题已经被问过了,但似乎还有一些事情需要澄清。 :)
数据库的设计是每个用户都有适当的读取文件的权限,所以连接池需要与不同的用户建立连接,这是脱离连接池概念的。由于优化和性能,我需要调用所谓的 "user preparation",其中包括设置会话变量、在缓存中计算和缓存值等,然后执行查询。
目前,我有两个解决方案。在第一个解决方案中,我首先检查是否为用户准备好了一切,然后执行一个或多个查询。如果它没有准备好,那么我需要调用 "user preparation",然后执行一个或多个查询。使用这个解决方案,我失去了很多性能,因为每次我都必须进行检查,所以我决定使用另一个解决方案。
第二种解决方案包括 "database pool",其中每个池用于一个用户。仅在第一次连接时 useCount === 0(我不使用 {direct: true})我调用 "user preparation"(它是设置一些会话变量并准备缓存的存储过程)然后执行 sql查询。
我已经在 initOptions 参数中的连接事件中完成了用户准备,以初始化 pgPromise。我使用了 pg-promise-demo,所以我不需要解释其余的代码。
使用数据库池包装器进行 pgp 初始化的代码如下所示:
import * as promise from "bluebird";
import pgPromise from "pg-promise";
import { IDatabase, IMain, IOptions } from "pg-promise";
import { IExtensions, ProductsRepository, UsersRepository, Session, getUserFromJWT } from "../db/repos";
import { dbConfig } from "../server/config";
// pg-promise initialization options:
export const initOptions: IOptions<IExtensions> = {
promiseLib: promise,
async connect(client: any, dc: any, useCount: number) {
if (useCount === 0) {
try {
await client.query(pgp.as.format("select prepareUser()", [getUserFromJWT(session.JWT)]));
} catch(error) {
console.error(error);
}
}
},
extend(obj: IExtensions, dc: any) {
obj.users = new UsersRepository(obj);
obj.products = new ProductsRepository(obj);
}
};
type DB = IDatabase<IExtensions>&IExtensions;
const pgp: IMain = pgPromise(initOptions);
class DBPool {
private pool = new Map();
public get = (ct: any): DB => {
const checkConfig = {...dbConfig, ...ct};
const {host, port, database, user} = checkConfig;
const dbKey = JSON.stringify({host, port, database, user})
let db: DB = this.pool.get(dbKey) as DB;
if (!db) {
// const pgp: IMain = pgPromise(initOptions);
db = pgp(checkConfig) as DB;
this.pool.set(dbKey, db);
}
return db;
}
}
export const dbPool = new DBPool();
import diagnostics = require("./diagnostics");
diagnostics.init(initOptions);
网页 api 看起来像:
GET("/api/getuser/:id", (req: Request) => {
const user = getUserFromJWT(session.JWT);
const db = dbPool.get({ user });
return db.users.findById(req.params.id);
});
我对源代码是否正确实例化 pgp 或应该在 get 方法内的 if 块中实例化感兴趣(该行已注释)?
我已经看到 pg-promise 使用从 dbPool.js 导出的 DatabasePool 单例,这类似于我的 DBPool class,但目的是给出“警告:为创建重复的数据库对象相同的连接”。是否可以使用 DatabasePool 单例而不是我的 dbPool 单例?
在我看来,dbContext(pgp 初始化中的第二个参数)可以解决我的问题,但前提是它可以作为函数转发,而不是作为值或对象转发。我是不是错了,或者在访问数据库对象时 dbContext 可以是动态的?
请问有没有第三种(更好的)方案?或者任何其他建议。
如果您对此警告感到困扰:
WARNING: Creating a duplicate database object for the same connection
但您的意图是为每个用户维护一个单独的池,您可以通过为连接提供任何唯一参数来表明这一点。例如,您可以包括自定义 属性 和用户名:
const cn = {
database: 'my-db',
port: 12345,
user: 'my-login-user',
password: 'my-login-password'
....
my_dynamic_user: 'john-doe'
}
这足以让图书馆看到您的连接中有一些独特的东西,它与其他连接不匹配,因此它不会产生那个警告。
这也适用于连接字符串。
请注意,只有当连接总数远远超过用户数时,您尝试实现的目标才能正常工作。例如,如果您可以使用最多 100 个连接,最多 10 个用户。然后你可以分配 10 个池,每个池中最多有 10 个连接。否则,系统的可扩展性将受到影响,因为连接总数是非常有限的资源,您通常永远不会超过 100 个连接,因为它会在 CPU 运行 如此多的物理连接上造成过载同时。这就是共享单个连接池扩展性更好的原因。
我知道这个问题已经被问过了,但似乎还有一些事情需要澄清。 :)
数据库的设计是每个用户都有适当的读取文件的权限,所以连接池需要与不同的用户建立连接,这是脱离连接池概念的。由于优化和性能,我需要调用所谓的 "user preparation",其中包括设置会话变量、在缓存中计算和缓存值等,然后执行查询。
目前,我有两个解决方案。在第一个解决方案中,我首先检查是否为用户准备好了一切,然后执行一个或多个查询。如果它没有准备好,那么我需要调用 "user preparation",然后执行一个或多个查询。使用这个解决方案,我失去了很多性能,因为每次我都必须进行检查,所以我决定使用另一个解决方案。
第二种解决方案包括 "database pool",其中每个池用于一个用户。仅在第一次连接时 useCount === 0(我不使用 {direct: true})我调用 "user preparation"(它是设置一些会话变量并准备缓存的存储过程)然后执行 sql查询。
我已经在 initOptions 参数中的连接事件中完成了用户准备,以初始化 pgPromise。我使用了 pg-promise-demo,所以我不需要解释其余的代码。
使用数据库池包装器进行 pgp 初始化的代码如下所示:
import * as promise from "bluebird";
import pgPromise from "pg-promise";
import { IDatabase, IMain, IOptions } from "pg-promise";
import { IExtensions, ProductsRepository, UsersRepository, Session, getUserFromJWT } from "../db/repos";
import { dbConfig } from "../server/config";
// pg-promise initialization options:
export const initOptions: IOptions<IExtensions> = {
promiseLib: promise,
async connect(client: any, dc: any, useCount: number) {
if (useCount === 0) {
try {
await client.query(pgp.as.format("select prepareUser()", [getUserFromJWT(session.JWT)]));
} catch(error) {
console.error(error);
}
}
},
extend(obj: IExtensions, dc: any) {
obj.users = new UsersRepository(obj);
obj.products = new ProductsRepository(obj);
}
};
type DB = IDatabase<IExtensions>&IExtensions;
const pgp: IMain = pgPromise(initOptions);
class DBPool {
private pool = new Map();
public get = (ct: any): DB => {
const checkConfig = {...dbConfig, ...ct};
const {host, port, database, user} = checkConfig;
const dbKey = JSON.stringify({host, port, database, user})
let db: DB = this.pool.get(dbKey) as DB;
if (!db) {
// const pgp: IMain = pgPromise(initOptions);
db = pgp(checkConfig) as DB;
this.pool.set(dbKey, db);
}
return db;
}
}
export const dbPool = new DBPool();
import diagnostics = require("./diagnostics");
diagnostics.init(initOptions);
网页 api 看起来像:
GET("/api/getuser/:id", (req: Request) => {
const user = getUserFromJWT(session.JWT);
const db = dbPool.get({ user });
return db.users.findById(req.params.id);
});
我对源代码是否正确实例化 pgp 或应该在 get 方法内的 if 块中实例化感兴趣(该行已注释)?
我已经看到 pg-promise 使用从 dbPool.js 导出的 DatabasePool 单例,这类似于我的 DBPool class,但目的是给出“警告:为创建重复的数据库对象相同的连接”。是否可以使用 DatabasePool 单例而不是我的 dbPool 单例?
在我看来,dbContext(pgp 初始化中的第二个参数)可以解决我的问题,但前提是它可以作为函数转发,而不是作为值或对象转发。我是不是错了,或者在访问数据库对象时 dbContext 可以是动态的?
请问有没有第三种(更好的)方案?或者任何其他建议。
如果您对此警告感到困扰:
WARNING: Creating a duplicate database object for the same connection
但您的意图是为每个用户维护一个单独的池,您可以通过为连接提供任何唯一参数来表明这一点。例如,您可以包括自定义 属性 和用户名:
const cn = {
database: 'my-db',
port: 12345,
user: 'my-login-user',
password: 'my-login-password'
....
my_dynamic_user: 'john-doe'
}
这足以让图书馆看到您的连接中有一些独特的东西,它与其他连接不匹配,因此它不会产生那个警告。
这也适用于连接字符串。
请注意,只有当连接总数远远超过用户数时,您尝试实现的目标才能正常工作。例如,如果您可以使用最多 100 个连接,最多 10 个用户。然后你可以分配 10 个池,每个池中最多有 10 个连接。否则,系统的可扩展性将受到影响,因为连接总数是非常有限的资源,您通常永远不会超过 100 个连接,因为它会在 CPU 运行 如此多的物理连接上造成过载同时。这就是共享单个连接池扩展性更好的原因。