5 Proven Optimizations That Supercharged Our Category Tree API Performance
This article walks through five successive performance optimizations—adding Redis caching, scheduling async updates, introducing a Caffeine local cache, enabling Nginx GZip compression, and compressing Redis data—to dramatically reduce the size and latency of a category‑tree API in a SpringBoot application.
Preface
The category tree query is a common feature in e‑commerce systems, and we performed five rounds of optimization to improve its performance.
Background
Our website uses SpringBoot with the Thymeleaf template engine for dynamic rendering. The first version of the API directly queried the database for category data, assembled a category tree, and returned it to the front end.
First Optimization
We added a Redis cache. The flow was:
When the client requests the category tree, first query Redis.
If Redis has data, return it directly.
If Redis misses, query the database, assemble the tree, return it, and store the result in Redis with a 5‑minute TTL.
Second Optimization
We introduced a scheduled Job that asynchronously refreshes the category tree in Redis every five minutes, while keeping the original on‑demand sync logic as a fallback. The TTL was changed to permanent.
Third Optimization
Load testing revealed that fetching the tree from Redis on every request became a bottleneck. We added a local in‑memory cache using Spring’s recommended Caffeine library.
New flow:
Check local cache first.
If hit, return immediately.
If miss, query Redis.
If Redis hits, populate local cache and return.
If Redis also misses (e.g., Redis down), query the database, update Redis, then update local cache and return.
Note: the local cache should have a short expiration (e.g., 5 minutes) to keep data fresh.
Fourth Optimization
When the number of categories grew to tens of thousands, the response size reached ~1 MB. We enabled Nginx GZip compression, reducing the payload to about 100 KB.
Fifth Optimization
Redis stored the whole tree as a large JSON string, causing a big‑key issue. We slimmed the data by:
Removing unnecessary fields (e.g., inDate, inUserId, inUserName).
Renaming JSON properties to short keys using @JsonProperty annotations.
@AllArgsConstructor
@Data
public class Category {
private Long id;
private String name;
private Long parentId;
private Date inDate;
private Long inUserId;
private String inUserName;
private List<Category> children;
}We then compressed the JSON with GZip and stored the byte array via RedisTemplate. On read, the byte array is decompressed back to JSON and deserialized.
@AllArgsConstructor
@Data
public class Category {
/** 分类编号 */
@JsonProperty("i")
private Long id;
/** 分类层级 */
@JsonProperty("l")
private Integer level;
/** 分类名称 */
@JsonProperty("n")
private String name;
/** 父分类编号 */
@JsonProperty("p")
private Long parentId;
/** 子分类列表 */
@JsonProperty("c")
private List<Category> children;
}After these five optimizations, the API response size shrank tenfold, QPS increased from ~100 to >500, and the system remained stable for years.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
