【问题标题】:Replace function used in index替换索引中使用的函数
【发布时间】:2013-07-11 19:25:20
【问题描述】:

当我更改底层函数时,基于函数构建的索引会发生什么情况?

说,我有一个函数clean_name()定义为:

CREATE OR REPLACE FUNCTION clean_name(n text)
RETURNS TEXT AS
$BODY$
DECLARE
 rec TEXT;
BEGIN
 EXECUTE
  'SELECT Regexp_replace(' || quote_literal(n) || ', ''[a-z]'', '''', ''g'');'
 INTO rec;
RETURN rec;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE
;

然后创建一个索引:

CREATE INDEX my_table_upper_name_btree
ON schema.my_table USING GIST (my_text_field);

但后来我决定重新定义删除大写字母的函数。我创建的索引会发生什么?它会自行改变吗?我还要DROPCREATE 吗? VACUUM [ANALYZE] [FULL]?

(所讨论的功能类似,而是使用了一系列相当长的替代品,这些替代品仍在调整中,但预计会稳定。)

【问题讨论】:

  • 无论版本如何,我都对答案感兴趣。
  • 那需要说明——最好附上你实际使用的版本。

标签: function postgresql indexing plpgsql


【解决方案1】:

IMMUTABLE 表示“不改变”或“不可改变”。为了严格避免违反该规则,您必须删除函数和依赖它的所有内容,然后重新创建它和使用它的索引。

如果您在原地替换该功能,您需要对后果负责。出于这个原因,我个人认为 PostgreSQL 应该禁止 OR REPLACE 使用 IMMUTABLE 函数,从而迫使您跳过额外的环节,例如设置 ignore_immutable_checks_even_though_it_might_cause_incorrect_queries 配置选项。

如果您更改不可变函数的行为,则基于该函数的索引无效。服务器无法判断函数的行为是否发生了变化;您可能只是将其替换为在各个方面都具有相同行为的优化版本。因此,它不会使您的索引无效,尽管它可能应该这样做,因为如果您的函数的行为确实不同,您可能会根据该函数获得不正确的查询结果

【讨论】:

  • 服务器无法判断函数的行为是否发生了变化当函数是 inline 时可以,如我的示例所示。 Check this answer
【解决方案2】:

如果更改函数,则必须重建索引。

create table t (i integer);
insert into t (i)
select generate_series(1, 100000);
analyze t;

一个返回相反整数的简单函数:

create or replace function f(i integer)
returns integer as $$
select i * -1;
$$ immutable language sql;

以及上面的索引:

create index t_i_index on t(f(i));

使用的索引:

explain select * from t order by f(i);
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Index Scan using t_i_index on t  (cost=0.00..3300.26 rows=100000 width=4)

现在函数改为返回整数本身:

create or replace function f(i integer)
returns integer as $$
select i;
$$ immutable language sql;

并且不再使用索引:

explain select * from t order by f(i);
                          QUERY PLAN                           
---------------------------------------------------------------
 Sort  (cost=11116.32..11366.32 rows=100000 width=4)
   Sort Key: i
   ->  Seq Scan on t  (cost=0.00..1443.00 rows=100000 width=4)

如果索引被重建

reindex index t_i_index;

再次使用:

explain select * from t order by f(i);
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Index Scan using t_i_index on t  (cost=0.00..4376.26 rows=100000 width=4)

【讨论】:

  • 好吧,不出意外。我希望“REINDEX”也能说服规划者再次使用索引,不是吗?
  • @Dylan 是的,重新索引有效且更简单。我已经更新了答案。
【解决方案3】:

我很惊讶地发现 Postgres 如此依赖其管理员的良好行为来防止更改(“曾经”)被索引使用的函数。我实际上认为这是非常有问题的。没有什么可以阻止错误的行为,我认为这是一个设计问题,而不是用户错误。

所以我决定制作一个方便的小系统来使用事件触发器(以及一点点正则表达式黑客)来执行这种强制。我认为它的保水性很好,但我还没有以任何高压方式对其进行测试。

使用这些:

CREATE OR REPLACE FUNCTION pg_index_monitor()
        RETURNS event_trigger LANGUAGE plpgsql AS $$
DECLARE
    obj record;
BEGIN
    FOR obj IN (
        WITH
            index_functions AS
        (
        select
            unnest(regexp_matches(indexprs, '(?<=(funcid(\s)))(\d+)', 'g'))::oid as objid
        ,   indexrelid
        FROM pg_index
        WHERE
            indexprs IS NOT NULL
        )
        SELECT
            *
        FROM pg_event_trigger_ddl_commands()
        JOIN index_functions USING (objid)
        JOIN pg_class ON indexrelid = oid
        where
            object_type = 'function'
    )
    LOOP
        RAISE EXCEPTION 'This function cannot be modified as it is being used by index "%".', obj.relname;
    END LOOP;
END
$$
;

DROP EVENT TRIGGER IF EXISTS pg_index_monitor_trigger;
CREATE EVENT TRIGGER pg_index_monitor_trigger
   ON ddl_command_end
EXECUTE FUNCTION pg_index_monitor();

然后,如果我有一个索引(旧的或新的)依赖于正在更新的函数,它将引发错误。

CREATE INDEX some_index ON some_table (test(some_column));

随后尝试编辑该链接函数:

CREATE OR REPLACE FUNCTION test(text) RETURNS text AS $$
select 'abc'::text;
$$
LANGUAGE sql
IMMUTABLE
;

[P0001] ERROR: This function cannot be modified as it is being used by index "some_index".

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 1970-01-01
    • 2021-03-04
    • 2017-12-26
    • 1970-01-01
    • 1970-01-01
    • 2013-04-21
    相关资源
    最近更新 更多