javascript
MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差
之前已經(jīng)分享過(guò)多篇關(guān)于Spring Boot中使用Java 21新特性虛擬線程的性能測(cè)試案例:
- Spring Boot 3.2虛擬線程搭建靜態(tài)文件服務(wù)器有多快?
- Spring Boot 虛擬線程與Webflux在JWT驗(yàn)證和MySQL查詢上的性能比較
早上看到群友問到一個(gè)關(guān)于虛擬線程遇到MySQL連接不兼容導(dǎo)致的性能問題:
這個(gè)問題確實(shí)之前就有看到過(guò)相關(guān)的評(píng)測(cè),順著個(gè)這個(gè)問題,重新把相關(guān)評(píng)測(cè)找出來(lái),給大家分享一下。
以下內(nèi)容主要參考文章:https://medium.com/deno-the-complete-reference/springboot-physical-vs-virtual-threads-vs-webflux-performance-comparison-for-jwt-verify-and-mysql-23d773b41ffd
評(píng)測(cè)案例
評(píng)測(cè)采用現(xiàn)實(shí)場(chǎng)景中的處理流程,具體如下:
- 從HTTP授權(quán)標(biāo)頭(authorization header)中提取 JWT
- 驗(yàn)證 JWT 并從中提取用戶的電子郵件
- 使用提取到的電子郵件執(zhí)行 MySQL 查詢用戶
- 返回用戶記錄
這個(gè)場(chǎng)景其實(shí)是Spring Boot 虛擬線程與Webflux在JWT驗(yàn)證和MySQL查詢上的性能比較測(cè)試的后續(xù)。前文主要對(duì)比了虛擬線程和WebFlux的,但沒有對(duì)比虛擬線程與物理線程的區(qū)別。所以,接下來(lái)的內(nèi)容就是本文關(guān)心的重點(diǎn):在物理線程和虛擬線程下,MySQL驅(qū)動(dòng)是否有性能優(yōu)化。
測(cè)試環(huán)境
- Java 20(使用預(yù)覽模式,開啟虛擬線程)
- Spring Boot 3.1.3
- 依賴的第三方庫(kù):jjwt、mysql-connector-java
測(cè)試工具:Bombardier
采用了開源負(fù)載測(cè)試工具:Bombardier。在測(cè)試場(chǎng)景中預(yù)先創(chuàng)建 100,000 個(gè) JWT 列表。
在測(cè)試期間,Bombardier 從該池中隨機(jī)選擇了JWT,并將它們包含在HTTP請(qǐng)求的Authorization標(biāo)頭中。
MySQL表結(jié)構(gòu)與數(shù)據(jù)準(zhǔn)備
User表結(jié)構(gòu)如下:
mysql> desc users;
+--------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+-------+
| email | varchar(255) | NO | PRI | NULL | |
| first | varchar(255) | YES | | NULL | |
| last | varchar(255) | YES | | NULL | |
| city | varchar(255) | YES | | NULL | |
| county | varchar(255) | YES | | NULL | |
| age | int | YES | | NULL | |
+--------+--------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
準(zhǔn)備大約10w條數(shù)據(jù):
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
| 99999 |
+----------+
1 row in set (0.01 sec)
測(cè)試代碼:使用物理線程
配置文件:
server.port=3000
spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username= dbuser
spring.datasource.password= dbpwd
spring.jpa.hibernate.ddl-auto= update
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
User實(shí)體定義:
@Entity
@Table(name = "users")
public class User {
@Id
private String email;
private String first;
private String last;
private String city;
private String county;
private int age;
// 省略了getter和setter
}
數(shù)據(jù)訪問實(shí)現(xiàn):
public interface UserRepository extends CrudRepository<User, String> {
}
API實(shí)現(xiàn):
@RestController
public class UserController {
@Autowired
UserRepository userRepository;
private SignatureAlgorithm sa = SignatureAlgorithm.HS256;
private String jwtSecret = System.getenv("JWT_SECRET");
@GetMapping("/")
public User handleRequest(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) {
String jwtString = authHdr.replace("Bearer","");
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret.getBytes())
.parseClaimsJws(jwtString).getBody();
Optional<User> user = userRepository.findById((String)claims.get("email"));
return user.get();
}
}
應(yīng)用主類:
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
測(cè)試代碼:使用虛擬線程
主要調(diào)整應(yīng)用主類,其他一樣,具體修改如下:
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}
}
測(cè)試代碼:使用WebFlux
server.port=3000
spring.r2dbc.url=r2dbc:mysql://localhost:3306/testdb?allowPublicKeyRetrieval=true&ssl=false
spring.r2dbc.username=dbuser
spring.r2dbc.password=dbpwd
spring.r2dbc.pool.initial-size=10
spring.r2dbc.pool.max-size=10
@Table(name = "users")
public class User {
@Id
private String email;
private String first;
private String last;
private String city;
private String county;
private int age;
// 省略getter、setter和構(gòu)造函數(shù)
}
數(shù)據(jù)訪問實(shí)現(xiàn):
public interface UserRepository extends R2dbcRepository<User, String> {
}
業(yè)務(wù)邏輯實(shí)現(xiàn):
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public Mono<User> findById(String id) {
return userRepository.findById(id);
}
}
API實(shí)現(xiàn):
@RestController
@RequestMapping("/")
public class UserController {
@Autowired
UserService userService;
private SignatureAlgorithm sa = SignatureAlgorithm.HS256;
private String jwtSecret = System.getenv("JWT_SECRET");
@GetMapping("/")
@ResponseStatus(HttpStatus.OK)
public Mono<User> getUserById(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) {
String jwtString = authHdr.replace("Bearer","");
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret.getBytes())
.parseClaimsJws(jwtString).getBody();
return userService.findById((String)claims.get("email"));
}
}
應(yīng)用主類:
@EnableWebFlux
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
測(cè)試結(jié)果
每次測(cè)試都包含 100 萬(wàn)個(gè)請(qǐng)求,分別評(píng)估了它們?cè)诓煌l(fā)(50、100、300)水平下的性能。下面是結(jié)果展示:
分析總結(jié)
在這個(gè)測(cè)試案例中使用了MySQL驅(qū)動(dòng),虛擬線程的實(shí)現(xiàn)方式性能最差,WebFlux依然保持領(lǐng)先。所以,主要原因在于這個(gè)MySQL的驅(qū)動(dòng)對(duì)虛擬線程不友好。如果涉及到數(shù)據(jù)庫(kù)訪問的情況下,需要尋找對(duì)虛擬線程支持最佳的驅(qū)動(dòng)程序。另外,該測(cè)試使用的是Java 20和Spring Boot 3.1。對(duì)于Java 21和Spring Boot 3.2建議讀者在使用的時(shí)候自行評(píng)估。
最后,對(duì)于MySQL驅(qū)動(dòng)對(duì)虛擬線程支持好的,歡迎留言區(qū)推薦一下。如果您學(xué)習(xí)過(guò)程中如遇困難?可以加入我們超高質(zhì)量的Spring技術(shù)交流群,參與交流與討論,更好的學(xué)習(xí)與進(jìn)步!更多Spring Boot教程可以點(diǎn)擊直達(dá)!,歡迎收藏與轉(zhuǎn)發(fā)支持!
歡迎關(guān)注我的公眾號(hào):程序猿DD。第一時(shí)間了解前沿行業(yè)消息、分享深度技術(shù)干貨、獲取優(yōu)質(zhì)學(xué)習(xí)資源
總結(jié)
以上是生活随笔為你收集整理的MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【驱动】I2C驱动分析(二)-驱动框架
- 下一篇: gradle idea java ssm