本文再细化并举例说明一下他们的差别.
首先函数稳定性分三种 :
volatilestableimmutable
首先创建1个测试表 :
digoal=> create table test (id int, info text);CREATE TABLE
例如 :
create or replace function f_volatile(i_id int) returns text as $$declareresult text;beginupdate test set info='new' where id=i_id returning info into result;return result;end;$$ language plpgsql volatile;
执行这个函数,正常返回 :
如果是immutable或者stable的话,将报错.
下面的函数用来返回一个NUMERIC,然后进行sum运算.
create or replace function f_test() returns numeric as $$declarebeginreturn @H_404_86@1.5;end;$$ language plpgsql volatile;
10W条记录,执行f_test()耗时335毫秒.
digoal=> explain analyze select f_test() from test; QUERY PLAN -------------------------------------------------------------------------------------------------------------- Seq Scan on test (cost=@H_404_86@0.00..@H_404_86@26638.00 rows=@H_404_86@100000 width=@H_404_86@0) (actual time=@H_404_86@0.035..@H_404_86@322.622 rows=@H_404_86@100000 loops=@H_404_86@1) Total runtime: @H_404_86@334.539 ms (@H_404_86@2 rows) Time: @H_404_86@335.035 ms
记住这个执行耗时. 后面要对比f_test()改成stable和immutable后的执行耗时.
单条执行时间 :
如下 :
digoal=> alter function f_volatile(int) stable;ALTER FUNCTIONTime: @H_404_86@0.660 ms
同样的参数值,stable函数多次执行返回的结果应该一致.
digoal=> alter function f_test() stable;ALTER FUNCTIONdigoal=> explain analyze select * from test where id<=f_test()::int;QUERY PLAN------------------------------------------------------------------------------------------------------------------Index Scan using idx_test_1 on test (cost=@H_404_86@0.25..@H_404_86@2.55 rows=@H_404_86@1 width=@H_404_86@21) (actual time=@H_404_86@0.019..@H_404_86@0.024 rows=@H_404_86@2 loops=@H_404_86@1)Index Cond: (id <= (f_test())::integer)Total runtime: @H_404_86@0.054 msTime: @H_404_86@0.926 ms
改回volatile,则不允许走索引. 如下 :
digoal=> explain analyze select * from test where id<=f_test()::int;QUERY PLAN---------------------------------------------------------------------------------------------------------Seq Scan on test (cost=@H_404_86@0.00..@H_404_86@27138.00 rows=@H_404_86@33333 width=@H_404_86@21) (actual time=@H_404_86@0.143..@H_404_86@269.208 rows=@H_404_86@2 loops=@H_404_86@1)Filter: (id <= (f_test())::integer)Rows Removed by Filter: @H_404_86@99998Total runtime: @H_404_86@269.242 msTime: @H_404_86@269.937 ms
另外一个测试是吧f_test()放到结果集部分,而不是where条件里面,stable和immutable的差别也很大 :
digoal=> alter function f_test() stable;ALTER FUNCTIONdigoal=> explain analyze select f_test() from test;QUERY PLAN--------------------------------------------------------------------------------------------------------------Seq Scan on test (cost=@H_404_86@0.00..@H_404_86@26638.00 rows=@H_404_86@100000 width=@H_404_86@0) (actual time=@H_404_86@0.137..@H_404_86@268.707 rows=@H_404_86@100000 loops=@H_404_86@1)Total runtime: @H_404_86@281.684 msTime: @H_404_86@282.248 ms改成immutabledigoal=> alter function f_test() immutable;ALTER FUNCTIONTime: @H_404_86@0.359 msdigoal=> explain analyze select f_test() from test;QUERY PLAN------------------------------------------------------------------------------------------------------------Seq Scan on test (cost=@H_404_86@0.00..@H_404_86@1638.00 rows=@H_404_86@100000 width=@H_404_86@0) (actual time=@H_404_86@0.011..@H_404_86@23.450 rows=@H_404_86@100000 loops=@H_404_86@1)Total runtime: @H_404_86@34.331 msTime: @H_404_86@35.061 ms
3. immutable,和stable非常类似,但是immutable是指在任何情况下,只要参数一致,结果就一致. 而在事务中参数一致则结果一致可以标记为stable而请你不要把它标记为immutable.
另外的显著的区别是优化器对immutable和stable函数的处理上.
如果函数的参数是常量的情况下 :
例如 :
select * from test where id>@H_404_86@3;
digoal=> create or replace function f_test(i_id int) returns int as $$declarebeginreturn i_id;end;$$ language plpgsql immutable;CREATE FUNCTIONTime: @H_404_86@1.020 ms
immutable 测试 :
digoal=> explain analyze select * from test where id<f_test(@H_404_86@50);QUERY PLAN--------------------------------------------------------------------------------------------------------------------Index Scan using idx_test_1 on test (cost=@H_404_86@0.00..@H_404_86@3.15 rows=@H_404_86@50 width=@H_404_86@21) (actual time=@H_404_86@0.007..@H_404_86@0.025 rows=@H_404_86@49 loops=@H_404_86@1)Index Cond: (id < @H_404_86@50)Total runtime: @H_404_86@0.058 ms注意这行 :
stable 测试 :
digoal=> alter function f_test(int) stable;ALTER FUNCTIONdigoal=> explain analyze select * from test where id<f_test(@H_404_86@50);QUERY PLAN--------------------------------------------------------------------------------------------------------------------Index Scan using idx_test_1 on test (cost=@H_404_86@0.25..@H_404_86@3.40 rows=@H_404_86@50 width=@H_404_86@21) (actual time=@H_404_86@0.019..@H_404_86@0.035 rows=@H_404_86@49 loops=@H_404_86@1)Index Cond: (id < f_test(@H_404_86@50))Total runtime: @H_404_86@0.066 ms
另外一组测试 :
digoal=> alter function f_test(int) stable;ALTER FUNCTIONQUERY PLAN-------------------------------------------------------------------------------------------------------------------Result (cost=@H_404_86@0.25..@H_404_86@1638.25 rows=@H_404_86@100000 width=@H_404_86@21) (actual time=@H_404_86@0.146..@H_404_86@50.367 rows=@H_404_86@100000 loops=@H_404_86@1)-> Seq Scan on test (cost=@H_404_86@0.25..@H_404_86@1638.25 rows=@H_404_86@100000 width=@H_404_86@21) (actual time=@H_404_86@0.014..@H_404_86@20.646 rows=@H_404_86@100000 loops=@H_404_86@1)Total runtime: @H_404_86@61.386 ms
当f_test是stable 时,比immutable多One-Time Filter: (f_test(2) > 1)
而当immutable,优化器则将
f_test(2)>1这部分直接优化掉了.
digoal=> alter function f_test(int) immutable;ALTER FUNCTIONQUERY PLAN-------------------------------------------------------------------------------------------------------------Seq Scan on test (cost=@H_404_86@0.00..@H_404_86@1638.00 rows=@H_404_86@100000 width=@H_404_86@21) (actual time=@H_404_86@0.011..@H_404_86@18.801 rows=@H_404_86@100000 loops=@H_404_86@1)Total runtime: @H_404_86@29.839 ms
【prepare statement 注意】
prepare statement请参考 :
digoal=> create or replace function immutable_random() returns numeric as $$declarebeginreturn random();end;$$ language plpgsql immutable;CREATE FUNCTION
创建一个prepare statement.
digoal=> prepare p_test(int) as select $1,immutable_random();PREPARETime: @H_404_86@0.473 ms
执行这个prepared statement :
digoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+-------------------Time: @H_404_86@0.398 msdigoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+-------------------Time: @H_404_86@0.209 msdigoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+-------------------Time: @H_404_86@0.212 msdigoal=>digoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+------------------Time: @H_404_86@0.290 msdigoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+--------------------Time: @H_404_86@0.211 ms
第六次开始使用generic_plan,而immutable function在plan时将被结果常量替换.
digoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+-------------------Time: @H_404_86@0.233 ms
以后再执行这个prepare statement,immutable_random()部分都将得到同样的结果.
digoal=> execute p_test(@H_404_86@1);?column? | immutable_random----------+-------------------Time: @H_404_86@0.165 ms
而把immutable_random()改成volatile或者stable后,immutable_random()都将产生不同的结果,不会发生以上情况.
因为他们在plan时函数不会被结果替换.
【MVCC 注意】
STABLE and IMMUTABLE functions use a snapshot established as of the start of the calling query,whereas VOLATILE functions obtain a fresh snapshot at the start of each query they execute.
例如 :
创建测试表 :
digoal=> create table test(id int,info text);CREATE TABLETime: @H_404_86@50.356 ms
digoal=> create or replace function f_mod(i_id int) returns void as $$declarebeginend;$$ language plpgsql volatile;
测试稳定性的函数 :
digoal=> create or replace function f_test(i_id int) returns bigint as $$declareresult int8;beginperform f_mod(i_id);select count(*) into result from test where id=i_id;return result;end;$$ language plpgsql volatile;
当稳定性=volatile时,修改可以被
select count(*) into result from test where id=i_id; 看到 :
所以更新后结果为0 :
digoal=> alter function f_test(int) stable;ALTER FUNCTIONdigoal=> select f_test(@H_404_86@2);f_test--------@H_404_86@1000
改成immutable,结果还是1000 :
digoal=> create or replace function f_test(i_id int) returns bigint as $$declareresult int8;beginselect count(*) into result from test where id=i_id;return result;end;$$ language plpgsql volatile;CREATE FUNCTION
看不到with的修改 :
digoal=> alter function f_test(int) immutable;ALTER FUNCTION
看不到with的修改 :
digoal=> alter function f_test(int) stable;ALTER FUNCTION
看不到with的修改 :
digoal=> alter function f_test(int) volatile;ALTER FUNCTIONdigoal=> with t1 as ()select f_test(@H_404_86@6);f_test--------@H_404_86@1000
在事务中时,都能看到本事务在前面做的修改 :
digoal=> alter function f_test(int) immutable;ALTER FUNCTIONdigoal=> begin;BEGINUPDATE @H_404_86@1000f_test--------@H_404_86@0digoal=> select f_test(@H_404_86@14);f_test--------@H_404_86@1000digoal=> end;COMMIT
volatile,stable测试略,同上。
【其他】
1. 查看函数的稳定性 :
digoal=> select proname,proargtypes,provolatile from pg_proc where proname='f_test';
proname | proargtypes | provolatile
---------+-------------+-------------
f_test | | i
f_test | 23 | i
(2 rows)
i表示immutable,s表示stable,v表示volatile.
3. stable函数和immutable函数不能直接调用UPDATE这种修改数据库的sql语句. 但是通过perform volatile function或者select volatile function还是会修改到数据,因为Postgresql不会有更深层次的检查.
3.
@L_403_3@