MySQL驱动扯后腿?Spring Boot用虚拟线程可能比用物理线程还差 ...

打印 上一主题 下一主题

主题 861|帖子 861|积分 2583

之前已经分享过多篇关于Spring Boot中使用Java 21新特性虚拟线程的性能测试案例:
早上看到群友问到一个关于虚拟线程遇到MySQL连接不兼容导致的性能问题:

这个问题确实之前就有看到过相关的评测,顺着个这个问题,重新把相关评测找出来,给大家分享一下。
以下内容主要参考文章:https://medium.com/deno-the-complete-reference/springboot-physical-vs-virtual-threads-vs-webflux-performance-comparison-for-jwt-verify-and-mysql-23d773b41ffd
评测案例

评测采用现实场景中的处理流程,具体如下:

  • 从HTTP授权标头(authorization header)中提取 JWT
  • 验证 JWT 并从中提取用户的电子邮件
  • 使用提取到的电子邮件执行 MySQL 查询用户
  • 返回用户记录
这个场景其实是Spring Boot 虚拟线程与Webflux在JWT验证和MySQL查询上的性能比较测试的后续。前文主要对比了虚拟线程和WebFlux的,但没有对比虚拟线程与物理线程的区别。所以,接下来的内容就是本文关心的重点:在物理线程和虚拟线程下,MySQL驱动是否有性能优化。
测试环境


  • Java 20(使用预览模式,开启虚拟线程)
  • Spring Boot 3.1.3
  • 依赖的第三方库:jjwt、mysql-connector-java
测试工具:Bombardier
采用了开源负载测试工具:Bombardier。在测试场景中预先创建 100,000 个 JWT 列表。
在测试期间,Bombardier 从该池中随机选择了JWT,并将它们包含在HTTP请求的Authorization标头中。
MySQL表结构与数据准备
User表结构如下:
  1. mysql> desc users;
  2. +--------+--------------+------+-----+---------+-------+
  3. | Field  | Type         | Null | Key | Default | Extra |
  4. +--------+--------------+------+-----+---------+-------+
  5. | email  | varchar(255) | NO   | PRI | NULL    |       |
  6. | first  | varchar(255) | YES  |     | NULL    |       |
  7. | last   | varchar(255) | YES  |     | NULL    |       |
  8. | city   | varchar(255) | YES  |     | NULL    |       |
  9. | county | varchar(255) | YES  |     | NULL    |       |
  10. | age    | int          | YES  |     | NULL    |       |
  11. +--------+--------------+------+-----+---------+-------+
  12. 6 rows in set (0.00 sec)
复制代码
准备大约10w条数据:
  1. mysql> select count(*) from users;
  2. +----------+
  3. | count(*) |
  4. +----------+
  5. |    99999 |
  6. +----------+
  7. 1 row in set (0.01 sec)
复制代码
测试代码:使用物理线程

配置文件:
  1. server.port=3000
  2. spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false&allowPublicKeyRetrieval=true
  3. spring.datasource.username= dbuser
  4. spring.datasource.password= dbpwd
  5. spring.jpa.hibernate.ddl-auto= update
  6. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  7. spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
复制代码
User实体定义:
  1. @Entity
  2. @Table(name = "users")
  3. public class User {
  4.   @Id
  5.   private String email;
  6.   private String first;
  7.   private String last;
  8.   private String city;
  9.   private String county;
  10.   private int age;
  11.   // 省略了getter和setter
  12. }
复制代码
数据访问实现:
  1. public interface UserRepository extends CrudRepository<User, String> {
  2. }
复制代码
API实现:
  1. @RestController
  2. public class UserController {
  3.     @Autowired
  4.     UserRepository userRepository;
  5.     private SignatureAlgorithm sa = SignatureAlgorithm.HS256;
  6.     private String jwtSecret = System.getenv("JWT_SECRET");
  7.     @GetMapping("/")
  8.     public User handleRequest(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) {
  9.         String jwtString = authHdr.replace("Bearer","");
  10.         Claims claims = Jwts.parser()
  11.             .setSigningKey(jwtSecret.getBytes())
  12.             .parseClaimsJws(jwtString).getBody();
  13.         Optional<User> user = userRepository.findById((String)claims.get("email"));
  14.         return user.get();
  15.     }
  16. }
复制代码
应用主类:
  1. @SpringBootApplication
  2. public class UserApplication {
  3.     public static void main(String[] args) {
  4.         SpringApplication.run(UserApplication.class, args);
  5.     }
  6. }
复制代码
测试代码:使用虚拟线程

主要调整应用主类,其他一样,具体修改如下:
  1. @SpringBootApplication
  2. public class UserApplication {
  3.     public static void main(String[] args) {
  4.         SpringApplication.run(UserApplication.class, args);
  5.     }
  6.     @Bean
  7.     public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
  8.         return protocolHandler -> {
  9.             protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
  10.         };
  11.     }
  12. }
复制代码
测试代码:使用WebFlux
  1. server.port=3000
  2. spring.r2dbc.url=r2dbc:mysql://localhost:3306/testdb?allowPublicKeyRetrieval=true&ssl=false
  3. spring.r2dbc.username=dbuser
  4. spring.r2dbc.password=dbpwd
  5. spring.r2dbc.pool.initial-size=10
  6. spring.r2dbc.pool.max-size=10
复制代码
  1. @Table(name = "users")
  2. public class User {
  3.   @Id
  4.   private String email;
  5.   private String first;
  6.   private String last;
  7.   private String city;
  8.   private String county;
  9.   private int age;
  10.   // 省略getter、setter和构造函数
  11. }
复制代码
数据访问实现:
  1. public interface UserRepository extends R2dbcRepository<User, String> {
  2. }
复制代码
业务逻辑实现:
  1. @Service
  2. public class UserService {
  3.   @Autowired
  4.   UserRepository userRepository;
  5.   public Mono<User> findById(String id) {
  6.     return userRepository.findById(id);
  7.   }
  8. }
复制代码
API实现:
  1. @RestController
  2. @RequestMapping("/")
  3. public class UserController {
  4.   @Autowired
  5.   UserService userService;
  6.   private SignatureAlgorithm sa = SignatureAlgorithm.HS256;
  7.   private String jwtSecret = System.getenv("JWT_SECRET");
  8.   @GetMapping("/")
  9.   @ResponseStatus(HttpStatus.OK)
  10.   public Mono<User> getUserById(@RequestHeader(HttpHeaders.AUTHORIZATION) String authHdr) {
  11.     String jwtString = authHdr.replace("Bearer","");
  12.     Claims claims = Jwts.parser()
  13.         .setSigningKey(jwtSecret.getBytes())
  14.         .parseClaimsJws(jwtString).getBody();
  15.     return userService.findById((String)claims.get("email"));
  16.   }
  17. }
复制代码
应用主类:
  1. @EnableWebFlux
  2. @SpringBootApplication
  3. public class UserApplication {
  4.   public static void main(String[] args) {
  5.     SpringApplication.run(UserApplication.class, args);
  6.   }
  7. }
复制代码
测试结果

每次测试都包含 100 万个请求,分别评估了它们在不同并发(50、100、300)水平下的性能。下面是结果展示:













分析总结

在这个测试案例中使用了MySQL驱动,虚拟线程的实现方式性能最差,WebFlux依然保持领先。所以,主要原因在于这个MySQL的驱动对虚拟线程不友好。如果涉及到数据库访问的情况下,需要寻找对虚拟线程支持最佳的驱动程序。另外,该测试使用的是Java 20和Spring Boot 3.1。对于Java 21和Spring Boot 3.2建议读者在使用的时候自行评估。
最后,对于MySQL驱动对虚拟线程支持好的,欢迎留言区推荐一下。如果您学习过程中如遇困难?可以加入我们超高质量的Spring技术交流群,参与交流与讨论,更好的学习与进步!更多Spring Boot教程可以点击直达!,欢迎收藏与转发支持!
欢迎关注我的公众号:程序猿DD。第一时间了解前沿行业消息、分享深度技术干货、获取优质学习资源

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表