forked from KSEB-4th-Project-3rd-Team/BE
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathperformance_optimization_strategies.txt
More file actions
200 lines (152 loc) Β· 11.3 KB
/
performance_optimization_strategies.txt
File metadata and controls
200 lines (152 loc) Β· 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
### μ±λ₯ μ΅μ ν μ λ΅ μμΈ μ€λͺ
---
### 1. λ³λ ¬ μ²λ¦¬: `CompletableFuture` νμ©
#### κ°. κ°λ
`CompletableFuture`λ Java 8μ λμ
λ λΉλκΈ°(Asynchronous) νλ‘κ·Έλλ° λꡬμ
λλ€. νΉμ μμ
(μ: μΈλΆ API νΈμΆ, λ¬΄κ±°μ΄ DB μ‘°ν)μ λ³λμ μ€λ λμμ μ€ννκ³ , κ·Έ μμ
μ΄ λλλ κ²μ κΈ°λ€λ¦¬μ§ μκ³ λ€μ μ½λλ₯Ό μ€νν μ μκ² ν΄μ€λλ€. μ΄λ₯Ό ν΅ν΄ μ¬λ¬ κ°μ λ
립μ μΈ μμ
μ λμμ μ²λ¦¬νμ¬ μ 체 μλ΅ μκ°μ λ¨μΆν μ μμ΅λλ€.
#### λ. λ¬Έμ μν©
νλμ μμ²μ μ²λ¦¬νλ λ° μ¬λ¬ κ°μ λ
립μ μΈ I/O μμ
(DB μ‘°ν, API νΈμΆ λ±)μ΄ νμν κ²½μ°, μ΄λ€μ μμ°¨μ μΌλ‘ μ²λ¦¬νλ©΄ κ° μμ
μ μκ°μ΄ λͺ¨λ λν΄μ Έ μ 체 μλ΅ μκ°μ΄ κΈΈμ΄μ§λλ€.
**μμ: λμ보λ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ μμ°¨ μ²λ¦¬**
```java
// μμ°¨ μ²λ¦¬ λ°©μ (μ΄ μμ μκ° = A + B + C)
public DashboardDataResponse getDashboardData() {
// μμ
A: μ¬μ©μ ν΅κ³ μ‘°ν (μ½ 1μ΄ μμ)
UserStats stats = userStatService.getStats();
// μμ
B: μ΅μ μ£Όλ¬Έ λͺ©λ‘ μ‘°ν (μ½ 1.5μ΄ μμ)
List<Order> recentOrders = orderService.getRecentOrders();
// μμ
C: μ¬κ³ νν© μ‘°ν (μ½ 0.8μ΄ μμ)
InventoryStatus inventoryStatus = inventoryService.getStatus();
// μ΄ 3.3μ΄ μμ
return new DashboardDataResponse(stats, recentOrders, inventoryStatus);
}
```
#### λ€. ν΄κ²° λ°©λ²: `CompletableFuture`λ‘ λ³λ ¬ μ²λ¦¬
κ° μμ
μ λ³λμ μ€λ λμμ λμμ μ€ννκ³ , λͺ¨λ μμ
μ΄ μλ£λλ©΄ κ²°κ³Όλ₯Ό μ‘°ν©ν©λλ€. Springμμλ `@Async` μ΄λ
Έν
μ΄μ
μ μ¬μ©νλ©΄ λ μ½κ² ꡬνν μ μμ΅λλ€.
**1. λΉλκΈ° νμ±ν**
`@EnableAsync` μ΄λ
Έν
μ΄μ
μ μ€μ ν΄λμ€μ μΆκ°ν©λλ€.
```java
// SmartWmsBeApplication.java λλ λ³λ Config ν΄λμ€
import org.springframework.scheduling.annotation.EnableAsync;
@EnableAsync
@SpringBootApplication
public class SmartWmsBeApplication {
// ...
}
```
**2. λ³λ ¬ μ²λ¦¬ μ½λ**
```java
// λ³λ ¬ μ²λ¦¬ λ°©μ (μ΄ μμ μκ° = κ°μ₯ μ€λ 걸리λ μμ
μκ°)
public DashboardDataResponse getDashboardDataWithParallel() throws Exception {
// μμ
A, B, Cλ₯Ό κ°κ° λ€λ₯Έ μ€λ λμμ λΉλκΈ° μ€ν
CompletableFuture<UserStats> userStatsFuture = CompletableFuture.supplyAsync(() -> userStatService.getStats());
CompletableFuture<List<Order>> recentOrdersFuture = CompletableFuture.supplyAsync(() -> orderService.getRecentOrders());
CompletableFuture<InventoryStatus> inventoryStatusFuture = CompletableFuture.supplyAsync(() -> inventoryService.getStatus());
// λͺ¨λ μμ
μ΄ μλ£λ λκΉμ§ λκΈ°
CompletableFuture.allOf(userStatsFuture, recentOrdersFuture, inventoryStatusFuture).join();
// κ° Futureμμ κ²°κ³Ό κ°μ Έμ€κΈ°
UserStats stats = userStatsFuture.get();
List<Order> recentOrders = recentOrdersFuture.get();
InventoryStatus inventoryStatus = inventoryStatusFuture.get();
// κ°μ₯ κΈ΄ μμ
μΈ 1.5μ΄λ§μ λͺ¨λ μμ
μλ£
return new DashboardDataResponse(stats, recentOrders, inventoryStatus);
}
```
#### λΌ. ν΅μ¬ κ³ λ €μ¬ν
* **μ€λ λ ν μ€μ **: κΈ°λ³Έ μ€λ λ νμ νκ³κ° μμΌλ―λ‘, `ThreadPoolTaskExecutor`λ₯Ό λ³λλ‘ μ€μ νμ¬ μ ν리μΌμ΄μ
μ λ§λ μ€λ λ μλ₯Ό ν보νλ κ²μ΄ μ€μν©λλ€.
* **I/O Bound μμ
μ μ ν©**: CPU μ¬μ©λμ΄ λμ μμ
보λ€λ λ€νΈμν¬ ν΅μ , DB μ‘°ν λ± λκΈ° μκ°μ΄ κΈ΄ I/O Bound μμ
μ μ¬μ©ν΄μΌ ν¨κ³Όκ° κ·Ήλνλ©λλ€.
---
### 2. FETCH JOIN: N+1 λ¬Έμ ν΄κ²°
#### κ°. κ°λ
N+1 λ¬Έμ λ, μ°κ΄ κ΄κ³ λ§€νλ μν°ν°λ₯Ό μ‘°νν λ λ°μνλ λνμ μΈ μ±λ₯ μ ν λ¬Έμ μ
λλ€.
1. λ¨Όμ λΆλͺ¨ μν°ν° λͺ©λ‘μ μ‘°ννλ 쿼리 1λ² (μ: `SELECT * FROM orders;`)
2. μ΄ν κ° λΆλͺ¨ μν°ν°μ λν΄ μ°κ΄λ μμ μν°ν°λ₯Ό μ‘°ννλ μΏΌλ¦¬κ° Nλ² μΆκ°λ‘ μ€νλλ νμ (μ: `SELECT * FROM order_item WHERE order_id = ?;` κ° Nλ² μ€ν)
#### λ. λ¬Έμ μν©
`InOutOrder` λͺ©λ‘μ μ‘°νν ν, κ° μ£Όλ¬Έ(`InOutOrder`)μ μν `OrderItem` λͺ©λ‘μ νλ©΄μ νμν΄μΌ νλ κ²½μ°λ₯Ό κ°μ ν΄ λ³΄κ² μ΅λλ€.
```java
// InOutOrderService.java
public List<InOutOrderResponse> getAllOrders() {
List<InOutOrder> orders = inOutOrderRepository.findAll(); // 1λ²μ 쿼리 λ°μ
return orders.stream()
.map(order -> new InOutOrderResponse(
order.getId(),
order.getOrderDate(),
order.getOrderItems().size() // Lazy LoadingμΌλ‘ μΈν΄ μ¬κΈ°μ Nλ²μ μΆκ° 쿼리 λ°μ
))
.collect(Collectors.toList());
}
```
μ μ½λμμ `order.getOrderItems().size()`λ₯Ό νΈμΆνλ μκ°, κ° `order` κ°μ²΄μ λν `OrderItem`μ κ°μ Έμ€κΈ° μν΄ μΆκ° μΏΌλ¦¬κ° λ°μν©λλ€. μ£Όλ¬Έμ΄ 100κ°λΌλ©΄ μ΄ 1+100=101λ²μ μΏΌλ¦¬κ° μ€νλ©λλ€.
#### λ€. ν΄κ²° λ°©λ²: `JOIN FETCH` μ¬μ©
JPQL(Java Persistence Query Language)μμ `JOIN FETCH`λ₯Ό μ¬μ©νλ©΄, μ°κ΄λ μν°ν°λ₯Ό μ¦μ ν¨κ» μ‘°ννμ¬ λ¨ ν λ²μ μΏΌλ¦¬λ‘ λͺ¨λ λ°μ΄ν°λ₯Ό κ°μ Έμ¬ μ μμ΅λλ€.
**`InOutOrderRepository.java` μμ **
```java
import org.springframework.data.jpa.repository.Query;
public interface InOutOrderRepository extends JpaRepository<InOutOrder, Long> {
// JOIN FETCHλ₯Ό μ¬μ©νμ¬ InOutOrderμ OrderItemμ ν λ²μ μ‘°ν
@Query("SELECT DISTINCT o FROM InOutOrder o JOIN FETCH o.orderItems")
List<InOutOrder> findAllWithOrderItems();
}
```
* `JOIN FETCH o.orderItems`: `InOutOrder`(λ³μΉ `o`)λ₯Ό μ‘°νν λ, μ°κ΄λ `orderItems` 컬λ μ
λ ν¨κ» κ°μ Έμ€λΌλ μλ―Έμ
λλ€.
* `DISTINCT`: JOINμΌλ‘ μΈν΄ `InOutOrder`κ° μ€λ³΅ μ‘°νλ μ μμΌλ―λ‘, `DISTINCT`λ₯Ό μ¬μ©νμ¬ μ€λ³΅μ μ κ±°ν©λλ€.
μ΄μ μλΉμ€μμλ μλ‘ λ§λ λ©μλλ₯Ό νΈμΆνλ©΄ λ©λλ€.
```java
// InOutOrderService.java
public List<InOutOrderResponse> getAllOrders() {
// λ¨ 1λ²μ μΏΌλ¦¬λ‘ λͺ¨λ λ°μ΄ν°λ₯Ό κ°μ Έμ΄
List<InOutOrder> orders = inOutOrderRepository.findAllWithOrderItems();
return orders.stream()
.map(order -> new InOutOrderResponse(
order.getId(),
order.getOrderDate(),
order.getOrderItems().size() // μΆκ° 쿼리 λ°μ μ ν¨
))
.collect(Collectors.toList());
}
```
---
### 3. λ°°μΉ μ²λ¦¬: JPA λ°°μΉ μ€μ μ΅μ ν
#### κ°. κ°λ
λ°°μΉ(Batch) μ²λ¦¬λ, μ¬λ¬ κ°μ `INSERT`, `UPDATE`, `DELETE` 쿼리λ₯Ό ν λ²μ λͺ¨μμ DBμ μ μ‘νλ κΈ°μ μ
λλ€. 쿼리λ§λ€ λ€νΈμν¬ ν΅μ μ νλ λμ , μ¬λ¬ 쿼리λ₯Ό νλμ μμ²μΌλ‘ λ¬Άμ΄ λ³΄λ΄λ―λ‘ λ€νΈμν¬ μ€λ²ν€λμ DB λΆνλ₯Ό ν¬κ² μ€μΌ μ μμ΅λλ€.
#### λ. λ¬Έμ μν©
λλμ λ°μ΄ν°λ₯Ό ν λ²μ μ μ₯ν΄μΌ ν κ²½μ°(μ: CSV νμΌλ‘ μν 10,000κ° λ±λ‘), `repository.save()`λ₯Ό 루ν μμμ νΈμΆνλ©΄ 10,000λ²μ `INSERT` μΏΌλ¦¬κ° κ°κ° DBλ‘ μ μ‘λμ΄ μμ²λ μκ°μ΄ μμλ©λλ€.
#### λ€. ν΄κ²° λ°©λ²: `application.properties` μ€μ
Spring Bootμμλ `application.properties` (λλ `application.yml`) νμΌμ κ°λ¨ν μ€μ μ μΆκ°νλ κ²λ§μΌλ‘ JPAμ μ°κΈ° λ°°μΉ κΈ°λ₯μ νμ±νν μ μμ΅λλ€.
```properties
# application.properties
# spring.jpa.properties.hibernate.jdbc.batch_size=500
# spring.jpa.properties.hibernate.order_inserts=true
# spring.jpa.properties.hibernate.order_updates=true
# spring.jpa.properties.hibernate.batch_versioned_data=true
```
* `batch_size`: ν λ²μ λͺ¨μμ λ³΄λΌ μΏΌλ¦¬μ κ°μμ
λλ€. 100~1000 μ¬μ΄μ κ°μ μ£Όλ‘ μ¬μ©νλ©°, DBμ μ ν리μΌμ΄μ
μν©μ λ§κ² μ‘°μ ν©λλ€.
* `order_inserts`, `order_updates`: `INSERT`μ `UPDATE` 쿼리λ₯Ό κ°κ° λͺ¨μμ μ€ν μμλ₯Ό μ΅μ νν©λλ€. μ±λ₯ ν₯μμ λμμ΄ λ©λλ€.
* `batch_versioned_data`: `@Version` μ΄λ
Έν
μ΄μ
μ μ¬μ©νλ λκ΄μ λ½(Optimistic Lock) μν°ν°μ λν΄μλ λ°°μΉ μ²λ¦¬λ₯Ό νμ±νν©λλ€.
#### λΌ. ν΅μ¬ κ³ λ €μ¬ν
* **ID μμ± μ λ΅**: `GenerationType.IDENTITY`λ μν°ν°λ₯Ό μ μ₯νλ μ¦μ DBμμ IDλ₯Ό λ°μμμΌ νλ―λ‘ λ°°μΉ `INSERT`κ° λμνμ§ μμ΅λλ€. λ°°μΉ μ²λ¦¬λ₯Ό μν΄μλ `GenerationType.SEQUENCE`λ `GenerationType.TABLE`μ μ¬μ©ν΄μΌ ν©λλ€.
* **`saveAll` λ©μλ**: JPAμ `saveAll` λ©μλλ λ΄λΆμ μΌλ‘ 루νλ₯Ό λλ©° `save`λ₯Ό νΈμΆνλ―λ‘, λ°°μΉ μ€μ μ΄ λμ΄ μλ€λ©΄ μλμΌλ‘ μ μ©λ©λλ€.
---
### 4. 컀λ₯μ
ν: HikariCP νλ
#### κ°. κ°λ
DB 컀λ₯μ
μ λ§Ίλ κ³Όμ μ λΉμ©μ΄ λΉμΌ μμ
μ
λλ€. 컀λ₯μ
ν(Connection Pool)μ 미리 μΌμ κ°μμ 컀λ₯μ
μ λ§λ€μ΄λκ³ , νμν λλ§λ€ λΉλ €μ£Όκ³ λ°λ©λ°λ λ°©μμΌλ‘ λμνμ¬ μ»€λ₯μ
μμ± λΉμ©μ μ μ½νλ κΈ°μ μ
λλ€. Spring Boot 2.0λΆν°λ `HikariCP`κ° κΈ°λ³Έ 컀λ₯μ
νλ‘ μ¬μ©λ©λλ€.
#### λ. λ¬Έμ μν©
κΈ°λ³Έ μ€μ μ λλΆλΆμ μν©μμ μ λμνμ§λ§, νΈλν½μ΄ λ§μ μλΉμ€μμλ 컀λ₯μ
λΆμ‘±, μλ΅ μ§μ° λ±μ λ¬Έμ κ° λ°μν μ μμ΅λλ€. μλ₯Ό λ€μ΄, λμμ 100κ°μ μμ²μ΄ λ€μ΄μλλ° μ»€λ₯μ
νμ μ΅λ ν¬κΈ°(`maximum-pool-size`)κ° 10μ΄λΌλ©΄, 90κ°μ μμ²μ μμ μμ²μ΄ 컀λ₯μ
μ λ°λ©ν λκΉμ§ λκΈ°ν΄μΌ ν©λλ€.
#### λ€. ν΄κ²° λ°©λ²: `application.properties` νλ
`HikariCP`μ μ£Όμ μμ±μ μ ν리μΌμ΄μ
μ λΆν νΉμ±μ λ§κ² μ‘°μ ν©λλ€.
```properties
# application.properties
# μ΅λ 컀λ₯μ
κ°μ (κ°μ₯ μ€μν μμ±)
spring.datasource.hikari.maximum-pool-size=20
# μ΅μ μ ν΄ μ»€λ₯μ
κ°μ (μ±λ₯μ μν΄ maximum-pool-sizeμ λμΌνκ² μ€μ νλ κ²μ κΆμ₯)
spring.datasource.hikari.minimum-idle=20
# 컀λ₯μ
μ μ»κΈ° μν΄ λκΈ°νλ μ΅λ μκ° (ms)
spring.datasource.hikari.connection-timeout=30000
# 컀λ₯μ
νμμ 컀λ₯μ
μ΄ μ΄μμμ μ μλ μ΅λ μκ° (ms)
spring.datasource.hikari.max-lifetime=1800000
# μ ν΄ μνμ 컀λ₯μ
μ νμμ μ κ±°νκΈ°κΉμ§ λκΈ°νλ μκ° (ms)
spring.datasource.hikari.idle-timeout=600000
```
#### λΌ. μ£Όμ μμ± μ€λͺ
λ° νλ κ°μ΄λ
* `maximum-pool-size`: λμμ μ²λ¦¬ν μ μλ DB μμ
μ μ΅λ κ°μ. μ€λ λ κ°μ, DBμ μ²λ¦¬ λ₯λ ₯ λ±μ κ³ λ €νμ¬ μ€μ ν΄μΌ ν©λλ€. λ무 ν¬λ©΄ μ€νλ € DBμ λΆνλ₯Ό μ€ μ μμ΅λλ€. `(CPU μ½μ΄ μ * 2) + (λμ€ν¬ μ)` λΌλ κ³ μ μ μΈ κ³΅μμ΄ μμ§λ§, νλμ μΈ μ ν리μΌμ΄μ
μμλ **λΆν ν
μ€νΈλ₯Ό ν΅ν΄ μ μ κ°μ μ°Ύλ κ²μ΄ κ°μ₯ μ ν**ν©λλ€.
* `minimum-idle`: νμ΄ μ μ§νλ μ΅μνμ μ ν΄ μ»€λ₯μ
μ. `maximum-pool-size`μ κ°μ κ°μΌλ‘ μ€μ νλ©΄, κ°μμ€λ¬μ΄ νΈλν½ μ¦κ°μλ 컀λ₯μ
μ μλ‘ μμ±νλ λΉμ© μμ΄ μ¦μ λμν μ μμ΄ μλ΅ μκ°μ μΌμ νκ² μ μ§νλ λ° λμμ΄ λ©λλ€.
* `connection-timeout`: μ΄ μκ° μμ 컀λ₯μ
μ μ»μ§ λͺ»νλ©΄ μμΈκ° λ°μν©λλ€. λ무 μ§§μΌλ©΄ μ₯μ λ‘ μ€μΈν μ μκ³ , λ무 κΈΈλ©΄ μ¬μ©μκ° λ¬΄νμ λκΈ°νλ κ²½νμ ν μ μμ΅λλ€.
* `max-lifetime`: DBλ λ°©νλ²½μ νμμμ μ€μ λ³΄λ€ μ§§κ² μ€μ νμ¬, λΉμ μμ μΌλ‘ λμ΄μ§ 컀λ₯μ
μ μ¬μ©νλ κ²μ λ°©μ§ν©λλ€.