Spring Data Repository - 分页大型数据集 (EclipseLink)
Spring Data Repository - Paging large data sets (EclipseLink)
我正在使用 Spring 数据和 EclipseLink JPA 对数据库结果集进行服务器端分页。我一切正常,我得到了预期的分页结果,但我注意到大型数据集(几百万行)的性能受到影响。 return 一页包含 20 个结果大约需要 5 分钟。也许这是意料之中的,但让我担心的是查询输出。
我的日志输出:
SELECT COUNT(filename) FROM document
SELECT filename, datecaptured, din, docdate, docid, doctype, drawer, foldernumber, format, pagenumber, tempfilename, userid FROM document ORDER BY din ASC
我明白为了分页,Spring 需要知道最大行数,所以第一个查询是有意义的。
第二个查询正在提取整个数据库,而我特别只要求 20 个偏移量为 0 的结果(页面)。
Spring/EclipseLink/JPA 是否实际上抓取了整个数据集,然后仅 return 分页请求的子集?
如果是这样,我应该如何修改我的存储库 class 以提高效率?
我的测试用例:
@Test
public void getPagedDocumentsTest() throws IOException {
Page<Document> requestedPage = documentRepository.findAll(new PageRequest(0, 20, Sort.Direction.ASC, "din"));
Assert.assertNotNull("Page is null", requestedPage);
Assert.assertNotNull("Page is empty", requestedPage.getContent());
List<Document> documents = requestedPage.getContent();
LOG.info("{}", documents);
LOG.info("{}", documents.size());
}
我的仓库class:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.example.data.model.Document;
@Repository
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {
}
编辑 - 根据@Chris 的建议
尝试将平台添加到我的属性中,但没有任何区别:
eclipselink.weaving=static
eclipselink.allow-zero-id=true
eclipselink.target-database=SQLServer
eclipselink.logging.level=FINE
还尝试将它添加到我的配置中(我正在使用 Java 配置):
@Bean
public LocalContainerEntityManagerFactoryBean entityManager() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("ExampleUnit");
factory.setPackagesToScan("com.example.data.model");
EclipseLinkJpaVendorAdapter eclipseLinkVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkVendorAdapter.setDatabase(Database.SQL_SERVER);
eclipseLinkVendorAdapter.setDatabasePlatform("SQLServer");
factory.setJpaVendorAdapter(eclipseLinkVendorAdapter);
factory.setDataSource(dataSource());
factory.setJpaProperties(jpaProperties());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
看起来平台设置正确。
[EL Config]: connection: 2015-08-06 12:04:05.691--ServerSession(686533955)--Connection(1896042043)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>SQLServerPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
但都没有帮助。 SQL 查询输出也保持不变。
编辑
从@Chris 找到了一个具有类似答案的相关问题:
EclipseLink generated SQL doesn't include pagination
您应该考虑的一件事是您是否真的需要知道页数/元素总数。如果您 return 从包含数百万个元素的结果集中访问一个页面,您的用户很可能不会对以任何方式浏览所有这些页面感兴趣 :)。也许您的前端以无限滚动方式显示数据,只需要知道是否还有更多页面,而不是页面数。
如果这些情况中的任何一个适用于您,您应该考虑 returning Slice
而不是 Page
,如:
public Slice<MyClass> findByMyField(..);
这样,Count
、Spring 数据将只要求比您最初想要的多一个元素,而不是执行昂贵的操作。如果该元素存在,Slice
将从 hasNext
方法 return 为真。
在我工作的地方,我们最近将 Slices 用于几个大型数据集并使用正确的索引(在 clearing the database cache :) 之后,我们看到了一些非常显着的收益。
我检查过的 EclipseLink 2.5 源我相信支持内置于以下数据库平台的数据库级过滤 classes:
- DB2 平台
- 德比平台
- 火鸟平台
- H2平台
- HANA 平台
- HSQL平台
- 我的SQL平台
- Oracle 平台
- PostgreSQL平台
- SymfowarePlatform
这些中的每一个都覆盖了 printSQLSelectStatement 方法,以利用它们各自的数据库功能来允许在 SQL 本身中进行过滤。其他平台将需要使用 JDBC 过滤,这取决于驱动程序来限制行 - 它们可能能够优化查询,但它是特定于驱动程序的,我相信这就是为什么您的查询需要比您期望的时间更长的原因。
我不太了解 SQL服务器,无法说明它具有哪些可在 SQL 中使用的等效功能,但如果找到它,则需要创建一个 SQLServerPlatform subclass,重写 printSQLSelectStatement 方法,就像在上面 classes 中所做的那样,然后指定要使用的平台 class。还请提交 bug/feature 以将其包含在 EclipseLink 中。
此处描述了其他选项:
http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination
我正在使用 Spring 数据和 EclipseLink JPA 对数据库结果集进行服务器端分页。我一切正常,我得到了预期的分页结果,但我注意到大型数据集(几百万行)的性能受到影响。 return 一页包含 20 个结果大约需要 5 分钟。也许这是意料之中的,但让我担心的是查询输出。
我的日志输出:
SELECT COUNT(filename) FROM document
SELECT filename, datecaptured, din, docdate, docid, doctype, drawer, foldernumber, format, pagenumber, tempfilename, userid FROM document ORDER BY din ASC
我明白为了分页,Spring 需要知道最大行数,所以第一个查询是有意义的。
第二个查询正在提取整个数据库,而我特别只要求 20 个偏移量为 0 的结果(页面)。
Spring/EclipseLink/JPA 是否实际上抓取了整个数据集,然后仅 return 分页请求的子集?
如果是这样,我应该如何修改我的存储库 class 以提高效率?
我的测试用例:
@Test
public void getPagedDocumentsTest() throws IOException {
Page<Document> requestedPage = documentRepository.findAll(new PageRequest(0, 20, Sort.Direction.ASC, "din"));
Assert.assertNotNull("Page is null", requestedPage);
Assert.assertNotNull("Page is empty", requestedPage.getContent());
List<Document> documents = requestedPage.getContent();
LOG.info("{}", documents);
LOG.info("{}", documents.size());
}
我的仓库class:
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import com.example.data.model.Document;
@Repository
public interface DocumentRepository extends PagingAndSortingRepository<Document, String> {
}
编辑 - 根据@Chris 的建议
尝试将平台添加到我的属性中,但没有任何区别:
eclipselink.weaving=static
eclipselink.allow-zero-id=true
eclipselink.target-database=SQLServer
eclipselink.logging.level=FINE
还尝试将它添加到我的配置中(我正在使用 Java 配置):
@Bean
public LocalContainerEntityManagerFactoryBean entityManager() {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPersistenceUnitName("ExampleUnit");
factory.setPackagesToScan("com.example.data.model");
EclipseLinkJpaVendorAdapter eclipseLinkVendorAdapter = new EclipseLinkJpaVendorAdapter();
eclipseLinkVendorAdapter.setDatabase(Database.SQL_SERVER);
eclipseLinkVendorAdapter.setDatabasePlatform("SQLServer");
factory.setJpaVendorAdapter(eclipseLinkVendorAdapter);
factory.setDataSource(dataSource());
factory.setJpaProperties(jpaProperties());
factory.setLoadTimeWeaver(new InstrumentationLoadTimeWeaver());
return factory;
}
看起来平台设置正确。
[EL Config]: connection: 2015-08-06 12:04:05.691--ServerSession(686533955)--Connection(1896042043)--Thread(Thread[main,5,main])--connecting(DatabaseLogin(
platform=>SQLServerPlatform
user name=> ""
connector=>JNDIConnector datasource name=>null
))
但都没有帮助。 SQL 查询输出也保持不变。
编辑
从@Chris 找到了一个具有类似答案的相关问题:
EclipseLink generated SQL doesn't include pagination
您应该考虑的一件事是您是否真的需要知道页数/元素总数。如果您 return 从包含数百万个元素的结果集中访问一个页面,您的用户很可能不会对以任何方式浏览所有这些页面感兴趣 :)。也许您的前端以无限滚动方式显示数据,只需要知道是否还有更多页面,而不是页面数。
如果这些情况中的任何一个适用于您,您应该考虑 returning Slice
而不是 Page
,如:
public Slice<MyClass> findByMyField(..);
这样,Count
、Spring 数据将只要求比您最初想要的多一个元素,而不是执行昂贵的操作。如果该元素存在,Slice
将从 hasNext
方法 return 为真。
在我工作的地方,我们最近将 Slices 用于几个大型数据集并使用正确的索引(在 clearing the database cache :) 之后,我们看到了一些非常显着的收益。
我检查过的 EclipseLink 2.5 源我相信支持内置于以下数据库平台的数据库级过滤 classes:
- DB2 平台
- 德比平台
- 火鸟平台
- H2平台
- HANA 平台
- HSQL平台
- 我的SQL平台
- Oracle 平台
- PostgreSQL平台
- SymfowarePlatform
这些中的每一个都覆盖了 printSQLSelectStatement 方法,以利用它们各自的数据库功能来允许在 SQL 本身中进行过滤。其他平台将需要使用 JDBC 过滤,这取决于驱动程序来限制行 - 它们可能能够优化查询,但它是特定于驱动程序的,我相信这就是为什么您的查询需要比您期望的时间更长的原因。
我不太了解 SQL服务器,无法说明它具有哪些可在 SQL 中使用的等效功能,但如果找到它,则需要创建一个 SQLServerPlatform subclass,重写 printSQLSelectStatement 方法,就像在上面 classes 中所做的那样,然后指定要使用的平台 class。还请提交 bug/feature 以将其包含在 EclipseLink 中。
此处描述了其他选项: http://wiki.eclipse.org/EclipseLink/Examples/JPA/Pagination