jer*_*uki 7 postgresql count database-performance postgresql-performance
我在postgresql中有一个表,其中包含一个不断更新的数组.
在我的应用程序中,我需要获取该数组列中不存在特定参数的行数.我的查询如下所示:
select count(id)
from table
where not (ARRAY['parameter value'] <@ table.array_column)
Run Code Online (Sandbox Code Playgroud)
但是当增加行的数量和该查询的执行量(每秒几次,可能是数百或数千)时,性能会下降很多,在我看来,postgresql中的计数可能具有线性执行顺序(I我不完全确定这一点.
基本上我的问题是:
是否有一种我不知道的现有模式适用于这种情况?什么是最好的方法呢?
你能给我的任何建议都会非常感激.
PostgreSQL实际上支持数组列上的GIN索引.不幸的是,它似乎不可用NOT ARRAY[...] <@ indexed_col,并且GIN索引无论如何都不适合经常更新的表.
演示:
CREATE TABLE arrtable (id integer primary key, array_column integer[]);
INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;
explain (buffers, analyze) select count(id)
from arrtable
where not (ARRAY[1] <@ arrtable.array_column);
Run Code Online (Sandbox Code Playgroud)
不幸的是,这表明我们不能使用索引编写.如果你不否定它可以使用的状态,这样你就可以搜索和计算的行做包含搜索元素(删除NOT).
你可以使用索引来算的条目做包含目标值,然后从所有条目的数量减去结果.由于countPostgreSQL(9.1及更早版本)中表中的所有行都很慢并且需要顺序扫描,因此这实际上比当前查询慢.如果您打开了b树索引,则在9.2上可能会使用仅索引扫描对行进行计数id,在这种情况下,这可能实际上是正常的:
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
Run Code Online (Sandbox Code Playgroud)
它保证比Pg 9.1及更低版本的原始版本表现更差,因为除了原始版本所需的seqscan之外,它还需要GIN索引扫描.我现在已经在9.2上对它进行了测试,它似乎确实使用了一个指数来计算,因此值得探索9.2.使用一些不那么简单的虚拟数据:
drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
Run Code Online (Sandbox Code Playgroud)
请注意,像这样的GIN索引会减慢LOT的更新速度,并且首先创建起来非常慢.它不适合那些根本没有更新的表 - 比如你的表.
更糟糕的是,使用此索引的查询占用原始查询的两倍,最多只占同一数据集的一半.对于索引不是非常有选择性的情况(例如ARRAY[1]原始查询的4s vs 2s),这是最糟糕的.在索引具有高度选择性的情况下(即:不是很多匹配ARRAY[199]),它在原始的3秒内运行大约1.2秒.这个索引根本不值得拥有这个查询.
这里有什么教训?有时,正确的答案就是进行顺序扫描.
由于不会为你的命中率做,无论是作为@debenhur建议保持与触发器物化视图,或尝试反转阵列是一个参数列表,该条目并不会有那么你可以使用其主旨在于指数@maniek建议.
\n\n\n是否存在 I\xe2\x80\x99m 不知道适用于这种情况的现有模式?最好的方法是什么?
\n
在这种情况下,最好的选择可能是标准化您的架构。将数组拆分成一个表。在属性表上添加 B 树索引,或对主键进行排序,以便可以通过property_id.
CREATE TABLE demo( id integer primary key );\nINSERT INTO demo (id) SELECT id FROM arrtable;\nCREATE TABLE properties (\n demo_id integer not null references demo(id),\n property integer not null,\n primary key (demo_id, property)\n);\nCREATE INDEX properties_property_idx ON properties(property);\nRun Code Online (Sandbox Code Playgroud)\n\n然后您可以查询属性:
\n\nSELECT count(id) \nFROM demo \nWHERE NOT EXISTS (\n SELECT 1 FROM properties WHERE demo.id = properties.demo_id AND property = 1\n)\nRun Code Online (Sandbox Code Playgroud)\n\n我预计这会比原始查询快很多,但实际上对于相同的示例数据来说,结果是一样的;它的运行时间与您的原始查询相同 2 到 3 秒。这是同样的问题,搜索不存在的东西比搜索存在的东西慢得多;如果我们正在查找包含属性的行,我们可以避免 seqscandemo并properties直接扫描匹配的 ID。
同样,对包含数组的表进行 seq 扫描也能完成这项工作。
\n| 归档时间: |
|
| 查看次数: |
3265 次 |
| 最近记录: |