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.
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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
