本文再细化并举例说明一下他们的差别.
首先函数稳定性分三种 :
volatile stable immutable
首先创建1个测试表 :
digoal=> create table test (id int, info text); CREATE TABLE digoal=> insert into test select generate_series(1,100000),random()::text; INSERT 0 100000 digoal=> create index idx_test_1 on test(id); CREATE INDEX
例如 :
create or replace function f_volatile(i_id int) returns text as $$ declare result text; begin update test set info='new' where id=i_id returning info into result; return result; end; $$ language plpgsql volatile;
执行这个函数,正常返回 :
如果是immutable或者stable的话,将报错.
digoal=> select * from f_volatile(1); f_volatile ------------ new (1 row)
下面的函数用来返回一个NUMERIC,然后进行sum运算.
create or replace function f_test() returns numeric as $$ declare begin return 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=0.00..26638.00 rows=100000 width=0) (actual time=0.035..322.622 rows=100000 loops=1) Total runtime: 334.539 ms (2 rows) Time: 335.035 ms
记住这个执行耗时. 后面要对比f_test()改成stable和immutable后的执行耗时.
单条执行时间 :
digoal=> select f_test(); f_test -------- 1.5 (1 row) Time: 0.192 ms
如下 :
digoal=> alter function f_volatile(int) stable; ALTER FUNCTION Time: 0.660 ms
同样的参数值,stable函数多次执行返回的结果应该一致.
digoal=> alter function f_test() stable; ALTER FUNCTION digoal=> explain analyze select * from test where id<=f_test()::int; QUERY PLAN ------------------------------------------------------------------------------------------------------------------ Index Scan using idx_test_1 on test (cost=0.25..2.55 rows=1 width=21) (actual time=0.019..0.024 rows=2 loops=1) Index Cond: (id <= (f_test())::integer) Total runtime: 0.054 ms (3 rows) Time: 0.926 ms
改回volatile,则不允许走索引. 如下 :
digoal=> explain analyze select * from test where id<=f_test()::int; QUERY PLAN --------------------------------------------------------------------------------------------------------- Seq Scan on test (cost=0.00..27138.00 rows=33333 width=21) (actual time=0.143..269.208 rows=2 loops=1) Filter: (id <= (f_test())::integer) Rows Removed by Filter: 99998 Total runtime: 269.242 ms (4 rows) Time: 269.937 ms
另外一个测试是吧f_test()放到结果集部分,而不是where条件里面,stable和immutable的差别也很大 :
digoal=> alter function f_test() stable; ALTER FUNCTION digoal=> explain analyze select f_test() from test; QUERY PLAN -------------------------------------------------------------------------------------------------------------- Seq Scan on test (cost=0.00..26638.00 rows=100000 width=0) (actual time=0.137..268.707 rows=100000 loops=1) Total runtime: 281.684 ms (2 rows) Time: 282.248 ms 改成immutable digoal=> alter function f_test() immutable; ALTER FUNCTION Time: 0.359 ms digoal=> explain analyze select f_test() from test; QUERY PLAN ------------------------------------------------------------------------------------------------------------ Seq Scan on test (cost=0.00..1638.00 rows=100000 width=0) (actual time=0.011..23.450 rows=100000 loops=1) Total runtime: 34.331 ms (2 rows) Time: 35.061 ms
3. immutable,和stable非常类似,但是immutable是指在任何情况下,只要参数一致,结果就一致. 而在事务中参数一致则结果一致可以标记为stable而请你不要把它标记为immutable.
另外的显著的区别是优化器对immutable和stable函数的处理上.
如果函数的参数是常量的情况下 :
例如 :
select * from test where id> 1+2;
select * from test where id>3;
digoal=> create or replace function f_test(i_id int) returns int as $$ declare begin return i_id; end; $$ language plpgsql immutable; CREATE FUNCTION Time: 1.020 ms
immutable 测试 :
digoal=> explain analyze select * from test where id<f_test(50); QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Index Scan using idx_test_1 on test (cost=0.00..3.15 rows=50 width=21) (actual time=0.007..0.025 rows=49 loops=1) Index Cond: (id < 50) Total runtime: 0.058 ms (3 rows) 注意这行 : Index Cond: (id < 50), f_test(50)已经替换成了结果50.
stable 测试 :
digoal=> alter function f_test(int) stable; ALTER FUNCTION digoal=> explain analyze select * from test where id<f_test(50); QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Index Scan using idx_test_1 on test (cost=0.25..3.40 rows=50 width=21) (actual time=0.019..0.035 rows=49 loops=1) Index Cond: (id < f_test(50)) Total runtime: 0.066 ms (3 rows) 注意这行 : Index Cond: (id < f_test(50)), f_test(50)没有被替换掉.
另外一组测试 :
digoal=> alter function f_test(int) stable; ALTER FUNCTION digoal=> explain analyze select * from test where f_test(2)>1; QUERY PLAN ------------------------------------------------------------------------------------------------------------------- Result (cost=0.25..1638.25 rows=100000 width=21) (actual time=0.146..50.367 rows=100000 loops=1) One-Time Filter: (f_test(2) > 1) -> Seq Scan on test (cost=0.25..1638.25 rows=100000 width=21) (actual time=0.014..20.646 rows=100000 loops=1) Total runtime: 61.386 ms (4 rows)
当f_test是stable 时,比immutable多One-Time Filter: (f_test(2) > 1)
而当immutable,优化器则将
f_test(2)>1这部分直接优化掉了.
digoal=> alter function f_test(int) immutable; ALTER FUNCTION digoal=> explain analyze select * from test where f_test(2)>1; QUERY PLAN ------------------------------------------------------------------------------------------------------------- Seq Scan on test (cost=0.00..1638.00 rows=100000 width=21) (actual time=0.011..18.801 rows=100000 loops=1) Total runtime: 29.839 ms (2 rows)
【prepare statement 注意】
prepare statement请参考 :
digoal=> create or replace function immutable_random() returns numeric as $$ declare begin return random(); end; $$ language plpgsql immutable; CREATE FUNCTION
创建一个prepare statement.
digoal=> prepare p_test(int) as select $1,immutable_random(); PREPARE Time: 0.473 ms
执行这个prepared statement :
digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------- 1 | 0.277766926214099 (1 row) Time: 0.398 ms digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------- 1 | 0.974089733790606 (1 row) Time: 0.209 ms digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------- 1 | 0.800415104720742 (1 row) Time: 0.212 ms digoal=> digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------ 1 | 0.41237005777657 (1 row) Time: 0.290 ms digoal=> execute p_test(1); ?column? | immutable_random ----------+-------------------- 1 | 0.0541226323693991 (1 row) Time: 0.211 ms
第六次开始使用generic_plan,而immutable function在plan时将被结果常量替换.
digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------- 1 | 0.431490630842745 (1 row) Time: 0.233 ms
以后再执行这个prepare statement,immutable_random()部分都将得到同样的结果.
digoal=> execute p_test(1); ?column? | immutable_random ----------+------------------- 1 | 0.431490630842745 (1 row) Time: 0.165 ms digoal=> execute p_test(2); ?column? | immutable_random ----------+------------------- 2 | 0.431490630842745 (1 row) Time: 0.273 ms digoal=> execute p_test(3); ?column? | immutable_random ----------+------------------- 3 | 0.431490630842745 (1 row) Time: 0.149 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 TABLE Time: 50.356 ms digoal=> insert into test select 1,random()::text from generate_series(1,1000); INSERT 0 1000 Time: 5.027 ms
digoal=> create or replace function f_mod(i_id int) returns void as $$ declare begin update test set id=i_id+1 where id=i_id; end; $$ language plpgsql volatile;
测试稳定性的函数 :
digoal=> create or replace function f_test(i_id int) returns bigint as $$ declare result int8; begin perform 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=> select f_test(1); f_test -------- 0 (1 row)
digoal=> alter function f_test(int) stable; ALTER FUNCTION digoal=> select f_test(2); f_test -------- 1000 (1 row)
改成immutable,结果还是1000 :
digoal=> alter function f_test(int) immutable; ALTER FUNCTION digoal=> select f_test(3); f_test -------- 1000 (1 row)
digoal=> create or replace function f_test(i_id int) returns bigint as $$ declare result int8; begin select 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 digoal=> with t1 as ( digoal(> update test set id=id+1 where id=4 digoal(> ) digoal-> select f_test(4); f_test -------- 1000 (1 row)
看不到with的修改 :
digoal=> alter function f_test(int) stable; ALTER FUNCTION digoal=> with t1 as ( update test set id=id+1 where id=5 ) select f_test(5); f_test -------- 1000 (1 row)
看不到with的修改 :
digoal=> alter function f_test(int) volatile; ALTER FUNCTION digoal=> with t1 as ( update test set id=id+1 where id=6 ) select f_test(6); f_test -------- 1000 (1 row)
在事务中时,都能看到本事务在前面做的修改 :
digoal=> alter function f_test(int) immutable; ALTER FUNCTION digoal=> begin; BEGIN digoal=> update test set id=id+1 where id=13; UPDATE 1000 digoal=> select f_test(13); f_test -------- 0 (1 row) digoal=> select f_test(14); f_test -------- 1000 (1 row) digoal=> 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不会有更深层次的检查.