[英]NestJS: Raw SQL query execution with mssql. Too Slow. Configuration problems?
Nestjs 中連接到 SQL 服務器數據庫的 APP。 所有查詢都寫在數據庫端,因此它們之間的連接是使用簡單的原始 SQL 和使用 mssql package。
事情是這樣的:當我在 SSMS 中運行時,會在幾毫秒內執行一個非常小的查詢(比如說 <20 條記錄)。 (甚至更大更復雜的查詢或 SP 具有良好的性能)當我在應用程序中運行時,使用本地數據庫連接,查詢開始有一些延遲(讓我們說 1 秒對於相同的查詢)但是當我開始使用 Azure 上的數據庫時,同樣的小查詢需要 3 到 5 秒(對於 20 條記錄
我讀到一些原因可能與參數嗅探有關,但我不認為是這樣。 我猜,每次新查詢到達時,我的后端都會重新啟動數據庫連接。
這是應用程序的邏輯:控制器使用的一個集中式 CRUD 服務。
在 main.ts 是連接:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const logger = new Logger('Bootstrap', { timestamp: true });
const configService = app.get(ConfigService);
//Database configuration
const sqlConfig = {
user:
configService.get('DB_USELOCAL') === 'false'
? configService.get('DB_USERNAME')
: configService.get('DB_USERNAME_LOCAL'),
password:
configService.get('DB_USELOCAL') === 'false'
? configService.get('DB_PASSWORD')
: configService.get('DB_PASSWORD_LOCAL'),
server:
configService.get('DB_USELOCAL') === 'false'
? configService.get('DB_SERVER')
: configService.get('DB_SERVER_LOCAL'),
database:
configService.get('DB_USELOCAL') === 'false'
? configService.get('DB_DATABASE')
: configService.get('DB_DATABASE_LOCAL'),
pool: {
max: 10,
min: 0,
idleTimeoutMillis: 30000,
},
requestTimeout: 180000, //3 minutes to wait for a request to the database.
options: {
//encrypt: false, // for azure
encrypt: configService.get('DB_USELOCAL') === 'false' ? true : false,
trustServerCertificate: false, // change to true for local dev / self-signed certs
},
};
sql.connect(sqlConfig);
logger.log('App connected to database MSSQL');
//CORS: Cross-origin resource sharing (CORS) is a mechanism that allows resources to be requested from another domain.
app.enableCors();
//App running
await app.listen(configService.get('PORT') || 3000);
logger.log(`App running on port ${configService.get('PORT') || 3000}`);
}
bootstrap();
在 CRUD 服務中請求的查詢
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { fxSQLerrorMsg } from './function/SLQerrorMsg.fx';
import * as sql from 'mssql';
import { FxArrayObjectStr } from './function/arrayObjectStr.fx';
import { FxObjectStr } from './function/objectStr.fx';
import { FindBodyDTO } from './findBody.dto';
@Injectable()
export class CrudService {
private logger = new Logger('Crud Service', { timestamp: true });
async find(
sp: string,
DB: string,
body?: FindBodyDTO | null,
query?: Record<string, any> | null,
email?: string,
filter?: string,
): Promise<Record<string, any>[]> {
const method = "'" + 'find' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = body
? "'" + JSON.stringify(body).replace('%20', ' ') + "'"
: null;
const queryParam = FxObjectStr(query);
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
',' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (await sql.query<Record<string, any>[]>(spScript))
.recordset as unknown as Record<string, any>[];
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Find'),
HttpStatus.BAD_REQUEST,
);
}
}
async post(
sp: string,
DB: string,
body: Record<string, any>[],
email?: string,
filter?: string,
): Promise<Record<string, string>> {
const method = "'" + 'post' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = FxArrayObjectStr(body);
const queryParam = null;
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
', ' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (
(await sql.query<string>(spScript)).recordset as any[]
)[0] as Record<string, string>;
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Post'),
HttpStatus.BAD_REQUEST,
);
}
}
async updateOne(
sp: string,
DB: string,
body: Record<string, any>[],
query?: Record<string, any>,
email?: string,
filter?: string,
): Promise<Record<string, string>> {
const method = "'" + 'updateOne' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = FxArrayObjectStr(body);
const queryParam = FxObjectStr(query);
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
', ' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (
(await sql.query<string>(spScript)).recordset as any[]
)[0] as Record<string, string>;
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Update'),
HttpStatus.BAD_REQUEST,
);
}
}
async updateMany(
sp: string,
DB: string,
body: Record<string, any>[],
query?: Record<string, any>,
email?: string,
filter?: string,
): Promise<Record<string, string>> {
const method = "'" + 'updateMany' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = FxArrayObjectStr(body);
const queryParam = FxObjectStr(query);
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
', ' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (
(await sql.query<string>(spScript)).recordset as any[]
)[0] as Record<string, string>;
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Update'),
HttpStatus.BAD_REQUEST,
);
}
}
async deleteOne(
sp: string,
DB: string,
query?: Record<string, any>,
email?: string,
filter?: string,
): Promise<Record<string, string>> {
const method = "'" + 'deleteOne' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = null;
const queryParam = FxObjectStr(query);
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
', ' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (
(await sql.query<string>(spScript)).recordset as any[]
)[0] as Record<string, string>;
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Delete'),
HttpStatus.BAD_REQUEST,
);
}
}
async deleteMany(
sp: string,
DB: string,
body: Record<string, any>[],
query: Record<string, any>,
email?: string,
filter?: string,
): Promise<Record<string, string>> {
const method = "'" + 'deleteMany' + "'";
const storeProcedure =
process.env.SPECIFYDB == 'true'
? 'EXECUTE [' + DB + '].[ml_sp].[' + sp + ']'
: 'EXECUTE [ml_sp].[' + sp + ']';
const bodyParam = FxArrayObjectStr(body);
const queryParam = FxObjectStr(query);
const emailScript = email ? "'" + email + "'" : null;
const filterScript = filter ? "'" + filter + "'" : null;
const spScript =
storeProcedure +
' ' +
method +
', ' +
bodyParam +
', ' +
queryParam +
', ' +
emailScript +
',' +
filterScript;
this.logger.verbose(spScript);
try {
return (
(await sql.query<string>(spScript)).recordset as any[]
)[0] as Record<string, string>;
} catch (error) {
this.logger.error(error);
throw new HttpException(
fxSQLerrorMsg(error.message, 'Delete'),
HttpStatus.BAD_REQUEST,
);
}
}
}
附加信息:我正在測試的查詢(我稱之為非常小的查詢)是:
ALTER VIEW [ml_view].[User2Role] AS
(SELECT [ml_users].[User2Role].[id] as [id],
[User_user_Aux].[email] as [user],
[PortfolioRole_portfoliorole_Aux].[name] as [portfoliorole],
[ml_users].[User2Role].[editiondate] as [editiondate],
[User_editedbyuser_Aux].[email] as [editedbyuser]
FROM [ml_users].[User2Role]
LEFT JOIN [ml_users].[User] as [User_user_Aux] ON [User_user_Aux].[id] = [ml_users].[User2Role].[userid]
LEFT JOIN [ml_setup].[PortfolioRole] as [PortfolioRole_portfoliorole_Aux] ON [PortfolioRole_portfoliorole_Aux].[id] = [ml_users].[User2Role].[portfolioroleid]
LEFT JOIN [ml_users].[User] as [User_editedbyuser_Aux] ON [User_editedbyuser_Aux].[id] = [ml_users].[User2Role].[editedbyuser])
實際上,它是作為視圖存儲的,並通過一個 SP 運行。 但是我們測試了直接執行視圖(Select * from [viewName]),結果是一樣的。
解決方案:問題不在於SQL服務器的NestJS配置,而恰恰在於Azure中分配給數據庫的資源(DTU或vCores)
接下來我總結了我們采取的一些行動,但最后,這是一個愚蠢的錯誤。 但是,猜測對於保留帖子很有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.