Как ускорить процесс записи в базу данных при высокой нагрузке на запись и низкой на чтение?
C#базы данныхзапись данныхоптимизацияпроизводительность
Что проверяют
Понимаете ли вы, из чего складывается стоимость записи: сетевые обращения, коммиты, обслуживание индексов, и что при редком чтении часть этой стоимости можно убрать. Следом спрашивают, что будет с данными, если буфер в памяти приложения пропадёт при падении.
Как отвечать
Идите от приложения к базе:
- пачки вместо строк:
SaveChangesв цикле на каждую запись означает отдельную транзакцию на строку. ОдинAddRangeи одинSaveChangesEF Core отправит пачкой команд, размер регулируетMaxBatchSize. Для потока в тысячи строк нужен массовый импорт:SqlBulkCopyдля SQL Server, бинарныйCOPYчерезBeginBinaryImportв Npgsql для PostgreSQL; - лёгкий EF Core: короткоживущий
DbContextна пачку, чтобы трекер изменений не разрастался. Для массовых изменений без загрузки сущностей естьExecuteUpdateиExecuteDelete; - буфер в приложении: входящие записи складываются в
Channel<T>, аBackgroundServiceсбрасывает их пачкой по размеру или таймеру. Если терять данные нельзя, буфером служит брокер (Kafka, RabbitMQ), а в базу пишет потребитель; - индексы: каждый индекс обновляется при каждой вставке. Раз чтений мало, оставьте только нужные для них и для уникальности;
- ключ: случайный
Guidв кластерном индексе SQL Server вставляет строки в случайные страницы и вызывает их расщепление. Лучшеbigintidentity или GUID, упорядоченный для SQL Server:NEWSEQUENTIALID()или генератор EF Core по умолчанию.Guid.CreateVersion7()из .NET 9 хорош для PostgreSQL, но SQL Server сравнивает GUID по другим байтам; - долговечность коммита:
synchronous_commit = offв PostgreSQL или delayed durability в SQL Server ускоряют коммиты ценой потери последних транзакций при сбое. Решение об этом принимает бизнес.
Где спотыкаются
Называют async/await способом ускорить запись. Асинхронность освобождает потоки приложения, но база от этого быстрее писать не станет.
Поднимают Max Pool Size в строке подключения. Больше соединений означает больше конкуренции за блокировки и журнал, и пропускная способность часто падает.
Предлагают кэш вроде MemoryCache. При редком чтении кэшировать нечего, а запись он не ускоряет.