linux内存初始化时内核CPU使用率高

2024-01-12

在服务器上引导我的 java 应用程序时,我遇到了 Linux 内核 CPU 消耗高的问题。此问题仅发生在生产中,在开发服务器上一切都是光速。

upd9:关于这个问题,有两个疑问:

  1. 如何修复它? -名义动物建议同步并删除所有内容,这确实有帮助。sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ; Works. upd12:但确实sync足够。

  2. 为什么会发生这种情况?- 它对我来说仍然是开放的,我确实知道将脏页刷新到磁盘会消耗内核 CPU 和 IO 时间,这是正常的。但奇怪的是,为什么即使是用“C”编写的单线程应用程序,我也会在内核空间中 100% 地加载所有内核?

由于参考upd10和参考upd11我有一个想法echo 3 > /proc/sys/vm/drop_caches不需要解决我的内存分配缓慢的问题。 运行“sync”应该足够了before启动消耗内存的应用程序。 明天可能会在生产中尝试这个并在这里发布结果。

upd10:FS 缓存页面丢失情况:

  1. 我执行了cat 10GB.fiel > /dev/null, then
  2. sync可以肯定的是,没有脏页(cat /proc/meminfo |grep ^Dirty显示184kb。
  3. 检查cat /proc/meminfo |grep ^Cached我得到: 4GB 缓存
  4. Running int main(char**)我得到了正常的性能(比如初始化 32MB 的分配数据需要 50 毫秒)。
  5. 缓存内存减少至 900MB
  6. 测试总结:我认为linux将用作FS缓存的页面回收到分配的内存中是没有问题的。

upd11:很多脏页情况。

  1. 项目清单

  2. 我运行我的HowMongoDdWorks带注释的示例read部分,一段时间后

  3. /proc/meminfo说2.8GB是Dirty3.6GB 是Cached.

  4. 我停下了HowMongoDdWorks并运行我的int main(char**).

  5. 这是结果的一部分:

    初始化15,时间0.00s x 0 [尝试 1/部分 0] 时间 1.11s x 1 [尝试 2/第 0 部分] 时间 0.04s x 0 [尝试 1/第 1 部分] 时间 1.04s x 1 [尝试 2/第 1 部分] 时间 0.05 秒 x 0 [尝试 1/第 2 部分] 时间 0.42s x 1 [尝试 2/第 2 部分] 时间 0.04s

  6. 通过测试总结:丢失脏页会显着减慢对已分配内存的首次访问(公平地说,只有当应用程序总内存开始与整个操作系统内存相当时,才会发生这种情况,即,如果您有 16 GB 中的 8 GB 可用内存,则没有问题分配 1GB,从 3GB 左右开始减慢)。

现在我设法在我的开发环境中重现这种情况,所以这里是新的细节。

开发机配置:

  1. Linux 2.6.32-220.13.1.el6.x86_64 - Scientific Linux 版本 6.1 (Carbon)
  2. 内存:15.55 GB
  3. CPU:1 个 Intel(R) Core(TM) i5-2300 CPU @ 2.80GHz(4 线程)(物理)

99.9% 的问题是由 FS 缓存中存在大量脏页引起的。这是在脏页上创建大量内容的应用程序:

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;

/**
 * @author dmitry.mamonov
 *         Created: 10/2/12 2:53 PM
 */
public class HowMongoDdWorks{
    public static void main(String[] args) throws IOException {
        final long length = 10L*1024L*1024L*1024L;
        final int pageSize = 4*1024;
        final int lengthPages = (int) (length/pageSize);
        final byte[] buffer = new byte[pageSize];
        final Random random = new Random();
        System.out.println("Init file");
        final RandomAccessFile raf = new RandomAccessFile("random.file","rw");
        raf.setLength(length);
        int written = 0;
        int readed = 0;
        System.out.println("Test started");
        while(true){
            { //write.
                random.nextBytes(buffer);
                final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
                raf.seek(randomPageLocation);
                raf.write(buffer);
                written++;
            }
            { //read.
                random.nextBytes(buffer);
                final long randomPageLocation = (long)random.nextInt(lengthPages)*(long)pageSize;
                raf.seek(randomPageLocation);
                raf.read(buffer);
                readed++;
            }
            if (written % 1024==0 || readed%1024==0){
                System.out.printf("W %10d R %10d pages\n", written, readed);
            }

        }
    }
}

这是测试应用程序,它会导致内核空间中的 HI(所有核心高达 100%)CPU 负载(与下面相同,但我将再次复制它)。

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
   int last = clock(); //remember the time
   for(int i=0;i<16;i++){ //repeat test several times
      int size = 256 * 1024 * 1024;
      int size4=size/4;
      int* buffer = malloc(size); //allocate 256MB of memory
      for(int k=0;k<2;k++){ //initialize allocated memory twice
          for(int j=0;j<size4;j++){ 
              //memory initialization (if I skip this step my test ends in 
              buffer[j]=k; 0.000s
          }
          //printing 
          printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
          last = clock();
      }
   }
   return 0;
}

虽然之前HowMongoDdWorks程序正在运行,int main(char** argv)将显示如下结果:

x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...

我将所有内容保留在这条线以下只是出于历史目的。


upd1:开发和生产系统对于此测试来说都足够大。upd7:这不是分页,至少我在问题期间没有看到任何存储 IO 活动。

  1. 开发 ~ 4 核,16GB RAM,~8GB 可用空间
  2. 生产 ~ 12 核,24 GB RAM,~ 16 GB 可用空间(8 到 10 GM 在 FS 缓存下,但没有 不同的是,即使所有 16GM 都是完全免费的,结果也是一样的),而且这台机器是 CPU 负载的,但不会太高~10%。

更新8(参考):新的测试用例和潜在的解释见尾部。

这是我的测试用例(我还测试了java和python,但“c”应该是最清楚的):

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
   int last = clock(); //remember the time
   for(int i=0;i<16;i++){ //repeat test several times
      int size = 256 * 1024 * 1024;
      int size4=size/4;
      int* buffer = malloc(size); //allocate 256MB of memory
      for(int k=0;k<2;k++){ //initialize allocated memory twice
          for(int j=0;j<size4;j++){ 
              //memory initialization (if I skip this step my test ends in 
              buffer[j]=k; 0.000s
          }
          //printing 
          printf(x "[%d] %.2f\n",k+1, (clock()-last)/(double)CLOCKS_PER_SEC); stat
          last = clock();
      }
   }
   return 0;
}

开发机器上的输出(部分):

x [1] 0.13 --first initialization takes a bit longer
x [2] 0.12 --then second one, but the different is not significant.
x [1] 0.13
x [2] 0.12
x [1] 0.15
x [2] 0.11
x [1] 0.14
x [2] 0.12
x [1] 0.14
x [2] 0.12
x [1] 0.13
x [2] 0.12
x [1] 0.14
x [2] 0.11
x [1] 0.14
x [2] 0.12 -- and the results is quite stable
...

生产机器的输出(部分):

x [1] 0.23
x [2] 0.19
x [1] 0.24
x [2] 0.19
x [1] 1.30 -- first initialization takes significantly longer
x [2] 0.19 -- then seconds one (6x times slowew)
x [1] 10.94 -- and some times it is 50x slower!!!
x [2] 0.19
x [1] 1.10
x [2] 0.21
x [1] 1.52
x [2] 0.19
x [1] 0.94
x [2] 0.21
x [1] 2.36
x [2] 0.20
x [1] 3.20
x [2] 0.20 -- and the results is totally unstable
...

在开发机器上运行此测试时,CPU 使用率甚至没有从地面上升,就像 htop 中所有内核的使用率都低于 5%。

但在生产机器上运行此测试时,我发现所有核心的 CPU 使用率高达 100%(在 12 核机器上平均负载上升至 50%),而且都是内核时间。

upd2:所有机器都安装了相同的 centos linux 2.6,我使用 ssh 与它们一起工作。

upd3:答:不太可能发生交换,在我的测试期间没有看到任何磁盘活动,并且大量 RAM 也是免费的。 (此外,描述已更新)。 – 德米特里 9 分钟前

upd4:htop 表示内核的 CPU 利用率很高,所有核心的利用率高达 100%(在产品上)。

upd5:初始化完成后CPU利用率是否稳定下来?在我的简单测试中 - 是的。对于真正的应用程序,它只是帮助停止其他一切来启动一个新程序(这是无稽之谈)。

我有两个问题:

  1. 为什么会发生这种情况?

  2. 如何修复它?

upd8:改进了测试和解释。

#include<stdlib.h>
#include<stdio.h>
#include<time.h>

int main(char** argv){
    const int partition = 8;
   int last = clock();
   for(int i=0;i<16;i++){
       int size = 256 * 1024 * 1024;
       int size4=size/4;
       int* buffer = malloc(size);
       buffer[0]=123;
       printf("init %d, time %.2fs\n",i, (clock()-last)/(double)CLOCKS_PER_SEC);
       last = clock();
       for(int p=0;p<partition;p++){
            for(int k=0;k<2;k++){
                for(int j=p*size4/partition;j<(p+1)*size4/partition;j++){
                    buffer[j]=k;
                }
                printf("x [try %d/part %d] time %.2fs\n",k+1, p, (clock()-last)/(double)CLOCKS_PER_SEC);
                last = clock();
            }
      }
   }
   return 0;
}

结果如下所示:

init 15, time 0.00s -- malloc call takes nothing.
x [try 1/part 0] time 0.07s -- usually first try to fill buffer part with values is fast enough.
x [try 2/part 0] time 0.04s -- second try to fill buffer part with values is always fast.
x [try 1/part 1] time 0.17s
x [try 2/part 1] time 0.05s -- second try...
x [try 1/part 2] time 0.07s
x [try 2/part 2] time 0.05s -- second try...
x [try 1/part 3] time 0.07s
x [try 2/part 3] time 0.04s -- second try...
x [try 1/part 4] time 0.08s
x [try 2/part 4] time 0.04s -- second try...
x [try 1/part 5] time 0.39s -- BUT some times it takes significantly longer then average to fill part of allocated buffer with values.
x [try 2/part 5] time 0.05s -- second try...
x [try 1/part 6] time 0.35s
x [try 2/part 6] time 0.05s -- second try...
x [try 1/part 7] time 0.16s
x [try 2/part 7] time 0.04s -- second try...

我从这次测试中了解到的事实。

  1. 内存分配本身很快。
  2. 首次访问分配的内存速度很快(因此这不是惰性缓冲区分配问题)。
  3. 我将分配的缓冲区分成几部分(测试中为 8 个部分)。
  4. 并用值0填充每个缓冲区部分,然后用值1,打印消耗时间。
  5. 第二个缓冲区部分填充总是很快。
  6. 但是第一个缓冲区部分文件总是比第二个填充慢一点(我相信我的内核在第一页访问时完成了一些额外的工作)。
  7. 有时,第一次用值填充缓冲区部分需要花费相当长的时间。

我尝试了建议的答案,似乎有帮助。我稍后会重新检查并再次发布结果。

看起来linux将分配的页面映射到耐用的文件系统缓存页面,并且需要花费大量时间将页面一页一页地刷新到磁盘。但完全同步运行速度很快并消除了问题。


Run

sudo sh -c 'sync ; echo 3 > /proc/sys/vm/drop_caches ; sync'

在你的开发机器上。这是确保缓存为空的安全、非破坏性方法。 (你会not即使您恰好同时保存或写入磁盘,运行上述命令也不会丢失任何数据。确实很安全。)

然后,确保没有运行任何 Java 内容,并重新运行上述命令以确保确定。例如,您可以检查是否有 Java 正在运行

ps axu | sed -ne '/ sed -ne /d; /java/p'

它不应该输出任何内容。如果是这样,请先关闭 Java 内容。

现在,重新运行您的应用程序测试。您的开发机器上现在是否也出现同样的速度下降情况?

如果您愿意以任何方式留下评论,德米特里,我很乐意进一步探讨这个问题。

编辑补充:我怀疑确实发生了速度减慢的情况,这是由于 Java 本身产生的较大启动延迟造成的。这是一个非常常见的问题,并且基本上是 Java 内置的,这是其架构的结果。对于较大的应用程序,无论机器的速度有多快,启动延迟通常都是一秒的相当大的一部分,这仅仅是因为 Java 必须加载和准备类(大多数情况下也是串行的,因此添加内核无济于事)。

换句话说,我认为这应该归咎于Java,而不是Linux;恰恰相反,因为 Linux 设法通过内核级缓存来减轻开发机器上的延迟,而这只是因为您实际上一直在运行这些 Java 组件,因此内核知道缓存它们。

编辑 2:当应用程序启动时,查看 Java 环境访问哪些文件将非常有用。你可以这样做strace:

strace -f -o trace.log -q -tt -T -e trace=open COMMAND...

它创建文件trace.log含有open()由以下启动的任何进程完成的系统调用COMMAND...。将输出保存到trace.PID对于每个进程COMMAND...启动,使用

strace -f -o trace -ff -q -tt -T -e trace=open COMMAND...

比较开发和生产安装上的输出将告诉您它们是否真正相同。其中之一可能有额外或缺失的库,从而影响启动时间。

如果安装较旧且系统分区相当满,则这些文件可能已形成碎片,导致内核花费更多时间等待 I/O 完成。 (请注意,amountI/O 保持不变;只是如果文件碎片化,完成所需的时间会增加。)您可以使用命令

LANG=C LC_ALL=C sed -ne 's|^[^"]* open("\(.*\)", O[^"]*$|\1|p' trace.* \
| LANG=C LC_ALL=C sed -ne 's|^[^"]* open("\(.*\)", O[^"]*$|\1|p' \
| LANG=C LC_ALL=C xargs -r -d '\n' filefrag \
| LANG=C LC_ALL=C awk '(NF > 3 && $NF == "found") { n[$(NF-2)]++ }
  END { for (i in n) printf "%d extents %d files\n", i, n[i] }' \
| sort -g

检查应用程序使用的文件的碎片程度;它报告有多少文件仅使用一个或多个扩展区。请注意,它不包括原始可执行文件(COMMAND...),仅它访问的文件。

如果您只想获取单个命令访问的文件的碎片统计信息,您可以使用

LANG=C LC_ALL=C strace -f -q -tt -T -e trace=open COMMAND... 2>&1 \
| LANG=C LC_ALL=C sed -ne 's|^[0-9:.]* open("\(.*\)", O[^"]*$|\1|p' \
| LANG=C LC_ALL=C xargs -r filefrag \
| LANG=C LC_ALL=C awk '(NF > 3 && $NF == "found") { n[$(NF-2)]++ }
  END { for (i in n) printf "%d extents %d files\n", i, n[i] }' \
| sort -g

如果问题不是由于缓存引起的,那么我认为这两种安装很可能并不真正等效。如果是,那么我会检查碎片。之后,我会进行完整的跟踪(省略-e trace=open)在两种环境上查看到底差异在哪里。


我相信我现在了解您的问题/情况。

在您的产品环境中,内核页面缓存大部分是脏的,即大多数缓存的内容都是将要写入磁盘的内容。

当您的应用程序分配新页面时,内核仅设置页面映射,实际上并不会立即提供物理RAM。这只发生在第一次访问每个页面时。

在第一次访问时,内核首先找到一个空闲页面——通常是一个包含“干净”缓存数据的页面,即从磁盘读取但未修改的数据。然后,它将其清零,以避免进程之间的信息泄漏。 (当使用 C 库分配工具时,例如malloc()等等,而不是直接mmap()函数族,库可以使用/重用映射的部分。尽管内核确实将页面清零,但库可能会“弄脏”它们。使用mmap()要获得匿名页面,您必须将它们归零。)

如果内核手头没有合适的干净页,它必须首先将一些最旧的脏页刷新到磁盘。 (内核内部有一些进程将页面刷新到磁盘,并将它们标记为干净,但是如果服务器负载导致页面不断变脏,通常希望拥有大部分脏页面而不是大部分干净页面 - 服务器会得到这样可以完成更多的工作。不幸的是,这也意味着首页访问延迟的增加,您现在遇到了这种情况。)

每一页都是sysconf(_SC_PAGESIZE)字节长,对齐。换句话说,指针p指向页面的开头当且仅当((long)p % sysconf(_SC_PAGESIZE)) == 0。我相信,大多数内核在大多数情况下实际上都会填充页面组而不是单个页面,从而增加了首次访问(对每组页面)的延迟。

最后,可能有一些编译器优化会对您的基准测试造成严重破坏。我建议您为基准测试编写一个单独的源文件main(),以及每次迭代完成的实际工作在单独的文件中。单独编译它们,然后将它们链接在一起,以确保编译器不会重新排列时间函数。实际完成的工作。基本上,在benchmark.c:

#define _POSIX_C_SOURCE 200809L
#include <time.h>
#include <stdio.h>

/* in work.c, adjust as needed */
void work_init(void);      /* Optional, allocations etc. */
void work(long iteration); /* Completely up to you, including parameters */
void work_done(void);      /* Optional, deallocations etc. */

#define PRIMING    0
#define REPEATS  100

int main(void)
{
    double          wall_seconds[REPEATS];
    struct timespec wall_start, wall_stop;
    long            iteration;

    work_init();

    /* Priming: do you want caches hot? */
    for (iteration = 0L; iteration < PRIMING; iteration++)
        work(iteration);

    /* Timed iterations */
    for (iteration = 0L; iteration < REPEATS; iteration++) {
        clock_gettime(CLOCK_REALTIME, &wall_start);
        work(iteration);
        clock_gettime(CLOCK_REALTIME, &wall_stop);
        wall_seconds[iteration] = (double)(wall_stop.tv_sec - wall_start.tv_sec)
                                + (double)(wall_stop.tv_nsec - wall_start.tv_nsec) / 1000000000.0;
    }

    work_done();

    /* TODO: wall_seconds[0] is the first iteration.
     *       Comparing to successive iterations (assuming REPEATS > 0)
     *       tells you about the initial latency.
    */

    /* TODO: Sort wall_seconds, for easier statistics.
     *       Most reliable value is the median, with half of the
     *       values larger and half smaller.
     *       Personally, I like to discard first and last 15.85%
     *       of the results, to get "one-sigma confidence" interval.
    */

    return 0;
}

实际的数组分配、释放和填充(每个重复循环)在work()函数定义在work.c.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux内存初始化时内核CPU使用率高 的相关文章

随机推荐

  • MVC5 - 数据注释 - 客户端验证没有发生?

    我有一个 MVC 5 应用程序 我使用数据注释来进行大部分验证 我的班级中的属性之一如下所示 Required ErrorMessage Please enter a business name StringLength 80 public
  • 如何从选项卡排序列表中排除小部件?

    此图来自Qt官网 我以此为例 我想避免一些不重要的小部件以选项卡为中心 如果您有一个小部件想要在一些常用的之间快速旋转 则此策略非常有用QLineEdit输入数据并转义那些很少使用的设置 Take the picture as an exa
  • python 中具有无限初始条件的 ODE

    我有一个二阶微分方程 我想用 python 求解它 问题是对于其中一个变量我没有初始条件0但仅限于无穷大的值 谁能告诉我应该提供哪些参数scipy integrate odeint 能解决吗 Equation Theta 需要根据时间来找到
  • 在 Eclipse 中添加外部 jar

    我创建了一个连接 MySQL 的程序 我使用 eclipse 添加外部 jar 选项添加 Connector j 程序在eclipse中运行良好 但是当我使用 eclipse 创建可执行 jar 并运行它时 它总是给出 ClassNotFo
  • 打开文件失败是否必须使用die?

    大多数时候 我会做这样的事情 open FH gt file txt or die Cann t open file Does die必须使用吗 如果我希望我的脚本继续 并且如果无法打开文件则忽略错误 我应该做什么 你可能想做类似的事情 i
  • openSUSE 的构建必备

    我是 openSUSE 的新手 我需要获得系统的构建必要条件 但无法使用它sudo apt get install build essential或者甚至通过使用sudo apt get update然后按照前面的代码进行操作 我找到了一种
  • 无法使用 SSH 访问 AWS CodeCommit

    弄清楚如何让 AWS CodeCommit 与标准 SSH 身份验证配合使用非常困难 看到另一个类似的主题 但没有答案 我还不能发表评论 这是在 Windows 上使用 Git Bash 重现步骤 创建具有完全权限的 IAM 用户 AwsA
  • 如何从 dropzone.js 上传和删除文件

    我使用了下面的代码 图像已被删除 但缩略图仍然显示 Dropzone options myDropzone init function this on success function file response file serverId
  • 在 R 中将日期转换为星期几

    我的数据框中有一个这种格式的日期 02 July 2015 我需要将其转换为星期几 即 183 就像是 df day of week lt weekdays as Date df date column 但这不理解日期的格式 你可以使用lu
  • 防止引导程序弹出窗口中的默认值

    我正在使用 twitter bootstrap 并且我已经得到了这段代码 addYT on click function event var this this event preventDefault popover placement
  • 递归:如何避免Python设置在迭代过程中更改设置 RuntimeError

    背景及问题描述 我有一些代码可以解决图着色问题 广义上定义为将 颜色 分配给无向图的问题 确保由边连接的两个顶点没有相同的颜色 我正在尝试使用约束传播来实现一个解决方案 以提高标准递归回溯算法的效率 但遇到以下错误 File C Users
  • 我想将 Qt QML Combobox 设置为 PyQt5 对象属性

    我正在编写一个小程序 它使用 Qt5 QML 作为 GUI 层 并使用 Python3 PyQt5 来实现数据模型 我现在想显示一个ComboBox在 QML 中并将其模型设置为枚举列表 如何将枚举导出为 python 类的属性 以便我可以
  • Sling解析脚本调用顺序

    我正在研究 sling 如何根据 url 调用脚本 在选择器的情况下 它似乎工作正常 但如果我不使用选择器 它会让我难以理解 我有一个页面 content AEMProject English test html其中有资源类型AEMProj
  • 如何检查模型中是否存在 DbContext.Set

    我遇到的情况是 我可能正在使用多个 DbContext 这些 DbContext 可能包含也可能不包含 SomeEntity 的 DbSet 当然 如果我关闭 SaveChanges 并且该实体不存在 则会出现以下错误 实体类型 SomeE
  • 如何使用 Java 禁用 Selenium WebDriver 中的 Chrome 插件

    Chrome 插件弹出 https i stack imgur com jRBdG png 当我为此应用程序执行自动化代码时 会显示上面的弹出窗口 现在我需要知道如何使用 Java 禁用 Selenium WebDriver 中的 PDF
  • 使用 CMake 链接到 TBB 库

    I have tbb下载并放置在我的存储库目录中 gt tree deps tbb d deps tbb bin cmake templates include serial tbb tbb compat internal machine
  • Laravel 扩展 TestResponse 类

    我正在尝试添加自定义断言TestReponse https laravel com api 5 5 Illuminate Foundation Testing TestResponse html类所以我可以做这样的事情 response t
  • 如何使多个带有 OR 的 LEFT JOIN 完全使用复合索引? (第2部分)

    它用于计算用户进入 离开工作场所时如何扫描指纹的系统 我不知道它的英文怎么称呼 我需要确定用户是否早上迟到 以及用户是否提前下班 This tb scan表包含用户扫描指纹的日期和时间 CREATE TABLE tb scan scperc
  • Swift:如何使用关联的应用程序打开文件?

    我喜欢在 macOS 上使用 Excel 打开 xls 文件 我只找到了 C 的示例 但没有找到 Swift 的示例 附加问题 即使该文件扩展名与 Excel 无关 是否也可以使用 Excel 启动该文件 有NSWorkspace shar
  • linux内存初始化时内核CPU使用率高

    在服务器上引导我的 java 应用程序时 我遇到了 Linux 内核 CPU 消耗高的问题 此问题仅发生在生产中 在开发服务器上一切都是光速 upd9 关于这个问题 有两个疑问 如何修复它 名义动物建议同步并删除所有内容 这确实有帮助 su