我正在尝试使用Java按排序顺序从PostgreSQL表中获取数据。问题出在PostgreSQL的查询计划中-看一下这些查询:
select *
from the_table
order by the_indexed_column asc
;
此查询计划为:
Gather Merge (cost=16673025.39..28912422.53 rows=104901794 width=64)
Workers Planned: 2
-> Sort (cost=16672025.36..16803152.60 rows=52450897 width=64)
Sort Key: "time"
-> Parallel Seq Scan on raw (cost=0.00..4030550.63 rows=52450897 width=64)
顶部的Sort
阻止了数据流,因为它必须首先聚合数据。这对于具有大量数据的排序(例如对于我来说是20GB,因为它们必须保存到磁盘上。
比较此查询:
select *
from raw
order by the_index_column asc
limit 10000000
;
计划:
Limit (cost=0.57..9871396.70 rows=10000000 width=64)
-> Index Scan using raw_time_idx on raw (cost=0.57..124263259.38 rows=125882152 width=64)
可以轻松地流传输这些数据。
我认为PostgreSQL仅针对总查询速度进行了优化,而不针对磁盘使用率和流功能等其他功能进行了优化。有没有一种方法可以让PostgreSQL选择第一个计划以支持第一个计划?
编辑: 这是用于执行查询的代码。末尾的字符串不打印。
Connection database = DriverManager.getConnection(DatabaseConstants.DATABASE_URL, DatabaseConstants.USER, DatabaseConstants.PASSWORD);
String sql = "select " +
"column_a, column_b, some_expression, morestuff " +
"from the_table " +
"order by the_indexed_column asc " +
";";
database.setAutoCommit(false);
PreparedStatement statement = database.prepareStatement(sql);
statement.setFetchSize(1024);
ResultSet set = statement.executeQuery();
System.out.println("Got first results...");
cursor_tuple_fraction
的值分别降低到0.05、0.01和0.0。
PostgreSQL版本:10.7, 驱动程序版本:42.2.5.jre7(Maven中的最新版本(现在为实际)), 操作系统:Fedora 29(最小,顶部带有KDE)
这是日志上带有log_min_duration_statement = 0
的输出:
2019-03-29 17:11:52.532 CET [15068] LOG: database system is ready to accept connections
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.397 ms parse <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.008 ms bind <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.046 ms execute <unnamed>: SET extra_float_digits = 3
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.024 ms parse <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.006 ms bind <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.615 CET [15119] LOG: duration: 0.026 ms execute <unnamed>: SET application_name = 'PostgreSQL JDBC Driver'
2019-03-29 17:12:04.662 CET [15119] LOG: duration: 0.023 ms parse <unnamed>: BEGIN
2019-03-29 17:12:04.662 CET [15119] LOG: duration: 0.006 ms bind <unnamed>: BEGIN
2019-03-29 17:12:04.662 CET [15119] LOG: duration: 0.004 ms execute <unnamed>: BEGIN
2019-03-29 17:12:04.940 CET [15119] LOG: duration: 277.705 ms parse <unnamed>: [the query...]
2019-03-29 17:12:05.162 CET [15119] LOG: duration: 222.742 ms bind <unnamed>/C_1: [the query...]
在此期间,磁盘使用量增加。
答案 0 :(得分:0)
这应该不是问题。通过将具有非零值的setFetchSize
应用于预处理语句来使用游标。
然后PostgreSQL将选择一个计划,该计划可以快速返回第一行,即索引扫描。
如果PostgreSQL仍然选择排序,请从默认值0.1(占总结果集的10%)降低cursor_tuple_fraction
。
记录:这是它在日志中的外观:
duration: 0.126 ms parse S_1: BEGIN
duration: 0.015 ms bind S_1: BEGIN
duration: 0.034 ms execute S_1: BEGIN
duration: 0.998 ms parse S_2: SELECT /* the query */
duration: 1.752 ms bind S_2/C_3: SELECT /* the query */
duration: 0.081 ms execute S_2/C_3: SELECT /* the query */
duration: 0.060 ms execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.065 ms execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.070 ms execute fetch from S_2/C_3: SELECT /* the query */
duration: 0.078 ms execute fetch from S_2/C_3: SELECT /* the query */