昨天在园子里提了一个EFcore多线程查询导致服务内存飙升的问题,现在依然还没有找到解决办法,今天把问题详细的陈述一遍。
刚开始发现问题是公司一个服务的内存飚到了7个G,系统直接杀死了这个进程,后面发现问题是随着高并发的访问,里面的EFcore进行大量数据查询,在访问结束后内存没有释放干净,内存不断的堆积,服务直接就崩了。然后我在本地用例还原了这个过程,大致如下:
GRPC服务端:
(一开始怀疑是DatabaseContext注入的问题,用了AddScoped;AddTransient;后者直接用using都没有解决问题,所以不是这个问题)
在GRPC里一个方法SayHello查询一张表的数据(数据大概1w多)
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
var d = efoscoreContext.Project.Where(w => w.ProjectId > 15).Select(s => new
{
*******
}).ToList();
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
客户端:
每三秒调一次Grpc的SayHello方法,后面发现GRPC服务里确实释放不完内存,随着访问的次数的增加,内存会堆积到一个很高的值,如果内存抗不住就崩了
while (true)
{
for (int i = 1; i < 40; i++)
{
reply = client.SayHello(new FirstGrpc.HelloRequest() { Name = "" });
}
Thread.Sleep(TimeSpan.FromSeconds(3));
}
过程会进行GC,但是还会剩下大量的内存没GC完,而且会累积到很高。
后面思考,GRPC服务以线程池的方式响应请求,会不会是Efcore在多线程里出现了问题的原因
后面我用控制台做了一个测试,发现问题确实是这样,Efocore在多线程查询,线程结束后,内存并没有回收完
多线程Efocore查询用例:
先顺序执行:
while (true)
{
for (int i = 1; i < 100; i++)
{
ProjectQuery()//执行efocore查询
;
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
顺序执行没有任何问题,内存占用是很正常的也就一直100多M
多线程执行:
while (true)
{
for (int i = 1; i < 100; i++)
{
var t = new Task(() => ProjectQuery()//执行efocore查询
);
t.Start();
}
Thread.Sleep(TimeSpan.FromSeconds(5));
}
多线程执行一上来就飚到了2G,后面会剩下1.2g一直在那里,再后面就会1-4M的累积,累积