Низкая производительность при использовании SQL Server MERGE с использованием Spring JdbcTemplate patchUpdate для ~ 14 тJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Низкая производительность при использовании SQL Server MERGE с использованием Spring JdbcTemplate patchUpdate для ~ 14 т

Сообщение Anonymous »

У меня возникают серьезные проблемы с производительностью при выполнении операции «upsert» в таблице SQL Server с использованием JdbcTemplate.batchUpdate Spring Boot в моем приложении Springboot 3.2.5 на Java 17.
У меня есть таблицаfund_return с кластерным уникальным индексом по 5 столбцам. Я использую оператор MERGE для обновления существующих записей или вставки новых.
Схема таблицы:

Код: Выделить всё

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)
);
Код Java: Я использую JdbcTemplate.batchUpdate с размером пакета 1000.

Код: Выделить всё

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);
""";

public void batchUpsert(List returns) {
jdbcTemplate.batchUpdate(UPSERT_SQL, returns, 1000, (ps, r) -> {
ps.setString(1, r.getFundCode());
ps.setString(2, r.getCurrency());
ps.setDate(3, java.sql.Date.valueOf(r.getDataDate()));
ps.setString(4, r.getPeriod());
ps.setString(5, r.getHolidayType());
ps.setBigDecimal(6, r.getFundReturn());
ps.setBigDecimal(7, r.getDepositReturnAnn());
ps.setBigDecimal(8, r.getFundBenchmarkReturn());
ps.setTimestamp(9, new Timestamp(System.currentTimeMillis()));
});
}
2. Служба звонков

Код: Выделить всё

log.info("Saving {} returns...", returns.size());

long saveStart = System.currentTimeMillis();

fundReturnService.batchUpsert(returns); // Calls the method above

log.info("Returns saved in {}ms", System.currentTimeMillis() - saveStart);
Я использую зависимость mssql-jdbc без информации о версии.

Код: Выделить всё

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 выполняется построчно (я не уверен, что даже эта логика должна занимать такое время) на стороне ядра базы данных, что приводит к линейному увеличению времени.
Как улучшить эту операцию сохранения? Я испробовал все предложения ИИ, но существенных улучшений не добился.

Подробнее здесь: https://stackoverflow.com/questions/798 ... update-for
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»