四时宝库

程序员的知识宝库

PostgreSQL技术内幕15:物化视图(postgresql逻辑结构图解)

0.简介

本节主要介绍PG的物化视图,从物化视图产生背景到使用方式,实现思路以及源码分析多个方面来对物化视图进行详细的分析。

1.物化视图简介(背景和概念)

了解物化视图需要先对视图有所了解,视图可以看作是一个虚拟的表,用一个名字来代替一个复杂的语句,在存储层面来讲,其存储的是一条创建时的语句而不是实际去存储数据。

通过视图,屏蔽了外部使用的复杂sql,但是每次查询需要扫描获取结果集(也就是执行创建视图对应结果的sql),这明显会带来效率上的问题 。为了解决这个问题,就出现了物化视图,其和视图差异在于,物化视图会存储自己的结果集,下次读取的时候直接读取自己的结果集,而不需要扫描原始表,物化视图的结果集的存储和扫描方式和普通表相同。

2.物化视图使用方式

2.1 创建物化视图

CREATE MATERIALIZED VIEW [ IF NOT EXISTS ] name
AS query [ WITH [ NO ] DATA ];

和普通视图相比,前面多了materialized关键字,with no data是是否在创建时填充数据,如果带着WITH NO DATA表示创建时不填充数据,要查询视图结果集,需要执行刷新语句。

2.2 查询物化视图

物化视图的查询和正常表的查询使用一致。

2.3 刷新物化视图

REFRESH MATERIALIZED VIEW [ CONCURRENTLY ] name [ WITH [ NO ] DATA ];

CONCURRENTLY可以支持刷新物化视图时其他连接的并发读取。其不能和with no data一起使用。

2.4 修改物化视图

ALTER MATERIALIZED VIEW [ IF EXISTS ] name
RENAME [ COLUMN ] column_name TO new_column_name;

ALTER MATERIALIZED VIEW [ IF EXISTS ] name
RENAME TO new_name;

ALTER MATERIALIZED VIEW [ IF EXISTS ] name
SET SCHEMA new_schema;

第一个用来修改物化视图列名称,第二个用来修改物化视图名称,第三个用来修改物化视图模式。

2.5 物化视图删除

DROP MATERIALIZED VIEW [ IF EXISTS ] name [ CASCADE | RESTRICT ];

删除直接使用drop即可。

3.物化视图实现原理

物化视图实现主要需要解决的问题点是:

1)数据如何存储

PG对于物化视图的数据存储采用的是和表存储结构一致,把其当成一张表来做处理。

2)如何和源表(也可以叫做基表)同步

目前PG仅仅是提供了手动刷新的方式来和源表来做同步,是一种全量更新的手段,如果想做定期更新可以使用一些触发器,函数定义等手段;这点来说可以参考starrocks,其采用定时任务提供异步的物化视图,和分区版本概念来做到增量同步,也支持同步的物化视图,主要通过关联更新来实现多个部分的联动更新。

4.物化视图源码分析

PG源码主要来看创建部分,即如何物化。

exec_simple_query
--> pg_parse_query
--> pg_analyze_and_rewrite
--> pg_plan_queries
--> PortalStart
--> PortalRun
    --> PortalRunUtility    // Execute a utility statement inside a portal.
        --> ProcessUtility
            --> standard_ProcessUtility
                --> ProcessUtilitySlow
                    /*
                     * 执行步骤:
                     *         1. 创建表,准备存储结果集
                     *         2. 查询重写(物化视图中的查询语句)
                     *         3. 生成查询的执行计划
                     *         4. 执行获取查询语句的结果集
                     */
                    --> ExecCreateTableAs  
                        // Create the tuple receiver object and insert info it will need
                        -->  CreateIntoRelDestReceiver  // 结果集输入到IntoRel中,新建的表中
                        --> QueryRewrite
                        --> pg_plan_query
                            --> standard_planner
                                --> subquery_planner
                                    --> grouping_planner
                                        --> query_planner
                                            --> make_one_rel
                                --> create_plan
                                    --> create_scan_plan
                        --> CreateQueryDesc  /* Create a QueryDesc, redirecting output to our tuple receiver */
                        --> ExecutorStart
                        --> ExecutorRun
                            --> standard_ExecutorRun
                                // 1. 建表
                                --> intorel_startup
                                    --> create_ctas_internal    //Actually create the target table
                                        --> DefineRelation // 建表
                                            --> heap_create_with_catalog
                                                --> heap_create
                                        --> StoreViewQuery  // Use the rules system to store the query for the view.
                                            --> UpdateRangeTableOfViewParse 
                                            --> DefineViewRules
                                                --> DefineQueryRewrite  // Set up the ON SELECT rule.
                                                    --> InsertRule // 插入的规则,重写为新的物化表,并不是源表
                                    --> SetMatViewPopulatedState
                                // 2. 执行查询语句,结果集存入物化的表中
                                --> ExecutePlan
                                    --> ExecScan    // 扫描获取tuple
                                        --> ExecScanFetch
                                            --> SeqNext
                                                --> table_beginscan
                                    --> intorel_receive  // receive one tuple
                                        --> table_tuple_insert  // 将查询到的tuple slot插入到创建的表中
                                            --> heapam_tuple_insert
                                                --> ExecFetchSlotHeapTuple
                                                    --> tts_buffer_heap_materialize
                                                        --> heap_copytuple
                                                    --> tts_buffer_heap_get_heap_tuple
                                                --> heap_insert // 插入到表中,找到指定的page,插入tuple。
                                                    --> heap_prepare_insert
                                                    --> RelationPutHeapTuple    
                        --> ExecutorEnd

--> PortalDrop

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接