求助:.NET Core WebAPI 框架自动生成的 Controller 中的 get 全部对象的方法执行太慢

2021-11-16 10:48:45 +08:00
 libasten
框架自动生成的代码:

```
[HttpGet]
public async Task<ActionResult<IEnumerable<DocItem>>> GetDocItem()
{
return await _context.DocItem.ToListAsync();
}
```

数据库中 4000 条数据,执行这个 api 卡顿 2 分钟都没有反应。
不要后台分页的建议,因为需求是想一次性把这 4000 条都返回给前台。
4297 次点击
所在节点    .NET
77 条回复
liuhan907
2021-11-17 16:29:26 +08:00
@Buges 调度和 GC 完全是两回事,外加这和性能压根没关系。你是完全没了解过 actor 相关的东西么,要是这样的话你去看看 dotnet 组的 Orleans 项目,看看他们的编程模型如果不能自定义调度器的话,要怎么优雅的实现。
leoskey
2021-11-17 17:39:50 +08:00
@Buges await 不会阻塞线程哈,线程在等待 task 完成时候,会去执行其它 task
Buges
2021-11-17 19:11:59 +08:00
@liuhan907 当然,我只是说既然接受了 GC 更容易接受其他具有一定 overhead 但降低复杂度的方式。
orleans 是分布式的东西么,那就理解了,分布式有什么需求我都不奇怪。erlang/elixir 就是 green thread 和 actor model ,需不需要自定义调度器我不清楚,不过显然与 async/await 没啥关系。

@leoskey await 会把函数挂起控制流还给 runtime ,当然不会阻塞。我说的是调用同步操作,如 Thread.sleep 。
liuhan907
2021-11-17 19:24:56 +08:00
@Buges async/await 只是一个叫法,准确的说是需要能够自定义执行流的暂停和恢复。但是目前主流语言里能做到这点的基本都叫 async/await 所以说强依赖也当然没错。另外这个需求和分布式其实关系不大,单纯的只是和编程模型有关系。还有我想说的是,GC 不代表就一定要有开销但是降低复杂度。你看看最近微软对 dotnet 做的很多更新,显然是相反的操作,包括 Span<T> 、Memory<T> 等等这类东西。
Lemeng
2021-11-17 19:41:30 +08:00
哎呀。头疼
Buges
2021-11-17 19:48:42 +08:00
@liuhan907 async/await 是显式标明 suspend point 的一种方式,相对应的是隐式插入 suspend point 。你要具体控制调度只要 spawn 的时候能提供参数就好了,不依赖显式或者隐式。

我觉得语言设计还是要尽量专注,不应该追求满足所有需求。开一些 unsafe 的后门倒也没啥,有需要可以用,不用时也不会有影响。但整体设计上有开销时同时导致不必要的复杂性,就不算良好的设计了。
liuhan907
2021-11-17 20:13:57 +08:00
@Buges 如果隐式插桩你还要控制调度,自定义调度实现起来会比显示调度更加麻烦,而且隐式调度还会有难以区分同步和异步调用的问题。
Buges
2021-11-17 20:28:19 +08:00
@liuhan907 插桩(构造 task )只是插桩,和调度(执行 task )没有关系的啊。
关于区分,你可以隐式 suspend(await),但显式 async ,kotlin 就是这样子。go 则是只能写“同步”的代码。
liuhan907
2021-11-17 20:38:00 +08:00
@Buges 构造 task 和调度 task 当然没关系,但是隐式也就意味着你不会显示把 Task 当返回值写上,这样调用方是不能区分这个方法到底是想让你异步调用还是同步调用。kt 那个异步不管是用还是自定义调度,都比 C#麻烦的多,是个很好的例子。
Buges
2021-11-17 21:30:58 +08:00
@liuhan907 同步还是异步是由调用方决定的,方法本身只要实现好自己的逻辑,不用关心自己被如何调用啊。
kotlin 的方式更直白,suspend 表明该函数内部存在 suspend point ,要同步调用那就直接调用,要异步调用那就 async 包一层再调用。从另一个角度来说,可以看作是 async 每次被调用时自动添加了 await ,平时 async 紧跟 await 的是最常见的,这样反而减少了很多输入。scope 的存在也让细粒度的控制(包括调度、取消等)变得更加容易。zig 也采用了类似的方式,同时创造性地解决了 colored function 问题( async 在同步环境中自动变成 noop )。
像 go 那样干脆把异步调用砍掉,只能写“同步”代码,也不失为一种不错的方式,在其目标领域表现良好。
liuhan907
2021-11-17 22:29:27 +08:00
@Buges kt 那种模式和 await 没有什么区别,我反而觉得变得麻烦而且不直观。我认为良好的方式需要满足两个条件,一个是显示告诉调用者这个方法内是否有异步调用,第二个是显示进行异步等待。所以我并不太喜欢 kt 那个模式。以及我之前尝试给 kt 定制调度器,觉得比 C#麻烦。
forgottencoast
2021-11-18 21:17:07 +08:00
@liuhan907
@Buges
长篇大论文字讨论不如写代码来明示各种方式的优劣。
liuhan907
2021-11-19 10:46:31 +08:00
@forgottencoast 懒,这玩意一写就是几百行还得加注释和两份代码对比,不想写,又不是做教程。
0o0o0o0
2021-11-19 13:29:23 +08:00
@Buges
C# 中 Thread.Sleep() 会阻塞当前进程,Kotlin 中 Thread.sleep() 也会阻塞线程。
Kotlin 中 delay() 相当于 C# 中的 Task.delay(),都可以挂起 协程 /Task ,释放当前的线程。
Kotlin 的 协程 和 C# Task 实现方式有区别,但是用起来似乎没有什么区别,所以说 Kotlin 的协程有什么优点呢?
0o0o0o0
2021-11-19 14:52:58 +08:00
对异步感兴趣的可以看一下这个 issue
https://github.com/dotnet/runtime/issues/45159
(尾部会链接到另一个 issue )
感觉里面就是上面两位大佬吵的内容
fanshaohua
2021-11-20 13:11:05 +08:00
大佬们,语言和框架的争议貌似对解决这个问题没什么帮助...

@libasten, 不确定你的问题解决了没?
我最近也刚好遇到一个 EF Core 的性能问题,发现官方文档提供了定位性能问题的思路,觉得挺好的。

https://docs.microsoft.com/zh-cn/ef/core/performance/#identify-bottlenecks-and-measure-measure-measure
确定瓶颈并再三衡量
对于性能而言始终不变的是,没有数据显示问题就不要急着优化。正如高德纳所说,“过早优化是万恶之源”。 性能诊断部分介绍了各种方法,可用于了解应用程序在数据库逻辑中的哪些位置最耗时,还介绍了如何确定有问题的具体部分。 确定缓慢查询后,可以考虑使用解决方案:数据库是否缺少索引? 是否应尝试其他查询模式?

有时间的话,可以一起研究下。
hez2010
2021-11-23 19:04:58 +08:00
你可以试试把返回值类型改成 `Task<List<DocItem>>`,`ActionResult`不是必须的,也没必要返回 `IEnumerable`。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/815708

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX