Dre*_*led 5 postgresql postgresql-9.2
我目前正在研究Postgres 9.2中的复杂排序问题.您可以在此处找到本问题(简化)中使用的源代码:http://sqlfiddle.com/#!12/9857e/11
我有一个巨大的(>> 20Mio行)表,其中包含不同类型的各种列.
CREATE TABLE data_table
(
id bigserial PRIMARY KEY,
column_a character(1),
column_b integer
-- ~100 more columns
);
Run Code Online (Sandbox Code Playgroud)
假设我想在2列(ASC)上对此表进行排序.但是我不想用简单的Order By来做那个,因为后来我可能需要在排序的输出中插入行,用户可能只希望一次看到100行(排序的输出).
为了实现这些目标,我执行以下操作:
CREATE TABLE meta_table
(
id bigserial PRIMARY KEY,
id_data bigint NOT NULL -- refers to the data_table
);
--Function to get the Column A of the current row
CREATE OR REPLACE FUNCTION get_column_a(bigint)
RETURNS character AS
'SELECT column_a FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Function to get the Column B of the current row
CREATE OR REPLACE FUNCTION get_column_b(bigint)
RETURNS integer AS
'SELECT column_b FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Creating a index on expression:
CREATE INDEX meta_sort_index
ON meta_table
USING btree
(get_column_a(id_data), get_column_b(id_data), id_data);
Run Code Online (Sandbox Code Playgroud)
然后我将data_table的Id复制到meta_table:
INSERT INTO meta_table(id_data) (SELECT id FROM data_table);
Run Code Online (Sandbox Code Playgroud)
稍后我可以使用类似的简单插入向表中添加其他行.
要获得行900000 - 900099(100行)我现在可以使用:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
ORDER BY 1,2,3 OFFSET 900000 LIMIT 100;
Run Code Online (Sandbox Code Playgroud)
(如果我想要所有数据,可以在data_table上使用额外的INNER JOIN.)
结果计划是:
Limit (cost=498956.59..499012.03 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..554396.21 rows=1000000 width=8)
Run Code Online (Sandbox Code Playgroud)
这是一个非常有效的计划(Postgres 9.2中的Index Only Scans是新的).
但是,如果我想要获得20'000'000 - 20'000'099(100行)的行数,该怎么办?相同的计划,更长的执行时间.好吧,为了提高Offset性能(改善PostgreSQL中的OFFSET性能),我可以做到以下(让我们假设我将每100'000行保存到另一个表中).
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE (get_column_a(id_data), get_column_b(id_data), id_data ) >= (get_column_a(587857), get_column_b(587857), 587857 )
ORDER BY 1,2,3 LIMIT 100;
Run Code Online (Sandbox Code Playgroud)
这运行得更快.结果计划是:
Limit (cost=0.51..61.13 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.51..193379.65 rows=318954 width=8)
Index Cond: (ROW((get_column_a(id_data)), (get_column_b(id_data)), id_data) >= ROW('Z'::bpchar, 27857, 587857))
Run Code Online (Sandbox Code Playgroud)
到目前为止,一切都很完美,postgres做得很好!
我们假设我想将第二列的顺序更改为DESC.
但是后来我必须更改我的WHERE子句,因为>运算符会比较两个列ASC.上述相同的查询(ASC Ordering)也可以写成:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE
(get_column_a(id_data) > get_column_a(587857))
OR (get_column_a(id_data) = get_column_a(587857) AND ((get_column_b(id_data) > get_column_b(587857))
OR ( (get_column_b(id_data) = get_column_b(587857)) AND (id_data >= 587857))))
ORDER BY 1,2,3 LIMIT 100;
Run Code Online (Sandbox Code Playgroud)
现在,计划更改和查询变得缓慢:
Limit (cost=0.00..1095.94 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..1117877.41 rows=102002 width=8)
Filter: (((get_column_a(id_data)) > 'Z'::bpchar) OR (((get_column_a(id_data)) = 'Z'::bpchar) AND (((get_column_b(id_data)) > 27857) OR (((get_column_b(id_data)) = 27857) AND (id_data >= 587857)))))
Run Code Online (Sandbox Code Playgroud)
如何使用DESC-Ordering的高效旧计划?
您有什么更好的想法如何解决问题?
(我已经尝试使用自己的运算符类声明自己的类型,但这太慢了)
你需要重新考虑你的方法。从哪里开始?这是一个明显的例子,基本上说明了您对 SQL 所采用的函数式方法在性能方面的限制。函数在很大程度上是计划程序不透明的,并且您将强制data_table对检索的每一行进行两次不同的查找,因为存储过程的计划无法折叠在一起。
现在,更糟糕的是,您正在根据另一个表中的数据对一个表建立索引。这可能适用于仅附加工作负载(允许插入但不允许更新),但如果 data_table 可以应用更新,则它将不起作用。如果 data_table 中的数据发生更改,索引将返回错误的结果。
在这些情况下,您最好总是以显式方式写入连接,并让规划者找出检索数据的最佳方法。
现在您的问题是,当您更改第二列的顺序时,索引的用处会大大降低(磁盘 I/O 方面也会更加密集)。另一方面,如果 data_table 上有两个不同的索引并且有显式连接,PostgreSQL 可以更轻松地处理这个问题。
| 归档时间: |
|
| 查看次数: |
1064 次 |
| 最近记录: |