Boost SpringBoot API Performance: 12 Proven Optimization Techniques

This article presents a practical guide for SpringBoot projects, detailing twelve concrete strategies—including batch processing, asynchronous execution, caching, pagination, and transaction handling—to dramatically reduce API latency and improve overall backend efficiency.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Boost SpringBoot API Performance: 12 Proven Optimization Techniques

Background

In a legacy SpringBoot project the team discovered that many interfaces suffered from excessive response time, prompting a focused performance‑tuning effort.

Interface optimization solutions

1. Batch processing

Group database writes into a single batch to avoid repeated I/O in loops.

// for‑loop single insert
list.stream().forEach(msg -> {
    insert();
});
// batch insert
batchInsert();

2. Asynchronous processing

Move long‑running, non‑critical logic to a background thread, thread pool, message queue or scheduled task.

Example: a financial purchase interface where accounting and file‑write steps are not required to return immediately can be executed asynchronously.

3. Space‑for‑time (caching)

Cache frequently accessed, rarely changed data (e.g., R2M, local cache, Memcached, or a simple Map) to avoid repeated database queries or heavy calculations.

Stock‑tool example: weekly‑updated rotation data is cached, and subsequent complex calculations are performed on the cached value, dramatically reducing latency.

4. Pre‑processing

Compute and store derived values (e.g., annualized return from net asset value) ahead of time so that the API can simply read a field.

5. Pooling

Reuse expensive resources such as database connections or threads via connection pools and thread pools, following the “pre‑allocate and reuse” principle.

6. Serial to parallel

When independent sub‑tasks have no data dependency, execute them concurrently to cut total response time.

7. Indexing

Proper indexes dramatically speed up queries; the article notes common cases where indexes are ineffective and suggests reviewing them during iteration.

8. Avoid large transactions

Long‑running transactions hold database connections, causing contention. Example code shows a @Transactional method that inserts several tasks; adding a push‑notification RPC inside the same transaction can lead to deadlocks and time‑outs.

@Transactional(value = "taskTransactionManager", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = {RuntimeException.class, Exception.class})
public BasicResult purchaseRequest(PurchaseRecord record) {
    BasicResult result = new BasicResult();
    taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_account.type(), TaskEnum.Account_bizType.purchase_request.type()));
    taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_sync.type(), TaskEnum.Sync_bizType.purchase.type()));
    taskMapper.insert(ManagerParamUtil.buildTask(record, TaskEnum.Task_type.pension_sync.type(), TaskEnum.Sync_bizType.cert.type()));
    result.setInfo(ResultInfoEnum.SUCCESS);
    return result;
}

Mitigation strategies:

Do not place RPC calls inside the transaction.

Keep read‑only queries outside the transaction.

Limit the amount of data processed within a single transaction.

9. Refactor program structure

Repeated refactoring after many feature iterations can eliminate duplicated queries and object creations, improving overall performance.

10. Deep pagination

Using LIMIT offset, count with a large offset forces the database to scan many rows. Replace it with a “keyset pagination” pattern that filters on an indexed primary key.

SELECT * FROM purchase_record
WHERE productCode = 'PA9044' AND status = 4 AND id > 100000
LIMIT 200;

11. SQL optimization

General advice: combine indexing, appropriate pagination, and selective column retrieval to speed up queries.

12. Lock granularity

Use the smallest possible lock scope; synchronize only the truly shared resources. Over‑broad locks (e.g., locking an entire class when only a single method needs protection) degrade concurrency.

// wrong: lock non‑shared code
private void notShare() { }
private void share() { }
private int wrong() {
    synchronized(this) {
        share();
        notShare();
    }
}

// correct: lock only shared part
private void notShare() { }
private void share() { }
private int right() {
    notShare();
    synchronized(this) {
        share();
    }
}

Conclusion

Interface performance problems usually accumulate over multiple iterations; adopting a higher‑level design mindset and applying the above techniques can significantly reduce latency and lower operational costs.

JavaCachingbackend optimizationAPI performance
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.