TG Telegram Group & Channel
.NET Разработчик | United States America (US)
Create: Update:

День 2278. #ЗаметкиНаПолях
Разбираем Курсорную Пагинацию. Окончание

Начало
Продолжение

Улучшаем курсорную пагинацию
Для ускорения пагинации можно добавить индекс:

CREATE INDEX idx_user_notes_date_id ON user_notes (date DESC, id DESC);

Индекс создаётся в обратном порядке, чтобы соответствовать порядку в запросах. Однако, если выполнить план запроса, мы увидим, что индекс используется, но запрос может выполняться даже дольше, чем без него:
EXPLAIN ANALYZE SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
WHERE u.date < @date OR (u.date = @date AND u.id <= @lastId)
ORDER BY u.date DESC, u.id DESC
LIMIT 1000;

Возможно, размер данных слишком мал, чтобы получить преимущество от индекса. Но мы можем использовать один трюк – сравнение кортежей:
SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
WHERE (u.date, u.id) <= (@date, @lastId)
ORDER BY u.date DESC, u.id DESC
LIMIT 1000;

В этом случае индекс сработает, серьёзно ускорив выполнение. Оптимизатор запросов не может определить, можно ли использовать составной индекс для сравнения на уровне строк. Однако индекс эффективно используется при сравнении кортежей.

У провайдера EF для Postgres есть метод EF.Functions.LessThanOrEqual, который принимает ValueTuple в качестве аргумента. Мы можем использовать его для создания сравнения кортежей:
query = query.Where(x => EF.Functions.LessThanOrEqual(
ValueTuple.Create(x.Date, x.Id),
ValueTuple.Create(date, lastId)));


Кодирование курсора
Мы можем закодировать курсор, используемый для извлечения следующего набора результатов. Клиенты получат курсор в виде строки Base64. Им не нужно знать внутреннюю структуру курсора:
using System.Buffers.Text;
using System.Text;
using System.Text.Json;

public record Cursor(DateOnly Date, Guid LastId)
{
public string Encode() =>
Base64Url.EncodeToString(
Encoding.UTF8.GetBytes(
JsonSerializer.Serialize(this)));

public static Cursor? Decode(string? cursor)
{
if (string.IsNullOrWhiteSpace(cursor))
return null;

try
{
return JsonSerializer.Deserialize<Cursor>(
Encoding.UTF8.GetString(
Base64Url.DecodeFromChars(cursor)));
}
catch
{
return null;
}
}
}

Вот пример использования:
var cursor = new Cursor(new DateOnly(2025, 4, 26),
Guid.Parse("019500f9-8b41-74cf-ab12-25a48d4d4ab4"));

var encoded = cursor.Encode();
// eyJEYXRlIjoiMjAyNS0wMi0xNSIsIkxhc3RJZCI6IjAxOTUwMGY5LThiNDEtNzRjZi1hYjEyLTI1YTQ4ZDRkNGFiNCJ9

var decoded = Cursor.Decode(encoded);


Источник: https://www.milanjovanovic.tech/blog/understanding-cursor-pagination-and-why-its-so-fast-deep-dive

День 2278. #ЗаметкиНаПолях
Разбираем Курсорную Пагинацию. Окончание

Начало
Продолжение

Улучшаем курсорную пагинацию
Для ускорения пагинации можно добавить индекс:
CREATE INDEX idx_user_notes_date_id ON user_notes (date DESC, id DESC);

Индекс создаётся в обратном порядке, чтобы соответствовать порядку в запросах. Однако, если выполнить план запроса, мы увидим, что индекс используется, но запрос может выполняться даже дольше, чем без него:
EXPLAIN ANALYZE SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
WHERE u.date < @date OR (u.date = @date AND u.id <= @lastId)
ORDER BY u.date DESC, u.id DESC
LIMIT 1000;

Возможно, размер данных слишком мал, чтобы получить преимущество от индекса. Но мы можем использовать один трюк – сравнение кортежей:
SELECT u.id, u.date, u.note, u.user_id
FROM user_notes AS u
WHERE (u.date, u.id) <= (@date, @lastId)
ORDER BY u.date DESC, u.id DESC
LIMIT 1000;

В этом случае индекс сработает, серьёзно ускорив выполнение. Оптимизатор запросов не может определить, можно ли использовать составной индекс для сравнения на уровне строк. Однако индекс эффективно используется при сравнении кортежей.

У провайдера EF для Postgres есть метод EF.Functions.LessThanOrEqual, который принимает ValueTuple в качестве аргумента. Мы можем использовать его для создания сравнения кортежей:
query = query.Where(x => EF.Functions.LessThanOrEqual(
ValueTuple.Create(x.Date, x.Id),
ValueTuple.Create(date, lastId)));


Кодирование курсора
Мы можем закодировать курсор, используемый для извлечения следующего набора результатов. Клиенты получат курсор в виде строки Base64. Им не нужно знать внутреннюю структуру курсора:
using System.Buffers.Text;
using System.Text;
using System.Text.Json;

public record Cursor(DateOnly Date, Guid LastId)
{
public string Encode() =>
Base64Url.EncodeToString(
Encoding.UTF8.GetBytes(
JsonSerializer.Serialize(this)));

public static Cursor? Decode(string? cursor)
{
if (string.IsNullOrWhiteSpace(cursor))
return null;

try
{
return JsonSerializer.Deserialize<Cursor>(
Encoding.UTF8.GetString(
Base64Url.DecodeFromChars(cursor)));
}
catch
{
return null;
}
}
}

Вот пример использования:
var cursor = new Cursor(new DateOnly(2025, 4, 26),
Guid.Parse("019500f9-8b41-74cf-ab12-25a48d4d4ab4"));

var encoded = cursor.Encode();
// eyJEYXRlIjoiMjAyNS0wMi0xNSIsIkxhc3RJZCI6IjAxOTUwMGY5LThiNDEtNzRjZi1hYjEyLTI1YTQ4ZDRkNGFiNCJ9

var decoded = Cursor.Decode(encoded);


Источник: https://www.milanjovanovic.tech/blog/understanding-cursor-pagination-and-why-its-so-fast-deep-dive
👍20


>>Click here to continue<<

.NET Разработчик




Share with your best friend
VIEW MORE

United States America Popular Telegram Group (US)