MySQL 中使用 JPA + Hibernate 的 9 个高性能技巧


MySQL 中使用 JPA + Hibernate 的 9 个高性能技巧

文章插图
 
尽管有SQL标准,但每个关系数据库终将是唯一的,因此你需要调整数据访问层,以便充分利用在使用中的关系数据库 。
在本文中,我们将介绍在使用带有JPA和Hibernate的MySQL时,为了提高性能,我们可以做哪些事情 。
不要使用AUTO标识符GeneratorType
每个实体都需要标识符,标识符惟一地标识与该实体关联的表记录 。JPA和Hibernate允许根据三种不同的策略自动生成实体标识符:
  • IDENTITY
  • SEQUENCE
  • TABLE
当增加数据库连接数时,TABLE标识符策略不会缩放 。而且,即使是一个数据库连接,标识符生成响应时间比使用IDENTITY或SEQUENCE大十倍 。
如果你使用AUTO GenerationType:
@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;Hibernate 5将会退回到使用TABLE生成器,这对性能不利 。
你可以使用以下映射轻松解决此问题:
@Id@GeneratedValue(strategy= GenerationType.AUTO, generator="native")@GenericGenerator(name = "native", strategy = "native")private Long id;本地生成器将选择IDENTITY而不是TABLE 。
IDENTITY生成器禁用JDBC批处理插入
MySQL 5.7和8.0都不支持SEQUENCE对象,因此你需要使用IDENTITY 。IDENTITY生成器可以防止Hibernate使用JDBC批量插入 。
JDBC批量更新和删除不受影响,只有INSERT语句不能被批处理,因为在Persistence Context被刷新之前,INSERT语句已被执行,从而Hibernate知道要分配给持久化实体什么实体标识符 。
如果要解决此问题,则必须通过不同的框架,如jOOQ,执行JDBC批处理插入 。
通过Docker和tmpfs加速集成测试
MySQL和MariaDB在不得不丢弃数据库模式的时候,以及每次新的集成测试即将运行因而重新创建它的时候,是非常慢的 。但是,你可以在Docker和tmpfs的帮助下轻松解决此问题 。
正如我在这篇文章中所解释的,通过映射内存中的数据文件夹,集成测试的运行速度将与有内存数据库(如H2或HSQLDB)时的速度相同 。
对非结构化数据使用JSON
即使是在你使用RDBMS的时候,肯定也有很多次想要存储非结构化数据:
  • 来自客户端,如JSON的数据,需要被解析并插入到我们的系统中 。
  • 可以缓存的图像处理结果以保存再处理
虽然本机不支持,但是你可以轻松地将JAVA对象映射到JSON列 。甚至可以将JSON列类型映射到Jackson JsonNode 。
【MySQL 中使用 JPA + Hibernate 的 9 个高性能技巧】更重要的是,你甚至不必编写这些自定义类型,可以从Maven Central中抓取:
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>1.0.0</version></dependency>使用存储过程来保存数据库
在处理大量数据时,将所有数据移入和移出数据库并不是非常高效 。不过,通过调用存储过程对数据库端进行处理会好很多 。
小心ResultSet流
SQL流在两层应用程序中是很有意义的 。如果你要执行ResultSet流,那么你也得注意JDBC驱动程序 。在MySQL上,你需要将Statement大小设置为Integer.MIN_VALUE 。
然而,对于基于Web的应用程序,分页更为合适 。JPA 2.2甚至引入了对Java 1.8 Stream方法的支持,但执行计划可能不如使用SQL级别分页时那么高效 。
PreparedStatements可能会被仿真
你可能以为,既然Hibernate默认使用PreparedStatements,那么所有语句都是像这样执行的:
MySQL 中使用 JPA + Hibernate 的 9 个高性能技巧

文章插图
 
实际上,更像是这样执行的:
MySQL 中使用 JPA + Hibernate 的 9 个高性能技巧

文章插图
 
除非你设置了useServerPrepStmts MySQL JDBC驱动程序属性,否则PreparedStatements将在JDBC驱动程序级别进行仿真以保存一个额外的数据库 。
始终结束数据库事务
在关系数据库中,每个语句都在给定的数据库事务中执行 。因此,事务是不可选的 。
但是,你应该始终通过提交或回滚来结束当前正在运行的事务 。忘记结束事务可能会导致持续被锁很长时间,同时也会阻止MVCC清理过程回收不再需要的旧元组或索引条目 。




    推荐阅读