【问题标题】:Efficient way to determine if large set of rows exists in postgres确定postgres中是否存在大量行的有效方法
【发布时间】:2014-03-11 02:03:28
【问题描述】:

我有一个幂等后台处理任务,它需要一行信息,进行一些清理并插入数据库。我的问题是相同的信息可能会被多次处理。

为了解决这个问题,我根据每行数据的信息创建了一个键(散列),并在索引上创建了一个唯一约束以防止重复。

问题:我通过以下方式检查数据库中是否已经存在数据:

SELECT key FROM items WHERE key IN (key,key,key,key).

我发现这个查询有点快,但仍然有一些反应慢

SELECT key FROM items WHERE (key = ANY(VALUES(key),(key)))

然后我对返回的键和我期望的键进行交集,只处理不存在的数据。

这一直很好,直到表达到 1 亿以上,我可以一次检查 100 多个键,这会导致大量的 IO 扫描和检索每一行。

我的问题:有没有更有效的方法来使用唯一约束和索引来检查是否存在?也许有些东西实际上并没有到达每一行?

或者,是否有其他可行的方法?简单地尝试插入和捕获唯一约束违规实际上会更快吗?

简化表定义:

Column         |            Type             |                           Modifiers                           | Storage  | Description
------------------------+-----------------------------+---------------------------------------------------------------+----------+-------------
 id                     | integer                     | not null default nextval('items_id_seq'::regclass) | plain    |
 created_at             | timestamp without time zone | not null                                                      | plain    |
 updated_at             | timestamp without time zone | not null                                                      | plain    |
 key                    | character varying(255)      |                                                               | extended |
 item_attributes        | hstore                      |                                                               | extended |
 item_name              | character varying(255)      |                                                               | plain    |
Indexes:
    "items_pkey" PRIMARY KEY, btree (id)
    "index_items_on_key" UNIQUE, btree (key)

还有一个查询计划:

Nested Loop  (cost=0.10..108.25 rows=25 width=41) (actual time=0.315..2.169 rows=25 loops=1)
   ->  HashAggregate  (cost=0.10..0.17 rows=25 width=32) (actual time=0.071..0.097 rows=25 loops=1)
         ->  Values Scan on "*VALUES*"  (cost=0.00..0.09 rows=25 width=32) (actual time=0.009..0.033 rows=25 loops=1)
   ->  Index Scan using index_items_on_key on items  (cost=0.00..4.32 rows=1 width=41) (actual time=0.076..0.077 rows=1 loops=25)
         Index Cond: ((key)::text = "*VALUES*".column1)
 Total runtime: 2.406 ms

【问题讨论】:

  • 包括您的表定义,包括索引和理想情况下您正在执行的查询的解释计划。
  • 尝试插入并忽略约束违规可能会更干净。它可能不会更快,因为检查约束所需的 IO 会影响执行插入所需的 IO,因此必须以任何一种方式完成。
  • @JustKim 我添加了一个带有索引的表定义。

标签: ruby-on-rails postgresql postgresql-9.1


【解决方案1】:

您没有说明数据来自何处以及如何处理。这是通用方法

with to_be_inserted (id, key) as (
    values (1, 'the_hash'), (2, 'another_hash')
)
insert into items (id, key)
select f(id, key)
from to_be_inserted tbi
where not exists (
    select 1
    from items
    where key = tbi.key
);

如果您将哈希存储为bytea 而不是text,则可能会显着提高性能,因为它是大小的一半,从而使索引也减半。并使用较小的md5 哈希。

如果无法在 SQL 中完成处理,则此键查找可能会更快

with might_be_inserted (key) as (
    values ('hash1'), ('hash2')
)
select key 
from might_be_inserted mbi
where not exists (
    select 1
    from items
    where key = mbi.key
)

【讨论】:

  • 感谢您的想法,在这种情况下,数据来自或处理的位置并不重要,只是说,我宁愿不处理已插入数据库的数据,即键存在于唯一索引中的位置。在这种情况下,每个项目可能会有一些繁重的工作,所以我的目标是不再进行处理。我相信理想的情况是只对存在的键进行索引查找,然后只处理键不存在的数据。
  • @Ross 这就是我的代码所做的。仅当密钥不存在时才会处理。我将其更改为选择一个处理函数以使其明确。这就是为什么我问数据是如何处理的。它来自哪里以及如何处理确实很重要。只有有了这些信息,才有可能围绕它构建特定的代码。
  • 啊,我明白了。在这种情况下,数据将在 ruby​​ 中进行预处理,以确定要插入数据库的具体内容。如果数据库中不存在密钥,我只需要进行该预处理。因此,除非密钥不存在并且我执行预处理,否则我不会拥有 to_be_inserted 数据。
猜你喜欢
  • 2014-05-06
  • 2010-09-19
  • 2017-11-07
  • 1970-01-01
  • 2015-12-09
  • 2023-01-10
  • 1970-01-01
  • 2017-04-03
  • 2014-08-03
相关资源
最近更新 更多