TypeORM 性能问题 - 加载具有许多关系的对象
TypeORM Performance problem - Loading object with many relations
TS + Node 12 + TypeORM + MySQL - AWS 2GB 实例 EC2(我认为中等)
问题很简单
对于获取订单 API 请求 - 我们从数据库加载订单 - 每个订单都带有来自其他表的更多相关字段 - 通过连接。类似于:
const galleries: Gallery[] = await relationshipAModel.createQueryBuilder('model')
.leftJoinAndSelect('model.subRelationshipA', 'a')
.leftJoinAndSelect('model.subRelationshipB', 'b')
.whereInIds(firstResultIds)
.getMany();
代码来自:https://github.com/typeorm/typeorm/issues/4499
我们的问题与此处链接的问题完全相同。
NodeJS 进程在加载 5-10 个请求时崩溃 - 每个请求加载 500 个订单 - 加载 25 / 50 / 100 个订单是可以的。
内存分析没有帮助。
打字稿中的订单对象如下所示:
@Entity()
export class Order {
@PrimaryColumn({ type: 'varchar', name: 'id' })
id: string;
@OneToMany(type => OrderItem, orderItem => orderItem.order, {
cascade: true
})
orderItems: OrderItem[];
@ManyToOne(type => ServiceLevel, serviceLevel => serviceLevel.orders, {
cascade: true
})
serviceLevel: ServiceLevel;
@ManyToOne(type => Customer, customer => customer.orders, { cascade: true })
customer: Customer;
@JoinColumn()
@OneToOne(type => Address, { cascade: true, nullable: true })
shippingAddress: Address;
@JoinColumn()
@OneToOne(type => Address, { cascade: true, nullable: true })
billingAddress: Address;
@ManyToMany(type => OrderTag, orderTag => orderTag.orders, {
cascade: true
})
@JoinTable()
orderTags: OrderTag[];
只留下“可疑”的字段
350 个订单的节点堆堆栈:
Nov 25 15:24:58 ip-172-31-38-186 web: {
Nov 25 15:24:58 ip-172-31-38-186 web: page: 0,
Nov 25 15:24:58 ip-172-31-38-186 web: maxResults: 350,
Nov 25 15:24:58 ip-172-31-38-186 web: filters: [ { fieldName: 'orderStatus', operator: 'NOT IN', values: [Array] } ],
Nov 25 15:24:58 ip-172-31-38-186 web: stepFilter: { fieldName: 'orderStatus', operator: 'IN', values: [ 'stepPrint' ] },
Nov 25 15:24:58 ip-172-31-38-186 web: sortFields: [ { fieldName: 'createdDate', value: 'DESC' } ]
Nov 25 15:24:58 ip-172-31-38-186 web: }
Nov 25 15:25:18 ip-172-31-38-186 web: <--- Last few GCs --->
Nov 25 15:25:18 ip-172-31-38-186 web: [20326:0x31ad300] 113205 ms: Mark-sweep 993.9 (999.1) -> 993.7 (997.4) MB, 766.6 / 0.0 ms (+ 127.3 ms in 23 steps since start of marking, biggest step 25.9 ms, walltime since start of marking 969 ms) (average mu = 0.231, current mu = 0.082) allocation [20326:0x31ad300] 114167 ms: Mark-sweep 994.1 (997.4) -> 993.2 (997.1) MB, 881.2 / 0.0 ms (+ 47.2 ms in 7 steps since start of marking, biggest step 26.7 ms, walltime since start of marking 963 ms) (average mu = 0.138, current mu = 0.036) allocation fa
Nov 25 15:25:18 ip-172-31-38-186 web: <--- JS stacktrace --->
Nov 25 15:25:18 ip-172-31-38-186 web: ==== JS stack trace =========================================
Nov 25 15:25:18 ip-172-31-38-186 web: 0: ExitFrame [pc: 0x13555cd]
Nov 25 15:25:18 ip-172-31-38-186 web: Security context: 0x003cbef008d1 <JSObject>
Nov 25 15:25:18 ip-172-31-38-186 web: 1: slice [0x2c5dd3dc3561] [buffer.js:~606] [pc=0xb6e02716b7c](this=0x3b080493db19 <Object map = 0x3047472a6099>,0x3b0942d12c71 <Uint8Array map = 0x3047472a55a9>,44922,45232)
Nov 25 15:25:18 ip-172-31-38-186 web: 2: _typeCast(aka typeCast) [0x20293188c3e9] [/var/app/current/node_modules/mysql/lib/protocol/packets/RowDataPacket.js:~53] [pc=0xb6e027ab692](this=0x338ac1d1b739 <RowDataPacket map =...
Nov 25 15:25:18 ip-172-31-38-186 web: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Nov 25 15:25:18 ip-172-31-38-186 web: 1: 0xa093f0 node::Abort() [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 2: 0xa097fc node::OnFatalError(char const*, char const*) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 3: 0xb842ae v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 4: 0xb84629 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 5: 0xd30fe5 [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 6: 0xd31676 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 7: 0xd3def5 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 8: 0xd3eda5 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 9: 0xd4185c v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [nod
e]
任何帮助将不胜感激!
如果 id
是 order
的 PRIMARY KEY
,那么 DISTINCT
是不必要的。
一个 LIMIT
应该有一个 ORDER BY
.
order
需要
INDEX(shopId, orderBatchId, id)
100 行对应 100 列?这似乎很多。重新考虑您真正需要哪些列以及是否需要 100 行。
是否有 nodejs 或“数据包”或其他网络限制? (因为 100 行,但 500 行。)
5 个唯一键意味着 可以 是一个单独的 table,将其中的 4 个等同于一个。这也意味着可能存在设计错误;我评论了另一个 shipping_addr
和 billing_addr
都为 UNIQUE
的问题。如果我试图向不同地址的两个亲戚寄东西,我就会遇到麻烦。特别是如果其他人正在使用您的系统发送到这些地址中的任何一个。
甚至 3 UNIQUEs
可能是一个错误。我知道 非常 几个有效案例 2 UNIQUEs
;常见的是 'normalization'.
这个构造让我头晕目眩。我需要弄清楚它是否符合您的要求:
FROM a LEFT JOIN b INNER JOIN c
如果这等同于此,那么你没有你想要的:
FROM a LEFT JOIN (b INNER JOIN c)
另一方面,这可能是您想要的:
FROM (a LEFT JOIN b) INNER JOIN c
这会更清楚,特别是因为 c
是一个子查询:
FROM (c JOIN a) LEFT JOIN b
请提供 EXPLAIN SELECT ...
以便我们了解它是如何评估的。
同时,如果你能先得到子查询,我的困惑就可以避免了。而且它可能 有助于提高性能。
LEFT
-- 您使用 LEFT JOIN
是因为其中每一项都可能缺少数据吗?例如,shippingAddress
可能会丢失(而您期望 NULLs
)?我问是因为它会对性能、可读性、优化等产生影响。需要时使用 LEFT
,否则不要。
TS + Node 12 + TypeORM + MySQL - AWS 2GB 实例 EC2(我认为中等)
问题很简单
对于获取订单 API 请求 - 我们从数据库加载订单 - 每个订单都带有来自其他表的更多相关字段 - 通过连接。类似于:
const galleries: Gallery[] = await relationshipAModel.createQueryBuilder('model')
.leftJoinAndSelect('model.subRelationshipA', 'a')
.leftJoinAndSelect('model.subRelationshipB', 'b')
.whereInIds(firstResultIds)
.getMany();
代码来自:https://github.com/typeorm/typeorm/issues/4499
我们的问题与此处链接的问题完全相同。
NodeJS 进程在加载 5-10 个请求时崩溃 - 每个请求加载 500 个订单 - 加载 25 / 50 / 100 个订单是可以的。
内存分析没有帮助。
打字稿中的订单对象如下所示:
@Entity()
export class Order {
@PrimaryColumn({ type: 'varchar', name: 'id' })
id: string;
@OneToMany(type => OrderItem, orderItem => orderItem.order, {
cascade: true
})
orderItems: OrderItem[];
@ManyToOne(type => ServiceLevel, serviceLevel => serviceLevel.orders, {
cascade: true
})
serviceLevel: ServiceLevel;
@ManyToOne(type => Customer, customer => customer.orders, { cascade: true })
customer: Customer;
@JoinColumn()
@OneToOne(type => Address, { cascade: true, nullable: true })
shippingAddress: Address;
@JoinColumn()
@OneToOne(type => Address, { cascade: true, nullable: true })
billingAddress: Address;
@ManyToMany(type => OrderTag, orderTag => orderTag.orders, {
cascade: true
})
@JoinTable()
orderTags: OrderTag[];
只留下“可疑”的字段
350 个订单的节点堆堆栈:
Nov 25 15:24:58 ip-172-31-38-186 web: {
Nov 25 15:24:58 ip-172-31-38-186 web: page: 0,
Nov 25 15:24:58 ip-172-31-38-186 web: maxResults: 350,
Nov 25 15:24:58 ip-172-31-38-186 web: filters: [ { fieldName: 'orderStatus', operator: 'NOT IN', values: [Array] } ],
Nov 25 15:24:58 ip-172-31-38-186 web: stepFilter: { fieldName: 'orderStatus', operator: 'IN', values: [ 'stepPrint' ] },
Nov 25 15:24:58 ip-172-31-38-186 web: sortFields: [ { fieldName: 'createdDate', value: 'DESC' } ]
Nov 25 15:24:58 ip-172-31-38-186 web: }
Nov 25 15:25:18 ip-172-31-38-186 web: <--- Last few GCs --->
Nov 25 15:25:18 ip-172-31-38-186 web: [20326:0x31ad300] 113205 ms: Mark-sweep 993.9 (999.1) -> 993.7 (997.4) MB, 766.6 / 0.0 ms (+ 127.3 ms in 23 steps since start of marking, biggest step 25.9 ms, walltime since start of marking 969 ms) (average mu = 0.231, current mu = 0.082) allocation [20326:0x31ad300] 114167 ms: Mark-sweep 994.1 (997.4) -> 993.2 (997.1) MB, 881.2 / 0.0 ms (+ 47.2 ms in 7 steps since start of marking, biggest step 26.7 ms, walltime since start of marking 963 ms) (average mu = 0.138, current mu = 0.036) allocation fa
Nov 25 15:25:18 ip-172-31-38-186 web: <--- JS stacktrace --->
Nov 25 15:25:18 ip-172-31-38-186 web: ==== JS stack trace =========================================
Nov 25 15:25:18 ip-172-31-38-186 web: 0: ExitFrame [pc: 0x13555cd]
Nov 25 15:25:18 ip-172-31-38-186 web: Security context: 0x003cbef008d1 <JSObject>
Nov 25 15:25:18 ip-172-31-38-186 web: 1: slice [0x2c5dd3dc3561] [buffer.js:~606] [pc=0xb6e02716b7c](this=0x3b080493db19 <Object map = 0x3047472a6099>,0x3b0942d12c71 <Uint8Array map = 0x3047472a55a9>,44922,45232)
Nov 25 15:25:18 ip-172-31-38-186 web: 2: _typeCast(aka typeCast) [0x20293188c3e9] [/var/app/current/node_modules/mysql/lib/protocol/packets/RowDataPacket.js:~53] [pc=0xb6e027ab692](this=0x338ac1d1b739 <RowDataPacket map =...
Nov 25 15:25:18 ip-172-31-38-186 web: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Nov 25 15:25:18 ip-172-31-38-186 web: 1: 0xa093f0 node::Abort() [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 2: 0xa097fc node::OnFatalError(char const*, char const*) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 3: 0xb842ae v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 4: 0xb84629 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 5: 0xd30fe5 [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 6: 0xd31676 v8::internal::Heap::RecomputeLimits(v8::internal::GarbageCollector) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 7: 0xd3def5 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 8: 0xd3eda5 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [node]
Nov 25 15:25:18 ip-172-31-38-186 web: 9: 0xd4185c v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [nod
e]
任何帮助将不胜感激!
如果 id
是 order
的 PRIMARY KEY
,那么 DISTINCT
是不必要的。
一个 LIMIT
应该有一个 ORDER BY
.
order
需要
INDEX(shopId, orderBatchId, id)
100 行对应 100 列?这似乎很多。重新考虑您真正需要哪些列以及是否需要 100 行。
是否有 nodejs 或“数据包”或其他网络限制? (因为 100 行,但 500 行。)
5 个唯一键意味着 可以 是一个单独的 table,将其中的 4 个等同于一个。这也意味着可能存在设计错误;我评论了另一个 shipping_addr
和 billing_addr
都为 UNIQUE
的问题。如果我试图向不同地址的两个亲戚寄东西,我就会遇到麻烦。特别是如果其他人正在使用您的系统发送到这些地址中的任何一个。
甚至 3 UNIQUEs
可能是一个错误。我知道 非常 几个有效案例 2 UNIQUEs
;常见的是 'normalization'.
这个构造让我头晕目眩。我需要弄清楚它是否符合您的要求:
FROM a LEFT JOIN b INNER JOIN c
如果这等同于此,那么你没有你想要的:
FROM a LEFT JOIN (b INNER JOIN c)
另一方面,这可能是您想要的:
FROM (a LEFT JOIN b) INNER JOIN c
这会更清楚,特别是因为 c
是一个子查询:
FROM (c JOIN a) LEFT JOIN b
请提供 EXPLAIN SELECT ...
以便我们了解它是如何评估的。
同时,如果你能先得到子查询,我的困惑就可以避免了。而且它可能 有助于提高性能。
LEFT
-- 您使用 LEFT JOIN
是因为其中每一项都可能缺少数据吗?例如,shippingAddress
可能会丢失(而您期望 NULLs
)?我问是因为它会对性能、可读性、优化等产生影响。需要时使用 LEFT
,否则不要。