RavenDB 3.5:我的线程池更智能

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

.NET 线程池是一项非常了不起的技术,适用范围很广。 RavenDB 从一开始就将它用于几乎所有并发工作。

在 RavenDB 3.5 中,我们决定改变这一点。 RavenDB 有很多并行执行需求,但其中大部分都有独特的特性,我们可以用自己的线程池更好地表达。

首先,与普通线程池不同,我们不只是注册一个委托和一些状态供其执行,我们总是注册一个要处理的项目列表,以及一个从该列表中获取单个项目或该列表的一部分。这让我们在工作窃取方面做得更好。因为我们对实际操作有更多的了解。我们 知道 当我们执行完一个特定的委托时,我们可以在列表中的下一个可用项目上运行相同的委托。这给了我们更高的代码局部性,因为我们总是执行相同的任务,只要我们在池中有任务。

我们经常有嵌套操作,一个并行任务(执行索引工作)会产生额外的并行工作(索引以下文档)。通过将这一切都基于我们的自定义线程池,我们可以以一种不涉及等待该工作完成的方式执行这些操作。相反,我们运行的线程池线程能够通过执行我们正在等待的工作来“等待”。我们没有阻塞的线程,并且在许多情况下我们可以避免任何上下文切换。

在负载下,这意味着线程不会将大量工作放在线程池上,然后必须相互争夺谁先完成工作,这意味着我们可以运行自己的任务,并且只有当有是否有足够的线程可用,换句话说,我们是否会扩展更多线程。

说到负载,新的线程池还具有动态负载平衡功能。因为我们知道 RavenDB 只会将线程池用于后台工作,所以我们可以相应地确定事情的优先级。默认情况下,RavenDB 试图将 CPU 使用率保持在 60% – 80% 的范围内。如果我们检测到我们有更高的 CPU 使用率,我们将开始减少我们正在做的后台工作,以确保我们不会影响前排工作(如服务请求)。我们将通过更改后台线程的优先级来开始这样做,并最终停止处理大多数后台线程中的工作(当然,我们总是有最小数量的线程将继续工作)。

线程池可以做的另一件有趣的事情是检测和处理 slowpokes。一个常见的例子是需要很 长时间 才能运行的索引。明显超过所有其他指标。线程池可以释放所有其他索引,并让调用代码知道这个特定任务已被留给自己运行。然后 RavenDB 将拆分索引工作,因此慢速索引不会减慢所有其余索引。

将执行请求处理的前排工作(标准 .NET 线程池)和后台池(这是我们自己的自定义实现)之间的线程池分开,我们在环境中获得了更多的可预测性。我们不必担心索引作业会接管处理请求所需的线程,或者服务器上的请求会影响新数据库的加载等。

最后,就像现在 RavenDB 中的所有其他功能一样,我们有一组丰富的调试端点,可以详细地告诉我们到底发生了什么。当我们谈论运行数月和数年的系统时,或者当我们试图对有问题的服务器进行故障排除时,这一点至关重要。