Oracle数据库与JPA和Hibernate 结合使用时的九个高性能技巧 - vladmihalcea


在本文中,我将向您展示 9 个技巧,它们将帮助您在使用 JPA 和 Hibernate 时加快 Oracle 数据库应用程序的速度。
为了充分利用正在使用的关系数据库,您需要确保数据访问层与底层数据库系统产生共鸣,因此,在本文中,我将向您展示一些可以提高性能的技巧您的 Oracle、JPA 和 Hibernate 应用程序。
 
1. 配置缓冲池和操作系统缓存
与任何关系数据库系统一样,Oracle 旨在尽可能减少磁盘访问。
当需要一个页面时,Oracle 会检查缓冲池以查看该页面是否可以从缓存中解析。这是一个合乎逻辑的阅读。如果没有缓存页面,Oracle 会从磁盘加载它并将其存储在缓冲池中。那是物理阅读。这样,下次您请求相同的页面时,它将从缓存而不是从数据库加载。
传统上,数据库系统使用两个日志来标记事务修改:

  • 在撤消日志用于在回退的情况下恢复未提交的更改。在 Oracle 中,撤消日志存储最新未提交的元组与之前状态之间的差异。
  • 在redo_log保证事务的持久性,并且由于缓冲池每一笔交易修改存储每一笔交易执行后,不刷新到磁盘。因此,出于这个原因,缓冲池会在检查点期间定期刷新。

由于基于 Unix 的操作系统有自己的页面缓存,因此使用 Direct I/O(例如,O_DIRECT)堆积数据和索引分区以避免在操作系统缓存和缓冲池中存储相同的页面非常重要。
 
2. 了解所有支持的 SQL 特性
Oracle 支持许多 SQL:2016 标准特性,例如Window Functions、CTE、Recursive CTE、PIVOTMERGE,甚至MATCH_RECOGNIZE是只有 Oracle 才添加支持的子句。
除了这些 SQL 标准特性之外,Oracle 还提供了特定于数据库的特性,例如 MODEL 子句。
Oracle 还提供特定于数据库的功能,例如 MODEL 子句或闪回查询
因此,如果您将数据访问查询限制为您在大学或W3 Schools 中学到的SQL:92 功能列表,您将错过许多可以帮助您解决非常复杂的数据访问需求的功能。
阅读Oracle 文档并熟悉它提供的所有功能非常重要。
而且,仅仅因为您使用 JPA 和 Hibernate,并不意味着您应该只编写 JPQL 和Criteria API查询。JPAEntityManager允许您运行本机 SQL 查询是有充分理由的,因为任何重要的数据库应用程序都需要执行本机 SQL。
 
3.优化执行计划缓存
与 PostgreSQL 和 MySQL 不同,Oracle 提供了一个执行计划缓存,可以让您加快 SQL 查询的执行速度。
执行计划缓存甚至可以为给定的查询存储多个计划,以匹配具有非常倾斜的数据分布的各种绑定参数值。
了解执行计划缓存的工作原理后,您应该配置数据访问层以利用这一非常有用的功能。因此,您应该:

 
4.开启JDBC语句缓存机制
Oracle JDBC Driver 提供了默认禁用的语句缓存机制。因此,为了加快 SQL 查询速度,您应该通过将oracle.jdbc.implicitStatementCacheSize属性设置为正整数值来启用它。
您可以通过JDBC URL 连接字符串以声明方式执行此操作:
jdbc:oracle:thin:@tcp://hpjp:1521/training? oracle.jdbc.implicitStatementCacheSize=100

或者,以编程方式,通过 JDBCDataSource属性:
OracleDataSource dataSource = new OracleDataSource();
dataSource.setDatabaseName("high_performance_java_persistence");
dataSource.setURL(url());
dataSource.setUser(username());
dataSource.setPassword(password());
 
Properties properties = new Properties();
properties.put(
    "oracle.jdbc.implicitStatementCacheSize",
    Integer.toString(cacheSize)
);
dataSource.setConnectionProperties(properties);

 
5.增加默认的JDBC语句fetch大小
与PostgreSQL 和 MySQL预取整个JDBC 的ResultSet 不同,Oracle 使用的提取大小仅为10. 因此,返回50记录的查询需要5次数据库往返以从数据库 Executor 中获取所有数据。
因此,在使用 Oracle 时,您应该始终增加默认提取大小。如果您使用的是 Hibernate,您可以通过hibernate.jdbc.fetch_size配置属性将此更改全局应用于所有 SQL 语句。
例如,如果您正在使用 Spring Boot,则可以在application.properties配置文件中设置此属性,如下所示:

spring.jpa.properties.hibernate.jdbc.fetch_size=100

而且,如果您使用数据库游标来获取Java 8Stream,那么您始终可以使用org.hibernate.fetchSizeJPA 查询提示将提取大小设置为较低的值:
Stream<Post> postStream = entityManager.createQuery("""
    select p
    from Post p
    order by p.createdOn desc
    """, Post.class)
.setHint(QueryHints.HINT_FETCH_SIZE, 10)
.getResultStream();

 
6.启用自动JDBC批处理
对于写入数据,JDBC 语句批处理可以帮助您减少事务响应时间。使用 Hibernate 时,启用批处理只是设置一些配置属性的问题
因此,您应该始终在 Spring Bootapplication.properties配置文件中设置以下 Hibernate 设置:

spring.jpa.properties.hibernate.jdbc.batch_size=10
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true

如果您使用的是乐观锁定并且您至少没有使用 Oracle 12c,那么您应该尝试将 JDBC 驱动程序升级到至少 12c 版本,因为 JDBC 驱动程序向后和向前兼容,并设置以下配置属性:
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true

 
7. 优先SEQUENCE而不是IDENTITY
Oracle 12c 添加了对 IDENTITY 列的支持。但是,您应该使用SEQUENCE标识符生成器来自动增加主键,因为这将允许 Hibernate 对 INSERT 语句使用自动批处理。
 
8. 映射 JPA 实体时使用 Hibernate @RowId
使用 Oracle 时,您可以使用Hibernate的@RowId来注释 JPA 实体,以便 UPDATE 语句可以通过其 ROWID 而不是主键值来定位记录。
 
9. 将非结构化数据存储在 JSON 列类型中
在关系数据库中,最好按照关系模型的原则来存储数据。
但是,您可能还需要存储非结构化数据,在这种情况下,JSON 列可以帮助您处理此类需求。
而 Oracle 21c 将添加一个JSON列类型:
CREATE TABLE book (
  id NUMBER(19, 0) NOT NULL PRIMARY KEY,
  isbn VARCHAR2(15 CHAR),
  properties JSON
)

如果你使用的是Oracle 19C,18C,12C或者,你可以存储JSON对象VARCHAR2,BLOB或CLOB列类型。建议存储小型 JSON 对象,以便它们可以放在一 VARCHAR2(4000)列中,从而适合缓冲池页面。
创建表时,您可以使用 CHECK 约束验证存储的 JSON 对象:

CREATE TABLE book (
  id NUMBER(19, 0) NOT NULL PRIMARY KEY,
  isbn VARCHAR2(15 CHAR),
  properties VARCHAR2(4000)
  CONSTRAINT ENSURE_JSON CHECK (properties IS JSON)
)

要索引具有高select的 JSON 属性,您可以使用 B+Tree 索引:

CREATE INDEX book_properties_title_idx
ON book b (b.properties.title)

要索引具有低select的 JSON 属性,例如布尔值或枚举值,您可以使用BITMAP索引:

CREATE BITMAP INDEX book_properties_reviews_idx
ON book (JSON_EXISTS(properties,'$.reviews'))

您还可以为JSON 列使用通用SEARCH索引,这将允许您匹配键/值 JSON 属性数据:

CREATE SEARCH INDEX book_search_properties_idx
ON book (properties) FOR JSON