У меня возникают серьезные проблемы с производительностью при выполнении операции «upsert» в таблице SQL Server с использованием JdbcTemplate.batchUpdate Spring Boot в моем приложении Springboot 3.2.5 на Java 17.
У меня есть таблицаfund_return с кластерным уникальным индексом по 5 столбцам. Я использую оператор MERGE для обновления существующих записей или вставки новых. Схема таблицы:
DB_URL = jdbc:sqlserver://IP_ADDRESS:PORT;databaseName=dbNAme;trustServerCerftificate=true
(I added sendStringParametersAsUnicode=false but it did not change anything.)
INFO --- c.i.b.m.f.s.FundPriceTransferService : Saving 14088 returns...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Returns saved in 28695ms
...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Saving 22000 returns...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Returns saved in 80000ms
Хотя небольшие списки обрабатываются быстро, большие наборы данных значительно ухудшаются.
При обработке около 14 088 записей операция занимает почти 29 секунд (28 695 мс).
В недавнем запуске отладки с ~22 000 записей процесс занял 80 секунд.
Я запустил план выполнения для одной строки, и он показывает Поиск по кластерному индексу, поэтому индексы, похоже, работают правильно. STATISTICS IO показывает только 11 логических чтений на запись.
Однако, несмотря на то, что я использую пакетное обновление, оператор MERGE выполняется построчно (я не уверен, что даже эта логика должна занимать такое время) на стороне ядра базы данных, что приводит к линейному увеличению времени.
Как улучшить эту операцию сохранения? Я испробовал все предложения ИИ, но существенных улучшений не добился.
У меня возникают серьезные проблемы с производительностью при выполнении операции «upsert» в таблице SQL Server с использованием JdbcTemplate.batchUpdate Spring Boot в моем приложении Springboot 3.2.5 на Java 17. У меня есть таблицаfund_return с кластерным уникальным индексом по 5 столбцам. Я использую оператор MERGE для обновления существующих записей или вставки новых. [b]Схема таблицы:[/b] [code]CREATE TABLE fund_return ( id int IDENTITY(1,1) NOT NULL, fund_code varchar(8) NOT NULL, currency varchar(3) NOT NULL, data_date date NOT NULL, period varchar(16) NOT NULL, holiday_type varchar(32) DEFAULT 'NextBusinessDay' NOT NULL, fund_return numeric(30,15) NULL, deposit_return_ann numeric(30,15) NULL, fund_benchmark_return numeric(30,15) NULL, modif_time datetime NULL, CONSTRAINT pk_fund_return PRIMARY KEY (id), CONSTRAINT uc_fund_return_unique UNIQUE CLUSTERED (fund_code, currency, data_date, period, holiday_type) ); [/code] [b]Код Java:[/b] Я использую JdbcTemplate.batchUpdate с размером пакета 1000. [code]private static final String UPSERT_SQL = """ MERGE INTO fund_return AS t USING (VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)) AS s (fund_code, currency, data_date, period, holiday_type, fund_return, deposit_return_ann, fund_benchmark_return, modif_time) ON t.fund_code = s.fund_code AND t.currency = s.currency AND t.data_date = s.data_date AND t.period = s.period AND t.holiday_type = s.holiday_type WHEN MATCHED THEN UPDATE SET fund_return = s.fund_return, deposit_return_ann = s.deposit_return_ann, fund_benchmark_return = s.fund_benchmark_return, modif_time = s.modif_time WHEN NOT MATCHED THEN INSERT (fund_code, currency, data_date, period, holiday_type, fund_return, deposit_return_ann, fund_benchmark_return, modif_time) VALUES (s.fund_code, s.currency, s.data_date, s.period, s.holiday_type, s.fund_return, s.deposit_return_ann, s.fund_benchmark_return, s.modif_time); """;
fundReturnService.batchUpsert(returns); // Calls the method above
log.info("Returns saved in {}ms", System.currentTimeMillis() - saveStart); [/code] Я использую зависимость mssql-jdbc без информации о версии. [code]DB_URL = jdbc:sqlserver://IP_ADDRESS:PORT;databaseName=dbNAme;trustServerCerftificate=true
(I added sendStringParametersAsUnicode=false but it did not change anything.)
INFO --- c.i.b.m.f.s.FundPriceTransferService : Saving 14088 returns...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Returns saved in 28695ms
...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Saving 22000 returns...
INFO --- c.i.b.m.f.s.FundPriceTransferService : Returns saved in 80000ms [/code] Хотя небольшие списки обрабатываются быстро, большие наборы данных значительно ухудшаются. [list] [*]При обработке около [b]14 088 записей[/b] операция занимает почти [b]29 секунд[/b] (28 695 мс).
[*]В недавнем запуске отладки с [b]~22 000 записей[/b] процесс занял [b]80 секунд[/b].
[/list] Я запустил план выполнения для одной строки, и он показывает [b]Поиск по кластерному индексу[/b], поэтому индексы, похоже, работают правильно. STATISTICS IO показывает только 11 логических чтений на запись. Однако, несмотря на то, что я использую пакетное обновление, оператор MERGE выполняется построчно (я не уверен, что даже эта логика должна занимать такое время) на стороне ядра базы данных, что приводит к линейному увеличению времени. Как улучшить эту операцию сохранения? Я испробовал все предложения ИИ, но существенных улучшений не добился.