Performance 内存收集速度慢,每秒约500个请求
我有asp.net Core 3.1 API,每秒约500个请求。 我在一个纯基于内存的集合中进行分页。 该藏品约70万至100万件。 我只使用LINQ“Where”、“OrderBy”、“Take”和“Skip”,这是最简单的分页方法。 假设我的班级是这样的Performance 内存收集速度慢,每秒约500个请求,performance,linq,asp.net-core,Performance,Linq,Asp.net Core,我有asp.net Core 3.1 API,每秒约500个请求。 我在一个纯基于内存的集合中进行分页。 该藏品约70万至100万件。 我只使用LINQ“Where”、“OrderBy”、“Take”和“Skip”,这是最简单的分页方法。 假设我的班级是这样的 public class Item { public int Id { get; set; } public string Name { get; set; } public DateTime AgeUtc { g
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime AgeUtc { get; set; }
}
private IEnumerable PopulateItems(int?水印,UiParameters resourceParameters)
{
i数不清的项目;
if(水印.HasValue)
{
//验证水印是否小于列表中的第一个水印
var firstKeyValye=MemoryCacheService.Instance.PartitionedReadOnlyItems.First().Key;
if(水印k.Key>=startKey和&k.Key s.Value)
.SelectMany(p=>p.AsEnumerable());
items=PatientLocationsPage
.Where(p=>p.Id>cursor)
.Take(resourceParameters.PageSize);
退货项目;
}
items=MemoryCacheService.Instance.ReadOnlyItems
.Skip((resourceParameters.PageNumber-1)*resourceParameters.PageSize)
.Take(resourceParameters.PageSize);
退货项目;
}
这不是Sql查询,而是内存集合中的查询 哦,孩子。这是行不通的,你在一个有一百万个元素的数据库中进行线性搜索,每秒500次
您需要在集合的顶部实现某种索引,甚至像记录集合中每50个id的索引这样简单,这样您就不必每次都从一开始就使用
。其中(x=>x.id>watermark)
。如果它是按Id
排序的,即使是二进制搜索也比您现在所做的要好。您不会解释如何创建ReadOnlyItems
,但是如果您可以使用SortedList
,则可以大大加快您的方法
使用Item.Id
作为键,将您的项添加到排序列表中
在我对一百万个Id为4:1的Id
随机项进行的测试中,SortedList
的测试时间约为0.02-0.04毫秒,而第一次查找的测试时间约为1.2-4毫秒(或快100倍),后续查找的测试时间约为0.001毫秒,而后续查找的测试时间约为3.5毫秒(或快400倍)
然后,您可以执行以下操作:
private IEnumerable<ItemDto> PopulateItems(int? watermark, UiParameters uiParameters) {
IEnumerable<ItemDto> items;
if (watermark.HasValue) {
var firstIndex = MemoryCacheService.Instance.ReadOnlyItems.IndexOfKey(watermark.Value) + 1;
items = MemoryCacheService.Instance.ReadOnlyItems.Values
.Skip(firstIndex + (uiParameters.PageNumber - 1) * uiParameters.PageSize)
.Take(uiParameters.PageSize);
}
else
items = MemoryCacheService.Instance.ReadOnlyItems.Values
.Skip((uiParameters.PageNumber - 1) * uiParameters.PageSize)
.Take(uiParameters.PageSize);
return items;
}
您尚未显示在表上构建的任何索引。另外,Take
/Skip
如果没有OrderBy
,SQL查询没有顺序。这不是SQL查询,它在内存集合中MemoryCacheService.Instance.ReadOnlyItems的类型是什么List@NetMage另外,在执行分区解决方案时,我使用ImmutableSortedDictionary,Id是键。。我在“where”语句中使用了它,您不应该将它拆分为更小的列表,因为这将需要为枚举数分配很多资源。您要做的是将指针列表(作为索引,而不是实际指针)存储到现有的大型列表中。不过,理想的情况是,既然您意识到这有多么困难,就应该使用一个已经制作好的内存SQL数据库。例如,Entity Framework对内存中的Sqlite数据库具有一流的支持,您将立即看到它的好处!请你指出我读过的任何资料。我还在关于实体框架的问题中添加了一个“编辑”?请阅读微软的文档ReadOnlyItems
将使用IHostedService
每天使用计时器从SQL数据库中填充。在赋值操作过程中,锁定(_syncObj){ReadOnlyItems=newDbItems}
。。。首次请求470709、470710、470711…471708中的ID示例(这是第1/749页…每页1000个项目)。最后一页,我将返回时间戳:1222686
,这也是本页最后一项中的相同Id。我希望这能回答您的问题。@bunjeeb似乎可以轻松地转换为SortedList
,但我添加了一个扩展方法,以防。。。
private IEnumerable<ItemDto> PopulateItems(int? watermark, UiParameters uiParameters) {
IEnumerable<ItemDto> items;
if (watermark.HasValue) {
var firstIndex = MemoryCacheService.Instance.ReadOnlyItems.IndexOfKey(watermark.Value) + 1;
items = MemoryCacheService.Instance.ReadOnlyItems.Values
.Skip(firstIndex + (uiParameters.PageNumber - 1) * uiParameters.PageSize)
.Take(uiParameters.PageSize);
}
else
items = MemoryCacheService.Instance.ReadOnlyItems.Values
.Skip((uiParameters.PageNumber - 1) * uiParameters.PageSize)
.Take(uiParameters.PageSize);
return items;
}
public static SortedList<TKey, TValue> ToSortedList<T, TKey, TValue>(this IEnumerable<T> items, Func<T, TKey> keyFn, Func<T, TValue> valueFn) {
var ans = new SortedList<TKey, TValue>();
foreach (var item in items)
ans.Add(keyFn(item), valueFn(item));
return ans;
}