相信大伙肯定使用过视图,那物化视图又是什么呢?

实际上物化视图(materialized View)跟视图(View)类似,也是一个视图名字对应一个 SQL 查询语句。

不同的是,物化视图定义的时候需要使用关键字 materialized,它会把结果集保存起来,查询的时候直接读取保存的结果集,而不需要扫描原始表。

如果说视图是虚拟表,那么物化视图可以说是实体表。

接下来我们来做几个实验看看他们实际的差别是怎么样的。

tpch=# create  view test1 as SELECT * FROM orders  c where o_orderdate = '1995-05-02';
CREATE VIEW
Time: 3.463 ms
tpch=# create materialized view test2 as SELECT * FROM orders  c where o_orderdate = '1995-05-02';
SELECT 3667
Time: 504.359 ms
tpch=# explain (costs off,analyze) select count(1) from test1;
                                           QUERY PLAN                                           
------------------------------------------------------------------------------------------------
 Finalize Aggregate (actual time=500.128..501.252 rows=1 loops=1)
   ->  Gather (actual time=499.998..501.246 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate (actual time=492.542..492.543 rows=1 loops=3)
               ->  Parallel Seq Scan on orders c (actual time=0.343..492.035 rows=1222 loops=3)
                     Filter: (o_orderdate = '1995-05-02'::date)
                     Rows Removed by Filter: 2998777
 Planning Time: 0.113 ms
 Execution Time: 501.343 ms
(10 rows)

Time: 502.113 ms
tpch=# explain (costs off,analyze) select count(1) from test2;
                              QUERY PLAN                              
----------------------------------------------------------------------
 Aggregate (actual time=0.623..0.625 rows=1 loops=1)
   ->  Seq Scan on test2 (actual time=0.014..0.364 rows=3667 loops=1)
 Planning Time: 0.113 ms
 Execution Time: 0.651 ms
(4 rows)

Time: 1.496 ms

tpch=# SELECT pg_relation_filepath('test1');
 pg_relation_filepath 
----------------------
(1 row)

Time: 0.660 ms
tpch=# SELECT pg_relation_filepath('test2');
 pg_relation_filepath 
----------------------
 base/16918/41807
(1 row)
Time: 0.290 ms

通过上面的实验我们得出两个结论:

  • 物化视图是真实存在的,而视图只是一个定义。

  • 创建物化视图的时间比较比视图长,但是执行查询比视图短,因为物化视图需要真实执行然后保存结果集。而且可以看到物化视图是没有Filter,因为它已经存储了这个查询条件的结果。

tpch=# delete from  orders  where o_orderdate = '1995-05-02' and o_orderkey ='6755';
DELETE 1
Time: 1262.232 ms (00:01.262)
tpch=# select count(1) from test1;
 count 
-------
  3666
(1 row)

Time: 485.321 ms
tpch=# select count(1) from test2;
 count 
-------
  3667
(1 row)

Time: 0.753 ms

可以看到我们删除了原表一条数据, 但物化视图却没有更新到。

因为PostgreSQL目前不支持物化视图的自动/增量更新。 如果原始表数据有更改,必须使用命令下面命令对物化视图的数据做全更新(非增量更新):refresh materialized view xxxxx;

物化视图支持创建索引,但是无法执行 DML 操作,如下图实验所示:

tpch=# create index o_id on test1(o_orderkey);
ERROR:  cannot create index on relation "test1"
DETAIL:  This operation is not supported for views.
Time: 0.443 ms
tpch=# create index o_id on test2(o_orderkey);
CREATE INDEX
Time: 6.607 ms


tpch=# INSERT INTO test1
tpch-# (o_orderkey, o_custkey, o_orderstatus, o_totalprice, o_orderdate, o_orderpriority, o_clerk, o_shippriority, o_comment)
tpch-# VALUES(6755, 662980, 'O', 28349.44, '1995-05-02', '2-HIGH         ', 'Clerk#000002720', 0, 'ully above the asymptotes. even accounts haggle fluf');
INSERT 0 1

tpch=# INSERT INTO test2
(o_orderkey, o_custkey, o_orderstatus, o_totalprice, o_orderdate, o_orderpriority, o_clerk, o_shippriority, o_comment)
VALUES(6755, 662980, 'O', 28349.44, '1995-05-02', '2-HIGH         ', 'Clerk#000002720', 0, 'ully above the asymptotes. even accounts haggle fluf');
ERROR:  cannot change materialized view "test2"


tpch=# delete from test2 where o_orderkey=6755;
ERROR:  cannot change materialized view "test2"

tpch=# delete from test1 where o_orderkey=6755;
DELETE 1

tpch=# update test2 set o_orderkey='123' where o_orderkey='6759';
ERROR:  cannot change materialized view "test2"
Time: 1.081 ms
tpch=# update test1 set o_orderkey='123' where o_orderkey='6759';
UPDATE 0
Time: 1292.286 ms (00:01.292)

总结

使用PostgreSQL时,如果一个视图的查询耗时很大,可以考虑使用物化视图。因为物化视图把结果集以表的方式记录下来,下次查询时候,直接使用顺序扫描返回结果。

如果原始表的数据更新频繁,而物化视图刷新很慢,则不太适合使用物化视图。

最终是否要使用物化视图,以及如何使用物化视图,要结合具体情况决定。