[.NET, C#] Разделённые запросы в EF Core
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
ORM Entity Framework Core с каждой версией становится все более и более богатой на фичи. Команда разработчиков тратит много времени на перфоманс и вероятно простое обновление Nuget-пакета уже приведет к некоторому бусту, который почувствуют пользователи. Но сегодня я хочу рассказать о совершенно конкретной фиче: это новый режим запросов — "разделённые запросы" или "split queries" в оригинале.
Предыстория
На моем текущем проекте в качестве хранилища используется Postgres, доступ к которой осуществляется через драйвер Npgsql и EF Core. Одной из центральных сущностей всего бизнес-процесса является "методика выполнения ПЦР-исследования", которая по сути представляет рецепт как выполнить исследование и включает в себя довольно много информации:
- список используемых реагентов с указанием "рецепта смешивания"
- список результатов, которые будут определены
- список оборудования на котором возможно выполнение исследования
- и так далее, всего 8 вложенных коллекций
Сам объект методики тоже имеет около дюжины полей, в основном небольших — название, описание, версия, и т.д.
Большинство коллекций являются связями много-ко-многим с другими справочными записями, которые сами содержат не так много информации, ну может до полудюжины скалярных полей.
В самом начале мы не парили себе голову и стали использовать lazy-loading, что хорошо работало в сценариях "частичного применения": когда извлекалась методика только со списком реагентов, или методика только со списком результатов.
Но в определенных сценариях требовалось получить методику "целиком", что привело к классической проблеме N+1, когда для извлечения сущности и связанных коллекций требуется 1 запрос на извлечение сущности и еще по одному для каждого элемента коллекции.
Такое отношение к данным сильно просаживало производительность и следующим витком было включить коллекции в родительский объект с использованием LINQ-конструкций .Include().ThenInclude().
Всё работало более менее хорошо до появления царь-методики. Это комплексный и сложный тест, в который включено много реагентов, много результатов, он использует много каналов детекции. К ней производится много форм комплектации наборов реагентов.
Так или иначе, мы получили развесистую структуру, в которой каждая вложенная коллекция имела не как обычно 3-5 записей, а по 20-30. Вот тут наш софт и сказал "кря".
Если раньше на извлечение полной сущности уходило от нескольких десятков миллисекунд до сотен, то у царь-методики это занимало до полутора десятков секунд, что иногда приводило к краху запроса в БД. Это было уже недопустимо и требовало каких-то решений.
Пересказ документации
Как пишут Майкрософт в своей статье про разделённые запросы обычно каждый LINQ-запрос преобразуется в один SQL запрос с использованием JOIN для извлечения связанных коллекций.
Я позволю себе немного перевернуть пример из документации и извлекать не блоги и посты, а посты и комменты. Пусть модель данных выглядит следующим образом:
public class Post
{
public int ID { get; set; }
public string Content { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public int ID { get; set; }
public Post ParentPost { get; set; }
public int ParentPostID { get; set; }
public string CommentText { get; set; }
}
И попытаемся извлечь хайповый пост с 1000 комментариями из бд следующим запросом:
var veryPopularPost = Posts
.Include(x => x.Comments)
.First(x => x.ID == 42);
Что будет транслировано примерно в такой SQL-запрос усреднённый, потому что вариаций масса:
SELECT p.id, p.content, c.id, c.parentpostid, c.commenttext
FROM posts AS p
LEFT JOIN comments AS c ON (p.id = c.parentpostid)
WHERE p.id = 42
LIMIT 1;
А что если статья большая? Например автор был в ударе и выдал 100 КБ текста (это около 50 печатных листов). Комментаторы читали, наслаждались и комментировали, так что оставили 1000 комментариев. Сколько примерно будет весить результирующий набор, который надо вычитать ORM?
На вскидку — p.content = 100 КБ, повторим 1000 раз и на выходе ~ 100 МБ текста, дублирующегося 1000 раз. И это без учета размера int и текста комментариев.
Lazy-load в этом случае будет побыстрее, хотя бомбить базу 1000 и одним запросом — тоже сомнительное развлечение. Можно ли что-то с этим сделать не прибегая вручную к оптимизации запросов?
AsSplitQuery()
Да, и вот каким образом. В EF Core 5.0 появилась новая директива .AsSplitQuery(), которая заставит query provider транслировать загрузку связанной коллекции отдельным запросом.
var veryPopularPost = Posts
.Include(x => x.Comments)
.AsSplitQuery()
.First(x => x.ID == 42);
Транслируется в следующие SQL-запросы:
SELECT p.id, p.content
FROM posts AS p
WHERE p.id = 42
LIMIT 1;
SELECT c.id, c.parentpostid, c.commenttext, p.id
FROM posts AS p
INNER JOIN comments AS c ON (p.id = c.parentpostid)
WHERE p.id = 42
LIMIT 1;
Что уже приведет к тому, что текст записи не будет читаться 1000 раз, а только 1.
Для нескольких коллекций поведение будет аналогичное, что избавит от комбинаторного взрыва.
Бенчмарки
На скорую руку я сваял бенчмарк. Пусть есть коллекция записей типа MainEntity, в которой нет ничего, кроме вложенных коллекций. 5 "маленьких" записей и 1 большая.
public class MainEntity
{
public int ID { get; set; }
public List<RefEntity1> Ref1 { get; set; }
public List<RefEntity2> Ref2 { get; set; }
public List<RefEntity3> Ref3 { get; set; }
public List<RefEntity4> Ref4 { get; set; }
public List<RefEntity5> Ref5 { get; set; }
public List<BigRefEntity> BigRef { get; set; }
}
public abstract class RefEntity
{
public int ID { get; set; }
public string Payload { get; set; } = string.Empty;
public MainEntity MainEntity { get; set; }
public int? MainEntityID { get; set; }
}
public class RefEntity1 : RefEntity { }
public class RefEntity2 : RefEntity { }
public class RefEntity3 : RefEntity { }
public class RefEntity4 : RefEntity { }
public class RefEntity5 : RefEntity { }
public class BigRefEntity : RefEntity { }
Записи заполняются при инициализации БД строчками случайной длины по 10 символов для "маленькой" записи и по 1000 для "большой".
В каждую вложенную коллекцию добавляется по ItemsInCollection записей. Тестовый метод извлекает по 2 записи MainEntity, присоединяя к ней от 0 до 6 коллекций (параметр LoadRefs) в двух режимах — одним запросом и разделёнными запросами (параметр SplitQueries).
[Benchmark]
public List<MainEntity> QueryLoad()
{
IQueryable<MainEntity> query = LoadRefs switch
{
0 => dbContext.MainEntities,
1 => dbContext.MainEntities
.Include(x => x.Ref1),
2 => dbContext.MainEntities
.Include(x => x.Ref1)
.Include(x => x.Ref2),
3 => dbContext.MainEntities
.Include(x => x.Ref1)
.Include(x => x.Ref2)
.Include(x => x.Ref3),
4 => dbContext.MainEntities
.Include(x => x.Ref1)
.Include(x => x.Ref2)
.Include(x => x.Ref3)
.Include(x => x.Ref4),
5 => dbContext.MainEntities
.Include(x => x.Ref1)
.Include(x => x.Ref2)
.Include(x => x.Ref3)
.Include(x => x.Ref4)
.Include(x => x.Ref5),
6 => dbContext.MainEntities
.Include(x => x.Ref1)
.Include(x => x.Ref2)
.Include(x => x.Ref3)
.Include(x => x.Ref4)
.Include(x => x.Ref5)
.Include(x => x.BigRef),
_ => throw new ArgumentOutOfRangeException()
};
var splitQuery = SplitQueries ? query.AsSplitQuery() : query;
return splitQuery.Take(2).ToList();
}
Полный код бенчмарка доступен на гитхабе.
Я запускал бенчмарк на домашней машине, СУБД в дефолтной конфигурации, подключение локальное через localhost.
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
AMD Ryzen 5 2400G with Radeon Vega Graphics, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.201
[Host] : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT [AttachedDebugger]
Job-HMJXLI : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT
InvocationCount=1 UnrollFactor=1
ItemsInCollection
SplitQueries
LoadRefs
Mean
Error
StdDev
Median
2
False
0
694.6 μs
18.42 μs
52.57 μs
686.9 μs
2
False
1
1,004.3 μs
25.43 μs
69.60 μs
983.4 μs
2
False
2
1,255.3 μs
32.02 μs
89.25 μs
1,237.0 μs
2
False
3
1,578.9 μs
45.46 μs
126.73 μs
1,545.1 μs
2
False
4
2,013.3 μs
56.55 μs
162.26 μs
1,976.8 μs
2
False
5
2,685.2 μs
69.00 μs
196.85 μs
2,651.1 μs
2
False
6
4,646.8 μs
134.52 μs
392.41 μs
4,515.2 μs
2
True
0
726.5 μs
17.60 μs
48.76 μs
725.0 μs
2
True
1
1,403.1 μs
34.46 μs
96.06 μs
1,394.3 μs
2
True
2
1,928.7 μs
57.68 μs
165.51 μs
1,923.3 μs
2
True
3
2,639.6 μs
96.20 μs
277.56 μs
2,584.5 μs
2
True
4
3,128.8 μs
117.46 μs
340.77 μs
3,180.4 μs
2
True
5
3,725.9 μs
121.37 μs
357.87 μs
3,713.8 μs
2
True
6
4,299.9 μs
166.28 μs
485.04 μs
4,233.4 μs
5
False
0
706.6 μs
18.03 μs
50.25 μs
698.9 μs
5
False
1
1,071.6 μs
20.91 μs
51.69 μs
1,068.6 μs
5
False
2
1,512.7 μs
30.13 μs
54.33 μs
1,513.6 μs
5
False
3
2,809.9 μs
148.44 μs
435.35 μs
2,619.9 μs
5
False
4
7,803.3 μs
435.35 μs
1,242.08 μs
7,243.8 μs
5
False
5
37,752.4 μs
439.33 μs
366.86 μs
37,791.4 μs
5
False
6
321,948.5 μs
3,336.86 μs
2,605.20 μs
321,361.0 μs
5
True
0
714.0 μs
12.87 μs
11.41 μs
715.7 μs
5
True
1
1,436.5 μs
33.54 μs
92.37 μs
1,418.8 μs
5
True
2
2,233.7 μs
79.47 μs
230.55 μs
2,232.8 μs
5
True
3
3,056.3 μs
166.89 μs
476.15 μs
3,051.3 μs
5
True
4
3,339.3 μs
105.32 μs
303.88 μs
3,340.5 μs
5
True
5
3,962.7 μs
179.15 μs
508.21 μs
3,862.4 μs
5
True
6
4,496.6 μs
133.87 μs
394.71 μs
4,484.2 μs
10
False
0
747.7 μs
30.51 μs
88.51 μs
719.0 μs
10
False
1
1,211.5 μs
49.81 μs
142.92 μs
1,162.0 μs
10
False
2
2,161.1 μs
88.84 μs
259.14 μs
2,123.4 μs
10
False
3
9,423.3 μs
702.14 μs
2,014.57 μs
9,313.8 μs
10
False
4
90,392.5 μs
821.13 μs
727.91 μs
90,467.2 μs
10
False
5
1,202,652.5 μs
23,336.09 μs
24,969.36 μs
1,205,782.6 μs
10
False
6
34,625,732.4 μs
691,082.68 μs
1,055,356.24 μs
34,718,363.9 μs
10
True
0
747.0 μs
24.88 μs
68.93 μs
738.7 μs
10
True
1
1,712.9 μs
53.74 μs
154.20 μs
1,697.2 μs
10
True
2
2,519.9 μs
107.27 μs
316.28 μs
2,491.5 μs
10
True
3
3,349.0 μs
149.58 μs
436.33 μs
3,295.7 μs
10
True
4
4,268.4 μs
165.83 μs
483.72 μs
4,274.0 μs
10
True
5
4,882.6 μs
188.59 μs
547.13 μs
4,832.2 μs
10
True
6
5,560.8 μs
249.02 μs
726.40 μs
5,478.1 μs
Обратите внимание на выделенные значения. Добавление одной дополнительной коллекции всего с 10 записями (пусть и с относительно большими данными) приводит к деградации в почти 30 раз.
А при использовании разделенных запросов разница уже не так драматична. Да и сами цифры значительно меньше в абсолютных велиичнах (5,5 мс против 34625 мс).
Всем спасибо и следите за комбинаторными взрывами!
Ссылки
===========
Источник:
habr.com
===========
Похожие новости:
- [.NET, C#] Shrinking .NET Console Application (перевод)
- [Open source, .NET, C#] Избавляемся от постоянного написания конструкторов для инжекта зависимостей с помощью C# Source Generators
- [.NET, C#, Программирование микроконтроллеров, Интернет вещей, DIY или Сделай сам] .NET nanoFramework — платформа для разработки приложений на C# для микроконтроллеров
- [C#, Unity] Основы Unity + Mirror
- [.NET, C#] Уменьшить размер консольного .NET 5.0 приложения
- [Разработка веб-сайтов, .NET, Компиляторы, C#, WebAssembly] Ahead-of-Time компиляция и Blazor
- [.NET, Машинное обучение] Посмотрим на девочек? Или ml.net в работе
- [.NET] Нюансы при работе с EF миграциями
- [Ненормальное программирование, Поисковые технологии, Python, Игры и игровые приставки] Однажды Microsoft забанила всю мою страну за читерство (перевод)
- Выпуск среды разработки PascalABC.NET 3.8
Теги для поиска: #_.net, #_c#, #_ef_core, #_.net, #_c#
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:59
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
ORM Entity Framework Core с каждой версией становится все более и более богатой на фичи. Команда разработчиков тратит много времени на перфоманс и вероятно простое обновление Nuget-пакета уже приведет к некоторому бусту, который почувствуют пользователи. Но сегодня я хочу рассказать о совершенно конкретной фиче: это новый режим запросов — "разделённые запросы" или "split queries" в оригинале. Предыстория На моем текущем проекте в качестве хранилища используется Postgres, доступ к которой осуществляется через драйвер Npgsql и EF Core. Одной из центральных сущностей всего бизнес-процесса является "методика выполнения ПЦР-исследования", которая по сути представляет рецепт как выполнить исследование и включает в себя довольно много информации:
Сам объект методики тоже имеет около дюжины полей, в основном небольших — название, описание, версия, и т.д. Большинство коллекций являются связями много-ко-многим с другими справочными записями, которые сами содержат не так много информации, ну может до полудюжины скалярных полей. В самом начале мы не парили себе голову и стали использовать lazy-loading, что хорошо работало в сценариях "частичного применения": когда извлекалась методика только со списком реагентов, или методика только со списком результатов. Но в определенных сценариях требовалось получить методику "целиком", что привело к классической проблеме N+1, когда для извлечения сущности и связанных коллекций требуется 1 запрос на извлечение сущности и еще по одному для каждого элемента коллекции. Такое отношение к данным сильно просаживало производительность и следующим витком было включить коллекции в родительский объект с использованием LINQ-конструкций .Include().ThenInclude(). Всё работало более менее хорошо до появления царь-методики. Это комплексный и сложный тест, в который включено много реагентов, много результатов, он использует много каналов детекции. К ней производится много форм комплектации наборов реагентов. Так или иначе, мы получили развесистую структуру, в которой каждая вложенная коллекция имела не как обычно 3-5 записей, а по 20-30. Вот тут наш софт и сказал "кря". Если раньше на извлечение полной сущности уходило от нескольких десятков миллисекунд до сотен, то у царь-методики это занимало до полутора десятков секунд, что иногда приводило к краху запроса в БД. Это было уже недопустимо и требовало каких-то решений. Пересказ документации Как пишут Майкрософт в своей статье про разделённые запросы обычно каждый LINQ-запрос преобразуется в один SQL запрос с использованием JOIN для извлечения связанных коллекций. Я позволю себе немного перевернуть пример из документации и извлекать не блоги и посты, а посты и комменты. Пусть модель данных выглядит следующим образом: public class Post
{ public int ID { get; set; } public string Content { get; set; } public List<Comment> Comments { get; set; } } public class Comment { public int ID { get; set; } public Post ParentPost { get; set; } public int ParentPostID { get; set; } public string CommentText { get; set; } } И попытаемся извлечь хайповый пост с 1000 комментариями из бд следующим запросом: var veryPopularPost = Posts
.Include(x => x.Comments) .First(x => x.ID == 42); Что будет транслировано примерно в такой SQL-запрос усреднённый, потому что вариаций масса: SELECT p.id, p.content, c.id, c.parentpostid, c.commenttext
FROM posts AS p LEFT JOIN comments AS c ON (p.id = c.parentpostid) WHERE p.id = 42 LIMIT 1; А что если статья большая? Например автор был в ударе и выдал 100 КБ текста (это около 50 печатных листов). Комментаторы читали, наслаждались и комментировали, так что оставили 1000 комментариев. Сколько примерно будет весить результирующий набор, который надо вычитать ORM? На вскидку — p.content = 100 КБ, повторим 1000 раз и на выходе ~ 100 МБ текста, дублирующегося 1000 раз. И это без учета размера int и текста комментариев. Lazy-load в этом случае будет побыстрее, хотя бомбить базу 1000 и одним запросом — тоже сомнительное развлечение. Можно ли что-то с этим сделать не прибегая вручную к оптимизации запросов? AsSplitQuery() Да, и вот каким образом. В EF Core 5.0 появилась новая директива .AsSplitQuery(), которая заставит query provider транслировать загрузку связанной коллекции отдельным запросом. var veryPopularPost = Posts
.Include(x => x.Comments) .AsSplitQuery() .First(x => x.ID == 42); Транслируется в следующие SQL-запросы: SELECT p.id, p.content
FROM posts AS p WHERE p.id = 42 LIMIT 1; SELECT c.id, c.parentpostid, c.commenttext, p.id FROM posts AS p INNER JOIN comments AS c ON (p.id = c.parentpostid) WHERE p.id = 42 LIMIT 1; Что уже приведет к тому, что текст записи не будет читаться 1000 раз, а только 1. Для нескольких коллекций поведение будет аналогичное, что избавит от комбинаторного взрыва. Бенчмарки На скорую руку я сваял бенчмарк. Пусть есть коллекция записей типа MainEntity, в которой нет ничего, кроме вложенных коллекций. 5 "маленьких" записей и 1 большая. public class MainEntity
{ public int ID { get; set; } public List<RefEntity1> Ref1 { get; set; } public List<RefEntity2> Ref2 { get; set; } public List<RefEntity3> Ref3 { get; set; } public List<RefEntity4> Ref4 { get; set; } public List<RefEntity5> Ref5 { get; set; } public List<BigRefEntity> BigRef { get; set; } } public abstract class RefEntity { public int ID { get; set; } public string Payload { get; set; } = string.Empty; public MainEntity MainEntity { get; set; } public int? MainEntityID { get; set; } } public class RefEntity1 : RefEntity { } public class RefEntity2 : RefEntity { } public class RefEntity3 : RefEntity { } public class RefEntity4 : RefEntity { } public class RefEntity5 : RefEntity { } public class BigRefEntity : RefEntity { } Записи заполняются при инициализации БД строчками случайной длины по 10 символов для "маленькой" записи и по 1000 для "большой". В каждую вложенную коллекцию добавляется по ItemsInCollection записей. Тестовый метод извлекает по 2 записи MainEntity, присоединяя к ней от 0 до 6 коллекций (параметр LoadRefs) в двух режимах — одним запросом и разделёнными запросами (параметр SplitQueries). [Benchmark]
public List<MainEntity> QueryLoad() { IQueryable<MainEntity> query = LoadRefs switch { 0 => dbContext.MainEntities, 1 => dbContext.MainEntities .Include(x => x.Ref1), 2 => dbContext.MainEntities .Include(x => x.Ref1) .Include(x => x.Ref2), 3 => dbContext.MainEntities .Include(x => x.Ref1) .Include(x => x.Ref2) .Include(x => x.Ref3), 4 => dbContext.MainEntities .Include(x => x.Ref1) .Include(x => x.Ref2) .Include(x => x.Ref3) .Include(x => x.Ref4), 5 => dbContext.MainEntities .Include(x => x.Ref1) .Include(x => x.Ref2) .Include(x => x.Ref3) .Include(x => x.Ref4) .Include(x => x.Ref5), 6 => dbContext.MainEntities .Include(x => x.Ref1) .Include(x => x.Ref2) .Include(x => x.Ref3) .Include(x => x.Ref4) .Include(x => x.Ref5) .Include(x => x.BigRef), _ => throw new ArgumentOutOfRangeException() }; var splitQuery = SplitQueries ? query.AsSplitQuery() : query; return splitQuery.Take(2).ToList(); } Полный код бенчмарка доступен на гитхабе. Я запускал бенчмарк на домашней машине, СУБД в дефолтной конфигурации, подключение локальное через localhost. BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
AMD Ryzen 5 2400G with Radeon Vega Graphics, 1 CPU, 8 logical and 4 physical cores .NET Core SDK=5.0.201 [Host] : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT [AttachedDebugger] Job-HMJXLI : .NET Core 5.0.4 (CoreCLR 5.0.421.11614, CoreFX 5.0.421.11614), X64 RyuJIT InvocationCount=1 UnrollFactor=1 ItemsInCollection SplitQueries LoadRefs Mean Error StdDev Median 2 False 0 694.6 μs 18.42 μs 52.57 μs 686.9 μs 2 False 1 1,004.3 μs 25.43 μs 69.60 μs 983.4 μs 2 False 2 1,255.3 μs 32.02 μs 89.25 μs 1,237.0 μs 2 False 3 1,578.9 μs 45.46 μs 126.73 μs 1,545.1 μs 2 False 4 2,013.3 μs 56.55 μs 162.26 μs 1,976.8 μs 2 False 5 2,685.2 μs 69.00 μs 196.85 μs 2,651.1 μs 2 False 6 4,646.8 μs 134.52 μs 392.41 μs 4,515.2 μs 2 True 0 726.5 μs 17.60 μs 48.76 μs 725.0 μs 2 True 1 1,403.1 μs 34.46 μs 96.06 μs 1,394.3 μs 2 True 2 1,928.7 μs 57.68 μs 165.51 μs 1,923.3 μs 2 True 3 2,639.6 μs 96.20 μs 277.56 μs 2,584.5 μs 2 True 4 3,128.8 μs 117.46 μs 340.77 μs 3,180.4 μs 2 True 5 3,725.9 μs 121.37 μs 357.87 μs 3,713.8 μs 2 True 6 4,299.9 μs 166.28 μs 485.04 μs 4,233.4 μs 5 False 0 706.6 μs 18.03 μs 50.25 μs 698.9 μs 5 False 1 1,071.6 μs 20.91 μs 51.69 μs 1,068.6 μs 5 False 2 1,512.7 μs 30.13 μs 54.33 μs 1,513.6 μs 5 False 3 2,809.9 μs 148.44 μs 435.35 μs 2,619.9 μs 5 False 4 7,803.3 μs 435.35 μs 1,242.08 μs 7,243.8 μs 5 False 5 37,752.4 μs 439.33 μs 366.86 μs 37,791.4 μs 5 False 6 321,948.5 μs 3,336.86 μs 2,605.20 μs 321,361.0 μs 5 True 0 714.0 μs 12.87 μs 11.41 μs 715.7 μs 5 True 1 1,436.5 μs 33.54 μs 92.37 μs 1,418.8 μs 5 True 2 2,233.7 μs 79.47 μs 230.55 μs 2,232.8 μs 5 True 3 3,056.3 μs 166.89 μs 476.15 μs 3,051.3 μs 5 True 4 3,339.3 μs 105.32 μs 303.88 μs 3,340.5 μs 5 True 5 3,962.7 μs 179.15 μs 508.21 μs 3,862.4 μs 5 True 6 4,496.6 μs 133.87 μs 394.71 μs 4,484.2 μs 10 False 0 747.7 μs 30.51 μs 88.51 μs 719.0 μs 10 False 1 1,211.5 μs 49.81 μs 142.92 μs 1,162.0 μs 10 False 2 2,161.1 μs 88.84 μs 259.14 μs 2,123.4 μs 10 False 3 9,423.3 μs 702.14 μs 2,014.57 μs 9,313.8 μs 10 False 4 90,392.5 μs 821.13 μs 727.91 μs 90,467.2 μs 10 False 5 1,202,652.5 μs 23,336.09 μs 24,969.36 μs 1,205,782.6 μs 10 False 6 34,625,732.4 μs 691,082.68 μs 1,055,356.24 μs 34,718,363.9 μs 10 True 0 747.0 μs 24.88 μs 68.93 μs 738.7 μs 10 True 1 1,712.9 μs 53.74 μs 154.20 μs 1,697.2 μs 10 True 2 2,519.9 μs 107.27 μs 316.28 μs 2,491.5 μs 10 True 3 3,349.0 μs 149.58 μs 436.33 μs 3,295.7 μs 10 True 4 4,268.4 μs 165.83 μs 483.72 μs 4,274.0 μs 10 True 5 4,882.6 μs 188.59 μs 547.13 μs 4,832.2 μs 10 True 6 5,560.8 μs 249.02 μs 726.40 μs 5,478.1 μs Обратите внимание на выделенные значения. Добавление одной дополнительной коллекции всего с 10 записями (пусть и с относительно большими данными) приводит к деградации в почти 30 раз. А при использовании разделенных запросов разница уже не так драматична. Да и сами цифры значительно меньше в абсолютных велиичнах (5,5 мс против 34625 мс). Всем спасибо и следите за комбинаторными взрывами! Ссылки =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:59
Часовой пояс: UTC + 5