diff --git a/src/main/java/ru/mcs/controller/BenchmarkController.java b/src/main/java/ru/mcs/controller/BenchmarkController.java index 4a5206f..fa82009 100644 --- a/src/main/java/ru/mcs/controller/BenchmarkController.java +++ b/src/main/java/ru/mcs/controller/BenchmarkController.java @@ -92,15 +92,11 @@ public ResponseEntity runSelectBenchmark( @Parameter(description = "ID организации", example = "1") @RequestParam(defaultValue = "1") Integer orgId, - @Parameter(description = "Имя объекта", example = "Contact") @RequestParam(defaultValue = "Contact") String object, - @Parameter(description = "Количество итераций", example = "10") @RequestParam(defaultValue = "10") int iterations) { - - BenchmarkResult result = benchmarkService.runSelectBenchmark(orgId, object, iterations); - return ResponseEntity.ok(result); + return ResponseEntity.ok(benchmarkService.runSelectBenchmark(orgId, object, iterations)); } @Operation( @@ -127,4 +123,26 @@ @RequestParam(defaultValue = "10000") int count) { return ResponseEntity.ok(benchmarkService.runNativeInsertBenchmark(orgId, object, count)); } + + + @Operation(summary = "COUNT бенчмарк") + @PostMapping("/count-benchmark") + public ResponseEntity runCountBenchmark( + @Parameter(description = "ID организации", example = "1") + @RequestParam(defaultValue = "1") Integer orgId, + @Parameter(description = "Имя объекта", example = "Contact") + @RequestParam(defaultValue = "Contact") String object, + @Parameter(description = "Количество итераций", example = "10") + @RequestParam(defaultValue = "10") int iterations) { + return ResponseEntity.ok(benchmarkService.runCountBenchmark(orgId, object, iterations)); + } + + @Operation(summary = "SELECT бенчмарк (Native lightweight)") + @PostMapping("/select-native") + public ResponseEntity runLightweightSelectBenchmark( + @RequestParam(defaultValue = "1") Integer orgId, + @RequestParam(defaultValue = "Contact") String object, + @RequestParam(defaultValue = "10") int iterations) { + return ResponseEntity.ok(benchmarkService.runLightweightSelectBenchmark(orgId, object, iterations)); + } } diff --git a/src/main/java/ru/mcs/dto/DataRowDto.java b/src/main/java/ru/mcs/dto/DataRowDto.java new file mode 100644 index 0000000..aa5db7d --- /dev/null +++ b/src/main/java/ru/mcs/dto/DataRowDto.java @@ -0,0 +1,15 @@ +package ru.mcs.dto; + +import java.util.UUID; + +public record DataRowDto( + Integer orgId, + UUID guid, + Integer objId, + String name, + String value0, + String value1, + String value2, + String value3, + String value4 +) {} diff --git a/src/main/java/ru/mcs/repository/MtDataRepository.java b/src/main/java/ru/mcs/repository/MtDataRepository.java index c99aba2..0139b01 100644 --- a/src/main/java/ru/mcs/repository/MtDataRepository.java +++ b/src/main/java/ru/mcs/repository/MtDataRepository.java @@ -31,4 +31,14 @@ @Modifying @Query("UPDATE MtData d SET d.isDeleted = true, d.deletedAt = CURRENT_TIMESTAMP WHERE d.id.orgId = :orgId AND d.id.guid = :guid") void softDelete(@Param("orgId") Integer orgId, @Param("guid") UUID guid); + + @Query(value = """ + SELECT org_id, guid, obj_id, name, value0, value1, value2, value3, value4 + FROM mt_data + WHERE org_id = :orgId AND obj_id = :objId AND is_deleted = false + LIMIT :limit + """, nativeQuery = true) + List findLightweight(@Param("orgId") Integer orgId, + @Param("objId") Integer objId, + @Param("limit") int limit); } diff --git a/src/main/java/ru/mcs/service/BenchmarkService.java b/src/main/java/ru/mcs/service/BenchmarkService.java index 6698464..0269850 100644 --- a/src/main/java/ru/mcs/service/BenchmarkService.java +++ b/src/main/java/ru/mcs/service/BenchmarkService.java @@ -2,8 +2,8 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; -import ru.mcs.config.PivotProperties; import ru.mcs.dto.BenchmarkResult; import ru.mcs.entity.MtObject; import ru.mcs.repository.MtDataRepository; @@ -17,7 +17,6 @@ private final NativeBatchInsertService nativeBatchService; private final MetadataService metadataService; private final MtDataRepository dataRepository; - private final PivotProperties properties; public BenchmarkResult runInsertBenchmark(Integer orgId, String objectName, int recordCount) { MtObject obj = metadataService.getObjectByName(orgId, objectName); @@ -28,35 +27,31 @@ int generated = generatorService.generateData(orgId, obj.getObjId(), recordCount); long durationMs = System.currentTimeMillis() - startTime; - double recordsPerSecond = (double) generated * 1000 / durationMs; - return BenchmarkResult.builder() .operation("INSERT_JPA") .objectName(objectName) .recordCount(generated) .durationMs(durationMs) - .recordsPerSecond(recordsPerSecond) + .recordsPerSecond((double) generated * 1000 / durationMs) .build(); } public BenchmarkResult runNativeInsertBenchmark(Integer orgId, String objectName, int recordCount) { MtObject obj = metadataService.getObjectByName(orgId, objectName); - log.info("Starting INSERT benchmark (Native JDBC): count={}", recordCount); + log.info("Starting INSERT benchmark (Native): count={}", recordCount); try { long startTime = System.currentTimeMillis(); int generated = nativeBatchService.batchInsert(orgId, obj.getObjId(), recordCount, 5000); long durationMs = System.currentTimeMillis() - startTime; - double recordsPerSecond = (double) generated * 1000 / durationMs; - return BenchmarkResult.builder() .operation("INSERT_NATIVE") .objectName(objectName) .recordCount(generated) .durationMs(durationMs) - .recordsPerSecond(recordsPerSecond) + .recordsPerSecond((double) generated * 1000 / durationMs) .build(); } catch (Exception e) { log.error("Native insert failed", e); @@ -66,27 +61,57 @@ public BenchmarkResult runSelectBenchmark(Integer orgId, String objectName, int iterations) { MtObject obj = metadataService.getObjectByName(orgId, objectName); - int totalRecords = 0; - // Warmup + log.info("Starting SELECT benchmark: iterations={}", iterations); + + // Warmup с пагинацией for (int i = 0; i < 3; i++) { - dataRepository.findByOrgIdAndObjId(orgId, obj.getObjId()); + dataRepository.findByOrgIdAndObjIdPaged(orgId, obj.getObjId(), PageRequest.of(0, 1000)); } + long totalRecords = dataRepository.countByOrgIdAndObjId(orgId, obj.getObjId()); + + // Тест SELECT с LIMIT 1000 long startTime = System.currentTimeMillis(); for (int i = 0; i < iterations; i++) { - var records = dataRepository.findByOrgIdAndObjId(orgId, obj.getObjId()); - totalRecords = records.size(); + dataRepository.findByOrgIdAndObjIdPaged(orgId, obj.getObjId(), PageRequest.of(0, 1000)); } long durationMs = System.currentTimeMillis() - startTime; long avgDuration = durationMs / iterations; return BenchmarkResult.builder() - .operation("SELECT") + .operation("SELECT_PAGED_1000") .objectName(objectName) - .recordCount(totalRecords) + .recordCount((int) totalRecords) .durationMs(avgDuration) - .recordsPerSecond(totalRecords > 0 ? (double) totalRecords * 1000 / avgDuration : 0) + .recordsPerSecond(avgDuration > 0 ? 1000.0 * 1000 / avgDuration : 0) + .iterations(iterations) + .build(); + } + + public BenchmarkResult runCountBenchmark(Integer orgId, String objectName, int iterations) { + MtObject obj = metadataService.getObjectByName(orgId, objectName); + + log.info("Starting COUNT benchmark: iterations={}", iterations); + + // Warmup + for (int i = 0; i < 3; i++) { + dataRepository.countByOrgIdAndObjId(orgId, obj.getObjId()); + } + + long startTime = System.currentTimeMillis(); + long count = 0; + for (int i = 0; i < iterations; i++) { + count = dataRepository.countByOrgIdAndObjId(orgId, obj.getObjId()); + } + long durationMs = System.currentTimeMillis() - startTime; + long avgDuration = durationMs / iterations; + + return BenchmarkResult.builder() + .operation("COUNT") + .objectName(objectName) + .recordCount((int) count) + .durationMs(avgDuration) .iterations(iterations) .build(); } @@ -95,4 +120,30 @@ MtObject obj = metadataService.getObjectByName(orgId, objectName); return dataRepository.countByOrgIdAndObjId(orgId, obj.getObjId()); } + + public BenchmarkResult runLightweightSelectBenchmark(Integer orgId, String objectName, int iterations) { + MtObject obj = metadataService.getObjectByName(orgId, objectName); + + // Warmup + for (int i = 0; i < 3; i++) { + dataRepository.findLightweight(orgId, obj.getObjId(), 1000); + } + + long startTime = System.currentTimeMillis(); + int rows = 0; + for (int i = 0; i < iterations; i++) { + rows = dataRepository.findLightweight(orgId, obj.getObjId(), 1000).size(); + } + long durationMs = System.currentTimeMillis() - startTime; + long avgDuration = durationMs / iterations; + + return BenchmarkResult.builder() + .operation("SELECT_NATIVE_1000") + .objectName(objectName) + .recordCount(rows) + .durationMs(avgDuration) + .recordsPerSecond(avgDuration > 0 ? (double) rows * 1000 / avgDuration : 0) + .iterations(iterations) + .build(); + } }