Materialized Views
Overview
A materialized view is a table that actually contains rows, but behaves like a view. That is, the data in the table changes when the data in the underlying tables changes.
There are several different types of materialized views:
- Snapshot materialized views* are the simplest to implement. They are only updated when manually refreshed.
- Eager materialized views are updated as soon as any change is made to the database that would affect it. Eagerly updated materialized views may have incorrect data if the view it is based on has dependencies on mutable functions like now().
- Lazy materialized views are updated when the transaction commits. They too may fall out of sync with the base view if the view depends on mutable functions like now().
- Very Lazy materialized views are functionally equivalent to Snapshot materialized views. The only difference is that changes are recorded incrementally and applied when the table is manually refreshed. (This may be faster than a full snapshot upon refresh.)
In this document we will only discuss the Snapshot Materialized Views implementation.
Why Materialized Views ?
Before we get too deep into how to implement materialized views, let’s first examine why we may want to use materialized views.
You may notice that certain queries are very slow. You may have exhausted all the techniques in the standard bag of techniques to speed up those queries.
In the end, you realize that getting the queries to run as fast as you like simply isn’t possible without completely restructuring the data.
What you end up doing is storing pre-queried bits of information so that you don’t have to run the real query when you need the data.
This is typically called “caching” outside of the database world.
What you are really doing is creating a materialized view. You are taking a view and turning it into a real table that holds real data, rather than a gateway to a SELECT
query.
Implementation
Candidates for Snapshot Materialized Views are views that are based on slowly-changing data. The Snapshot Materialized Views is actually functioning as a cache. The Snapshot Materialized View is refreshed per request. The Snapshot Materialized View definitions are stored in the materialized_views table.
Column | Type | Modifiers | Description |
---|---|---|---|
mv_name | name | not null | the Materialized View name |
v_name | name | not null | the original view name on which mv_name is based |
refresh_rate_in_sec | integer | the Materialized View refresh rate in seconds | |
last_refresh | timestamp with time zone | the timestamp of the last refresh | |
avg_cost_ms | integer | not null default 0 | the average cost of a refresh operation on the Materialized View in miliseconds. |
min_refresh_rate_in_sec | integer | default 0 | the min refresh rate allowed when refresh is called with teh force flag |
custom | boolean | default false | flag indicating if a Materialized View is custom |
active | boolean | default true | flag indicating if a Materialized View is active |
Flow
- Create the Materialized View by calling:
CreateMaterializedView
- if you are creating a new product viewCreateMaterializedViewAs
- preserves the original view name, The original view will be renamed and the new product Materialized View will have the original view name.CreateCustomMaterializedView
- if you are creating a new custom viewCreateCustomMaterializedViewAs
- preserves the original view name, The original view will be renamed and the new custom Materialized View will have the original view name.
- If your Snapshot Materialized View is
my_mt
you should create Stored Procedures:MtDropmy_mtIndexes
- Drops indexes onmy_mt
MtCreatemy_mtIndexes
- Creates needed indexes onmy_mt
Those indexes should be defined in the “Snapshot Materialized Views Index Definitions Section” in
post_upgrade/0020_create_materialized_views.sql
file.Those SP are called automatically when a Snapshot Materialized View is refreshed to boost refresh performance.
3) You can call IsMaterializedViewRefreshed
to check if it is time to refresh the view and if yes call RefreshMaterializedView
manually.
or
You can define a cron job that calls RefreshAllMaterializedViews
that loops over all Snapshot Materialized Views and refreshes it automatically
RefreshAllMaterializedViews
recieves a boolean v_force
flag, Please set this flag to false
when calling it from a cron job
in order to update the materialized views only when needed.
(This SP is called with v_force = true
after create/upgrade DB)
There are 4 additional functions :
CreateAllMaterializedViewsiIndexes
- Creates indexes for all Snapshot Materialized viewsDropMaterializedView
- Drops the Materialized ViewDropAllMaterializedViews
- Drop all Materialized ViewsUpdateMaterializedViewRefreshRate
- Updates the Materialized View refresh rateUpdateMaterializedViewMinRefreshRate
- Updates the Materialized View minimal refresh rateActivateMaterializedView
- activates/decativates a Materialized ViewActivateAllMaterializedViews
- activates/decativates all Materialized Views
Example of possible post_upgrade/0020_create_materialized_views.sql
This file includes only product Materialized Views for Custom Materialized Views please refer to the Customization section
/******************************************************************************************************
Snapshot Materialized Views Definitions Section
******************************************************************************************************/
select CreateMaterializedViewAs('vms', 300);
/******************************************************************************************************
Snapshot Materialized Views Index Definitions Section
******************************************************************************************************/
create or replace function MtCreatevmsIndexes()
returns void
as $procedure$
begin
create index vms_index on vms (vm_guid);
end; $procedure$
language plpgsql;
create or replace function MtDropvmsIndexes()
returns void
as $procedure$
begin
drop index if exists vms_index cascade;
end; $procedure$
language plpgsql;
select RefreshMaterializedView('vms');
Upgrade
When the DB is upgraded, all Materilized Views are dropped in the pre-upgrade step in order to reflect any change that may be done in the original views. The Materilized Views are recreated in the post-upgrade step insuring that after the upgrade the Materilized Views are updated and play the same role as before it. NOTE : Materialized Views are automatically refreshed upon create/upgrade
Customization
In addition, you can create a file named create_materialized_views.sql
under dbscripts/upgrade/post_upgrade/custom/
This file may include other custom materialized views settings and is executed by the create/upgrade database scripts.
NOTE: If from any reason you want to drop a Snapshot Materialized View that was setup in the product context, you can always do that by calling DropMaterializedView
in create_materialized_views.sql
If create_materialized_views.sql
is executed manually :
- verify that the the user that runs the psql is engine so the objects created will have the same credentials
- the custom dir and
create_materialized_views.sql
file under it should have the following permissions:drwxr-xr-x 2 root root custom -rw-r--r-- 1 root root create_materialized_views.sql
Example
This is an example of how dbscripts/upgrade/post_upgrade/custom/create_materialized_views.sql
may look like:
/******************************************************************************************************
Snapshot Materialized Views Definitions Section
******************************************************************************************************/
select CreateCustomMaterializedViewAs('vds', 300);
/******************************************************************************************************
Snapshot Materialized Views Index Definitions Section
******************************************************************************************************/
create or replace function MtCreatevdsIndexes()
returns void
as $procedure$
begin
create index vds_index2 on vds (vds_id);
end; $procedure$
language plpgsql;
create or replace function MtDropvdsIndexes()
returns void
as $procedure$
begin
drop index if exists vds_index2 cascade;
end; $procedure$
language plpgsql;
select RefreshMaterializedView('vds');
Schedule
The simplest way to schedule all Materialized Views updates is via a cron job that will perform the following command
psql -U <user> -c "select RefreshAllMaterializedViews(false);" <db>
You can also call
psql -U <user> -c "select RefreshAllMaterializedViews(true);" <db>
In this case all Materialized Views will be refreshed but if for example the crom run every minute and the minimun update rate for a view is 5 minutes, the minimum update rate is honored(so, in this example this view will be updated once in each 5 cron invocation of teh refresh SP.