Performance tuning - Spring Boot application’ni tezroq, barqarorroq va kamroq resurs bilan ishlaydigan qilish jarayoni. Bu blok quyidagilarni qamrab oladi: Spring Boot startup time optimization, lazy bean initialization, HikariCP tuning, N+1 detection, bulk operations, batch inserts, @Async thread pool configuration.
1. Performance tuning nimadan boshlanadi?
Performance tuning - "hamma joyga cache qo‘shish" emas.
To‘g‘ri yondashuv:
Measure → Find bottleneck → Fix → Measure againYa’ni:
1. O‘lcha
2. Qayer sekinligini top
3. Faqat o‘sha joyni tuzat
4. Natijani qayta o‘lchaYomon yondashuv:
App sekin ekan → cache qo‘shamiz
App sekin ekan → WebFlux qilamiz
App sekin ekan → server kattalashtiramizBu ko‘pincha muammoni yashiradi, hal qilmaydi.
2. Performance muammo qayerda bo‘lishi mumkin?
Spring Boot application’da bottleneck odatda quyidagi joylarda bo‘ladi:
Joy | Muammo |
|---|---|
Startup | App juda sekin start bo‘ladi |
Bean initialization | Keraksiz bean’lar startup’da yaratiladi |
Database | Query sekin, index yo‘q, N+1 bor |
JPA/Hibernate | Lazy/Eager noto‘g‘ri, entity graph yo‘q |
Connection pool | HikariCP noto‘g‘ri sozlangan |
HTTP client | Timeout yo‘q, threadlar band |
Serialization | Katta JSON, ortiqcha field’lar |
Async processing | Thread pool noto‘g‘ri |
Logging | Juda ko‘p sync log yozish |
Memory | Object allocation ko‘p, GC bosimi yuqori |
3. Spring Boot startup time optimization
Muammo
Ba’zi Spring Boot loyihalar 30-90 sekundda start bo‘ladi.
Sabablar:
- Juda ko‘p dependency
- Keraksiz auto-configuration
- Startup’da DB/API chaqirish
- @PostConstruct ichida og‘ir logic
- Migration juda ko‘p vaqt oladi
- Bean initialization haddan tashqari ko‘p
- Component scan katta package’larni skan qiladiStartup vaqtini o‘lchash
Spring Boot’da ApplicationStartup orqali startup step’larni ko‘rish mumkin.
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
app.setApplicationStartup(new BufferingApplicationStartup(2048));
app.run(args);
}
}Actuator bilan startup endpoint ochiladi:
management:
endpoints:
web:
exposure:
include: startup,health,infoKeyin:
GET /actuator/startupBu qaysi bean yoki auto-config qancha vaqt olganini ko‘rishga yordam beradi.
Startup’ni tezlashtirish yo‘llari
1. Keraksiz dependency’larni olib tashlash
Yomon:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>Agar kerak bo‘lmasa, ikkala web stack’ni birga qo‘shmang.
2. Component scan’ni cheklash
Yomon:
@SpringBootApplication(scanBasePackages = "com")Bu juda katta scope.
Yaxshi:
@SpringBootApplication(scanBasePackages = "uz.company.orderservice")Yoki eng yaxshisi - main class root package’da bo‘lsin:
uz.company.orderservice
├── OrderServiceApplication.java
├── controller
├── service
├── repository3. Startup’da external call qilmaslik
Yomon:
@PostConstruct
public void init() {
bankClient.loadRates();
externalApi.warmUp();
reportService.generateCache();
}Bu app startini sekinlashtiradi.
Yaxshi:
@EventListener(ApplicationReadyEvent.class)
public void warmUpAfterStartup() {
cacheWarmupService.warmUpAsync();
}Yoki alohida background job:
@Scheduled(fixedDelay = 300000)
public void refreshCache() {
cacheService.refresh();
}4. Lazy bean initialization
Nima?
Default holatda Spring singleton bean’larni startup paytida yaratadi.
App start
↓
Controller bean
Service bean
Repository bean
Client bean
Mapper bean
...Lazy initialization yoqilsa, bean kerak bo‘lganda yaratiladi.
spring:
main:
lazy-initialization: trueFoydasi
Startup tezlashadi
Kam ishlatiladigan bean’lar keyin yaratiladi
Test startup tezlashishi mumkinXavfi
Lazy initialization xatoni startup’da emas, runtime’da chiqarishi mumkin.
Masalan:
PaymentClient noto‘g‘ri config qilinganLazy bo‘lmasa:
App start paytida xato beradiLazy bo‘lsa:
Birinchi payment request kelganda xato beradiShuning uchun production’da ehtiyotkorlik kerak.
Qachon ishlatish mumkin?
Holat | Lazy init |
|---|---|
Local dev | Foydali |
Test environment | Foydali |
Production critical service | Ehtiyotkorlik bilan |
Rarely used admin bean | Mos |
Payment/security core bean | Tavsiya qilinmaydi |
5. HikariCP tuning
Spring Boot’da JDBC connection pool sifatida odatda HikariCP ishlatiladi.
Connection pool nima?
Har bir DB query uchun yangi connection ochish qimmat.
Yomon:
Request keldi
↓
DB connection ochildi
↓
Query ishladi
↓
Connection yopildiYaxshi:
Connection pool oldindan connection saqlab turadi
Request kelganda tayyor connection beradi
Ish tugasa connection pool’ga qaytaradiHikariCP asosiy config
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000Tushuntirish:
Config | Ma’nosi |
|---|---|
| Eng ko‘p connection soni |
| Bo‘sh turgan minimal connection |
| Connection kutish vaqti |
| Ishlatilmayotgan connection qancha yashaydi |
| Connection maksimal umri |
maximum-pool-sizeni noto‘g‘ri oshirish
Ko‘p developer o‘ylaydi:
Pool size 10 sekin → 100 qilamizBu har doim yaxshi emas.
Agar DB 100 connection’ni ko‘tara olmasa:
App tezlashmaydi
DB yanada sekinlashadi
Lock ko‘payadi
CPU oshadiPool size tanlash
Oddiy yondashuv:
maximum-pool-size = CPU core × 2 yoki workload’ga qarab 10-30Lekin real production’da quyidagilar bilan o‘lchanadi:
- DB CPU
- query latency
- active connections
- pending threads
- connection acquisition time
- p95/p99 response timeHikariCP’da kuzatiladigan metrikalar
Actuator + Micrometer bilan:
hikaricp.connections.active
hikaricp.connections.idle
hikaricp.connections.pending
hikaricp.connections.timeoutAgar pending ko‘paysa:
Threadlar connection kutyaptiSabablar:
- Pool kichik
- Query sekin
- Transaction juda uzoq
- Connection leak bor6. Transaction performance
Eng katta xato: transaction ichida external call
Yomon:
@Transactional
public void createOrder(CreateOrderRequest request) {
Order order = orderRepository.save(new Order(request));
paymentClient.pay(order.getId()); // external HTTP call
order.markPaid();
}Bu xavfli.
Nima bo‘ladi?
Transaction ochiladi
DB connection olinadi
External payment API kutiladi
DB connection band turadiAgar payment API 5 sekund kutsa:
DB connection ham 5 sekund bandYaxshi:
@Transactional
public Order createOrder(CreateOrderRequest request) {
return orderRepository.save(new Order(request));
}
public void processPayment(Long orderId) {
paymentClient.pay(orderId);
}Yoki outbox pattern:
Order yaratildi
↓
Outbox event yozildi
↓
Background worker payment jarayonini boshlaydi7. N+1 problem
N+1 nima?
Masalan, 100 ta order olish kerak.
List<Order> orders = orderRepository.findAll();
for (Order order : orders) {
System.out.println(order.getUser().getName());
}Agar user lazy bo‘lsa, quyidagicha bo‘lishi mumkin:
SELECT * FROM orders; -- 1 query
SELECT * FROM users WHERE id = 1;
SELECT * FROM users WHERE id = 2;
SELECT * FROM users WHERE id = 3;
...
SELECT * FROM users WHERE id = 100;Natija:
1 + 100 = 101 queryShuning uchun nomi N+1.
N+1 qanday aniqlanadi?
1. SQL log yoqish
logging:
level:
org.hibernate.SQL: DEBUG2. Hibernate statistics
spring:
jpa:
properties:
hibernate:
generate_statistics: true3. p6spy yoki datasource-proxy
Bu production’ga yaqin query monitoring uchun foydali.
4. APM
New Relic
Datadog
Elastic APM
Grafana Tempo + metricsN+1 fix: JOIN FETCH
@Query("""
select o from Order o
join fetch o.user
where o.status = :status
""")
List<Order> findAllByStatusWithUser(OrderStatus status);Bu bitta query bilan order va user’ni olib keladi.
N+1 fix: @EntityGraph
@EntityGraph(attributePaths = {"user"})
List<Order> findByStatus(OrderStatus status);Bu Spring Data JPA’da qulayroq.
N+1 fix: DTO projection
Ko‘pincha eng yaxshi yechim - entity emas, kerakli DTO’ni olish.
@Query("""
select new uz.company.dto.OrderListItemDto(
o.id,
o.status,
u.name
)
from Order o
join o.user u
where o.status = :status
""")
List<OrderListItemDto> findOrderListItems(OrderStatus status);Foydasi:
Kamroq column
Kamroq memory
Lazy loading muammosi yo‘q
API response uchun to‘g‘ri format8. EAGER vs LAZY performance
EAGER xavfi
@ManyToOne(fetch = FetchType.EAGER)
private User user;EAGER har doim bog‘liq entity’ni olib kelishga harakat qiladi.
Muammo:
Order kerak edi
User ham keldi
User profile ham keldi
Role ham keldi
Permission ham keldiNatija:
Katta query
Ko‘p memory
Sekin responseTavsiya
Ko‘p holatda:
@ManyToOne(fetch = FetchType.LAZY)
@OneToMany(fetch = FetchType.LAZY)
@ManyToMany(fetch = FetchType.LAZY)Keyin kerak joyda:
JOIN FETCH
@EntityGraph
DTO projectionishlatiladi.
9. Bulk operations
Muammo
1000 ta order statusini update qilish kerak.
Yomon:
List<Order> orders = orderRepository.findAllByStatus(OrderStatus.NEW);
for (Order order : orders) {
order.setStatus(OrderStatus.EXPIRED);
}
orderRepository.saveAll(orders);Bu ko‘p entity load qiladi, dirty checking ishlaydi, memory ishlatadi.
Yaxshi: bulk update
@Modifying
@Query("""
update Order o
set o.status = :newStatus
where o.status = :oldStatus
""")
int updateStatus(
@Param("oldStatus") OrderStatus oldStatus,
@Param("newStatus") OrderStatus newStatus
);Service:
@Transactional
public int expireOrders() {
return orderRepository.updateStatus(OrderStatus.NEW, OrderStatus.EXPIRED);
}Bu bitta SQL query:
UPDATE orders
SET status = 'EXPIRED'
WHERE status = 'NEW';Bulk operation xavfi
Bulk update persistence context’ni chetlab o‘tadi.
Ya’ni oldin load qilingan entity eski holatda qolishi mumkin.
Shuning uchun kerak bo‘lsa:
@Modifying(clearAutomatically = true, flushAutomatically = true)ishlatiladi.
10. Batch inserts
Muammo
Ko‘p row insert qilish:
for (Product product : products) {
productRepository.save(product);
}Bu juda ko‘p insert yuborishi mumkin.
Hibernate batch config
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50
order_inserts: true
order_updates: trueTushuntirish:
Config | Ma’nosi |
|---|---|
| 50 ta insert/update bir batch |
| Insertlarni entity type bo‘yicha tartiblaydi |
| Updatelarni tartiblaydi |
ID generation muhim
Hibernate batch insert GenerationType.IDENTITY bilan ko‘pincha yomon ishlaydi, chunki DB har insertdan keyin ID qaytaradi.
Yaxshiroq:
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;PostgreSQL uchun sequence generator:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(
name = "product_seq",
sequenceName = "product_seq",
allocationSize = 50
)
private Long id;Bu batch insert uchun ancha mos.
Juda katta importlarda flush/clear
@Transactional
public void importProducts(List<Product> products) {
for (int i = 0; i < products.size(); i++) {
entityManager.persist(products.get(i));
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear();
}
}
}Bu persistence context haddan tashqari kattalashib ketmasligi uchun kerak.
11. Pagination performance
OFFSET pagination muammosi
SELECT *
FROM orders
ORDER BY created_at DESC
LIMIT 20 OFFSET 100000;DB 100 000 row’ni o‘tkazib yuborib, keyingi 20 tasini beradi.
Katta jadvalda bu sekinlashadi.
Keyset pagination
Yaxshi:
SELECT *
FROM orders
WHERE created_at < '2026-05-21 10:00:00'
ORDER BY created_at DESC
LIMIT 20;API:
GET /orders?cursor=2026-05-21T10:00:00&pageSize=20Repository:
@Query("""
select o from Order o
where o.createdAt < :cursor
order by o.createdAt desc
""")
List<Order> findNextPage(
@Param("cursor") Instant cursor,
Pageable pageable
);Senior darajada katta dataset uchun OFFSET o‘rniga cursor/keyset pagination o‘ylash kerak.
12. @Async thread pool configuration
Muammo
@Async ishlatildi, lekin thread pool sozlanmadi.
Yomon:
@Async
public void sendEmail() {
emailClient.send();
}Default executor production uchun yetarli bo‘lmasligi mumkin.
To‘g‘ri executor config
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "notificationExecutor")
public Executor notificationExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(30);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("notification-");
executor.initialize();
return executor;
}
}Ishlatish:
@Async("notificationExecutor")
public void sendNotification(NotificationRequest request) {
notificationClient.send(request);
}Thread pool parametrlari
Parametr | Ma’nosi |
|---|---|
| Doimiy threadlar soni |
| Eng ko‘p threadlar soni |
| Navbat hajmi |
| Loglarda ajratish uchun nom |
Eng katta xato: cheksiz queue
Agar queue juda katta bo‘lsa:
Requestlar yig‘iladi
Memory oshadi
Latency oshadi
App sekin o‘ladiYaxshi yondashuv:
Queue cheklangan bo‘lsin
Reject policy aniq bo‘lsin
Metrics kuzatilsin13. HTTP client timeout tuning
External service chaqirganda timeout shart.
Yomon:
restTemplate.getForObject(url, Response.class);Timeout yo‘q bo‘lsa, thread uzoq vaqt band bo‘lishi mumkin.
RestClient/WebClient timeout
WebClient Netty config bilan:
@Bean
public WebClient webClient() {
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000);
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}Timeout policy:
Connect timeout: 1s
Response timeout: 2s
Overall business timeout: 3s14. JSON serialization performance
Muammo
Entity’ni to‘g‘ridan-to‘g‘ri response qilish:
@GetMapping("/{id}")
public Order getOrder(@PathVariable Long id) {
return orderRepository.findById(id).orElseThrow();
}Xavf:
Lazy loading
Recursive relationship
Keraksiz fieldlar
Katta JSON
Security leakYaxshi:
public record OrderResponse(
Long id,
String status,
BigDecimal totalAmount,
String customerName
) {}Controller:
@GetMapping("/{id}")
public OrderResponse getOrder(@PathVariable Long id) {
return orderService.getOrder(id);
}Senior qoida:
API response uchun DTO ishlat.
Entity’ni tashqariga chiqarma.15. Logging performance
Muammo
Juda ko‘p log ham performance’ni pasaytiradi.
Yomon:
log.info("Order payload: {}", objectMapper.writeValueAsString(order));Bu log level o‘chiq bo‘lsa ham serialization bo‘lishi mumkin.
Yaxshi:
if (log.isDebugEnabled()) {
log.debug("Order payload: {}", objectMapper.writeValueAsString(order));
}Sensitive va katta loglardan qochish
Yomon:
Full request body
Full response body
Token
Password
Card number
Large JSONLog quyidagicha bo‘lsin:
orderId=123 status=CREATED durationMs=45 traceId=abc12316. Cache bilan performance tuning
Cache foydali, lekin noto‘g‘ri ishlatilsa eski data va bug beradi.
Qachon cache yaxshi?
- Data tez-tez o‘qiladi
- Kam o‘zgaradi
- Query qimmat
- Response deterministicMisol:
@Cacheable(value = "products", key = "#id")
public ProductResponse getProduct(Long id) {
return productRepository.findProductResponseById(id);
}Update bo‘lsa:
@CacheEvict(value = "products", key = "#id")
public void updateProduct(Long id, UpdateProductRequest request) {
productRepository.update(id, request);
}Cache xavfi
- Stale data
- Cache stampede
- Memory oshishi
- Noto‘g‘ri key
- Tenant aralashib ketishiMulti-tenant system’da cache key:
tenantId + productIdbo‘lishi kerak.
17. Performance checklist
[ ] Startup time o‘lchanganmi?
[ ] Keraksiz dependency olib tashlanganmi?
[ ] @PostConstruct ichida og‘ir logic yo‘qmi?
[ ] Lazy init qayerda xavfsizligi tekshirilganmi?
[ ] HikariCP metrics kuzatiladimi?
[ ] Transaction ichida external call yo‘qmi?
[ ] N+1 query tekshirilganmi?
[ ] EAGER relationshiplar kamaytirilganmi?
[ ] DTO projection ishlatilganmi?
[ ] Batch insert/update config qilinganmi?
[ ] OFFSET pagination katta jadvalda ishlatilmayaptimi?
[ ] @Async executor alohida sozlanganmi?
[ ] HTTP client timeout bor-mi?
[ ] Loglar structured va minimalmi?
[ ] Cache invalidation aniqmi?
[ ] p95/p99 latency monitoring bormi?18. Real production misol
Muammo
GET /orders?page=5000&pageSize=20Response sekin: 4.5s
Tekshiruv:
- OFFSET juda katta
- Order entity User bilan EAGER
- Har order uchun payment alohida query
- JSON response kattaFix
1. Keyset pagination
2. DTO projection
3. JOIN kerak joyda
4. EAGER → LAZY
5. Index: created_at, status
6. Response’dan keraksiz fieldlarni olib tashlashNatija:
4.5s → 120msBu haqiqiy tuning: framework almashtirish emas, bottleneck’ni topib tuzatish.
19. Senior interview javob
Suhbatda so‘rasa:
Spring Boot performance tuningni qanday qilasiz?
Javob:
Avval o‘lchayman: metrics, logs, traces, DB query time, p95/p99 latency.
Keyin bottleneck topaman. Spring Boot’da ko‘p muammo DB query, N+1, noto‘g‘ri transaction, connection pool, timeout yo‘qligi yoki thread pool config’dan chiqadi.
Startup sekin bo‘lsa ApplicationStartup, Actuator startup endpoint, dependency va bean initialization’ni tekshiraman.
Runtime sekin bo‘lsa HikariCP metrics, SQL logs, Hibernate statistics, APM traces, thread dumps va GC metrics ko‘raman.
Fix sifatida DTO projection, JOIN FETCH, EntityGraph, batch insert, keyset pagination, Hikari tuning, HTTP timeout, bounded async executor va cache invalidation strategiyasini ishlataman.
Eng muhimi: har bir o‘zgarishdan keyin qayta o‘lchayman.20. Qisqa xulosa
Performance tuning’da eng muhim qoida:
Guess qilmang. Measure qiling.Senior developer quyidagilarni ajrata olishi kerak:
Muammo | To‘g‘ri yechim |
|---|---|
Startup sekin | Dependency, bean init, startup profiling |
Query sekin | Index, query optimization, DTO projection |
N+1 | JOIN FETCH, EntityGraph, projection |
Connection kutish | Hikari metrics, transaction qisqartirish |
Insert sekin | Batch insert, sequence generator |
Katta pagination | Keyset pagination |
Async sekin | Custom bounded executor |
External API osilib qoladi | Timeout + CircuitBreaker |
JSON katta | DTO, field kamaytirish |
Log og‘ir | Structured minimal logging |
Performance tuning - bu "tezlashtirish" emas. Bu resurs, latency, throughput va barqarorlikni nazorat qilish.