1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG088-PG 视图与物化视图:创建、使用与性能优化

本文档详细介绍PostgreSQL视图与物化视图的创建、使用与性能优化,风哥教程参考PostgreSQL官方文档内容,适合数据库管理员和开发人员在生产环境中使用视图和物化视图进行数据抽象和性能优化。

Part01-基础概念与理论知识

1.1 PostgreSQL视图概述

视图是一个虚拟表,其内容由查询定义。视图本身不存储数据,而是在查询时动态计算结果。视图可以简化复杂查询,提供数据抽象,增强安全性,并支持逻辑数据独立性。更多视频教程www.fgedu.net.cn

视图的作用:

  • 简化查询:将复杂查询封装为简单的视图
  • 数据抽象:隐藏底层表结构,提供统一的访问接口
  • 增强安全性:限制用户对数据的访问范围
  • 逻辑数据独立性:即使底层表结构变化,视图接口保持不变

1.2 物化视图概述

物化视图是一个存储查询结果的物理表,与普通视图不同,物化视图会实际存储数据,而不是在查询时动态计算。物化视图需要定期刷新以保持数据的一致性。

物化视图的作用:

  • 提高查询性能:预计算并存储复杂查询的结果
  • 减少重复计算:避免每次查询都执行复杂计算
  • 支持复杂分析:存储聚合结果,便于快速分析
  • 数据快照:提供特定时间点的数据快照

1.3 视图与物化视图的区别

视图与物化视图的主要区别:

特性 视图 物化视图
数据存储 不存储数据,查询时动态计算 存储数据,需要定期刷新
查询性能 每次查询都执行底层SQL 查询速度快,直接读取存储的数据
数据一致性 总是与底层表保持一致 需要手动刷新,可能存在数据延迟
存储开销 无存储开销 有存储开销,需要存储结果集
刷新机制 无需刷新 需要手动或自动刷新
风哥提示:选择视图还是物化视图,取决于具体的使用场景和性能需求。学习交流加群风哥微信: itpux-com

Part02-生产环境规划与建议

2.1 视图使用最佳实践

— 视图使用最佳实践

— 1. 视图设计原则
— – 保持视图简洁,避免过于复杂的逻辑
— – 只包含必要的列,减少数据传输
— – 避免在视图中使用ORDER BY,除非必要
— – 考虑使用WITH CHECK OPTION确保数据一致性

— 2. 视图命名规范
— – 使用有意义的名称,如v_前缀表示视图
— – 包含视图的用途信息
— – 保持命名一致,便于管理

— 3. 视图权限管理
— – 基于视图授予用户权限,而不是直接授予表权限
— – 限制用户对敏感数据的访问
— – 定期审查视图权限

— 4. 视图性能考虑
— – 避免在视图中使用复杂的JOIN和聚合
— – 考虑使用物化视图替代频繁访问的复杂视图
— – 定期分析视图的使用情况

2.2 物化视图使用最佳实践

— 物化视图使用最佳实践

— 1. 物化视图设计原则
— – 用于存储复杂查询的结果,如聚合、连接等
— – 用于频繁访问但不经常变化的数据
— – 考虑数据刷新频率,平衡数据一致性和性能

— 2. 物化视图命名规范
— – 使用有意义的名称,如mv_前缀表示物化视图
— – 包含物化视图的用途信息
— – 保持命名一致,便于管理

— 3. 物化视图刷新策略
— – 根据数据变化频率选择刷新方式
— – 考虑使用增量刷新减少刷新时间
— – 安排在低峰期进行刷新

— 4. 物化视图性能优化
— – 为物化视图创建适当的索引
— – 考虑使用表空间存储物化视图
— – 定期维护物化视图,如VACUUM

2.3 性能考虑

视图与物化视图的性能考虑:

  • 视图性能:
    • 每次查询都执行底层SQL,适合数据频繁变化的场景
    • 复杂视图可能导致性能问题,特别是包含多个JOIN和聚合的视图
    • 可以使用视图索引(PostgreSQL 9.3+)提高性能
  • 物化视图性能:
    • 查询速度快,适合频繁访问但数据变化不频繁的场景
    • 刷新操作可能影响性能,特别是大型物化视图
    • 可以通过增量刷新减少刷新时间
    • 为物化视图创建索引可以进一步提高查询性能
风哥教程针对生产环境建议:对于频繁访问的复杂查询,考虑使用物化视图提高性能。from PostgreSQL视频:www.itpux.com

Part03-生产环境项目实施方案

3.1 视图创建与使用

— 视图创建与使用

— 1. 创建视图
$ psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb

— 创建基本视图
fgedudb=# CREATE VIEW v_fgedu_users AS
SELECT id, name, email, created_at
FROM fgedu_users
WHERE active = true;

— 创建带WITH CHECK OPTION的视图
fgedudb=# CREATE VIEW v_fgedu_active_users AS
SELECT id, name, email, active, created_at
FROM fgedu_users
WHERE active = true
WITH CHECK OPTION;

— 创建复杂视图(包含JOIN)
fgedudb=# CREATE VIEW v_fgedu_user_orders AS
SELECT
u.id AS user_id,
u.name AS user_name,
u.email AS user_email,
o.id AS order_id,
o.order_date,
o.total_amount,
o.status
FROM fgedu_users u
JOIN fgedu_orders o ON u.id = o.user_id;

— 2. 使用视图
— 查询视图
fgedudb=# SELECT * FROM v_fgedu_users;

— 过滤视图数据
fgedudb=# SELECT * FROM v_fgedu_users WHERE created_at > ‘2026-01-01’;

— 从视图插入数据(如果视图可更新)
fgedudb=# INSERT INTO v_fgedu_active_users (name, email, active) VALUES (‘John Doe’, ‘john.doe@fgedu.net.cn’, true);

— 3. 修改视图
— 修改视图定义
fgedudb=# CREATE OR REPLACE VIEW v_fgedu_users AS
SELECT id, name, email, phone, created_at
FROM fgedu_users
WHERE active = true;

— 4. 删除视图
fgedudb=# DROP VIEW IF EXISTS v_fgedu_users;

— 5. 查看视图信息
— 查看所有视图
fgedudb=# SELECT * FROM pg_views WHERE schemaname = ‘public’;

— 查看视图定义
fgedudb=# SELECT definition FROM pg_views WHERE viewname = ‘v_fgedu_users’;

3.2 物化视图创建与使用

— 物化视图创建与使用

— 1. 创建物化视图
$ psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb

— 创建基本物化视图
fgedudb=# CREATE MATERIALIZED VIEW mv_fgedu_daily_sales AS
SELECT
DATE(order_date) AS sale_date,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales
FROM fgedu_orders
GROUP BY DATE(order_date)
ORDER BY sale_date;

— 创建带索引的物化视图
fgedudb=# CREATE MATERIALIZED VIEW mv_fgedu_product_sales AS
SELECT
p.id AS product_id,
p.name AS product_name,
COUNT(*) AS order_count,
SUM(oi.quantity) AS total_quantity,
SUM(oi.quantity * oi.unit_price) AS total_sales
FROM fgedu_products p
JOIN fgedu_order_items oi ON p.id = oi.product_id
GROUP BY p.id, p.name
ORDER BY total_sales DESC;

— 为物化视图创建索引
fgedudb=# CREATE INDEX idx_mv_fgedu_product_sales_product_id ON mv_fgedu_product_sales(product_id);
fgedudb=# CREATE INDEX idx_mv_fgedu_product_sales_total_sales ON mv_fgedu_product_sales(total_sales);

— 2. 刷新物化视图
— 完全刷新(阻塞查询)
fgedudb=# REFRESH MATERIALIZED VIEW mv_fgedu_daily_sales;

— 并发刷新(允许查询)
fgedudb=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_fgedu_daily_sales;

— 3. 使用物化视图
— 查询物化视图
fgedudb=# SELECT * FROM mv_fgedu_daily_sales;

— 过滤物化视图数据
fgedudb=# SELECT * FROM mv_fgedu_daily_sales WHERE sale_date > ‘2026-04-01’;

— 4. 修改物化视图
— 修改物化视图定义
fgedudb=# CREATE OR REPLACE MATERIALIZED VIEW mv_fgedu_daily_sales AS
SELECT
DATE(order_date) AS sale_date,
COUNT(*) AS order_count,
SUM(total_amount) AS total_sales,
AVG(total_amount) AS avg_sale
FROM fgedu_orders
GROUP BY DATE(order_date)
ORDER BY sale_date;

— 刷新物化视图以应用更改
fgedudb=# REFRESH MATERIALIZED VIEW mv_fgedu_daily_sales;

— 5. 删除物化视图
fgedudb=# DROP MATERIALIZED VIEW IF EXISTS mv_fgedu_daily_sales;

— 6. 查看物化视图信息
— 查看所有物化视图
fgedudb=# SELECT * FROM pg_matviews WHERE schemaname = ‘public’;

— 查看物化视图定义
fgedudb=# SELECT definition FROM pg_matviews WHERE matviewname = ‘mv_fgedu_daily_sales’;

3.3 视图维护

— 视图维护

— 1. 视图维护
— 检查视图有效性
fgedudb=# SELECT * FROM pg_views WHERE schemaname = ‘public’ AND definition LIKE ‘%fgedu%’;

— 验证视图定义
fgedudb=# CREATE OR REPLACE VIEW v_fgedu_users AS
SELECT id, name, email, created_at
FROM fgedu_users
WHERE active = true;

— 2. 物化视图维护
— 定期刷新物化视图
— 创建刷新脚本
$ vi /postgresql/scripts/refresh_materialized_views.sh

#!/bin/bash
# refresh_materialized_views.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn

psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb -c “REFRESH MATERIALIZED VIEW CONCURRENTLY mv_fgedu_daily_sales;”
psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb -c “REFRESH MATERIALIZED VIEW CONCURRENTLY mv_fgedu_product_sales;”

— 设置定时任务
$ crontab -e

0 * * * * /postgresql/scripts/refresh_materialized_views.sh

— 维护物化视图
— VACUUM物化视图
fgedudb=# VACUUM ANALYZE mv_fgedu_daily_sales;

— 重建物化视图索引
fgedudb=# REINDEX TABLE mv_fgedu_product_sales;

— 3. 监控视图使用
— 查看视图使用情况
fgedudb=# SELECT
schemaname,
relname AS viewname,
last_vacuum,
last_analyze,
n_tup_ins,
n_tup_upd,
n_tup_del,
n_tup_hot_upd
FROM pg_stat_user_tables
WHERE relname IN (
SELECT viewname FROM pg_views WHERE schemaname = ‘public’
);

— 查看物化视图使用情况
fgedudb=# SELECT
schemaname,
relname AS matviewname,
last_vacuum,
last_analyze,
n_tup_ins,
n_tup_upd,
n_tup_del,
n_tup_hot_upd
FROM pg_stat_user_tables
WHERE relname IN (
SELECT matviewname FROM pg_matviews WHERE schemaname = ‘public’
);

风哥提示:定期维护视图和物化视图,确保它们的性能和数据一致性。更多学习教程公众号风哥教程itpux_com

Part04-生产案例与实战讲解

4.1 视图创建与使用案例

— 视图创建与使用案例:用户权限管理

— 场景:为不同角色创建不同的视图,限制数据访问范围

— 实施步骤:

— 1. 创建用户表
$ psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb

fgedudb=# CREATE TABLE fgedu_users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
role VARCHAR(20) NOT NULL,
active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

— 2. 插入测试数据
fgedudb=# INSERT INTO fgedu_users (name, email, password, role) VALUES
(‘Admin User’, ‘admin@fgedu.net.cn’, ‘admin123’, ‘admin’),
(‘Manager User’, ‘manager@fgedu.net.cn’, ‘manager123’, ‘manager’),
(‘Regular User’, ‘user@fgedu.net.cn’, ‘user123’, ‘user’);

— 3. 创建管理员视图(查看所有用户)
fgedudb=# CREATE VIEW v_admin_users AS
SELECT id, name, email, role, active, created_at
FROM fgedu_users;

— 4. 创建经理视图(查看除管理员外的用户)
fgedudb=# CREATE VIEW v_manager_users AS
SELECT id, name, email, role, active, created_at
FROM fgedu_users
WHERE role != ‘admin’;

— 5. 创建普通用户视图(只查看自己)
fgedudb=# CREATE OR REPLACE VIEW v_user_profile AS
SELECT id, name, email, role, active, created_at
FROM fgedu_users
WHERE id = current_user::INTEGER;

— 6. 授予权限
— 创建角色
fgedudb=# CREATE ROLE admin_role;
fgedudb=# CREATE ROLE manager_role;
fgedudb=# CREATE ROLE user_role;

— 授予视图权限
fgedudb=# GRANT SELECT ON v_admin_users TO admin_role;
fgedudb=# GRANT SELECT ON v_manager_users TO manager_role;
fgedudb=# GRANT SELECT ON v_user_profile TO user_role;

— 测试视图
— 以管理员身份查询
fgedudb=# SET ROLE admin_role;
fgedudb=# SELECT * FROM v_admin_users;

— 以经理身份查询
fgedudb=# SET ROLE manager_role;
fgedudb=# SELECT * FROM v_manager_users;

— 以普通用户身份查询
fgedudb=# SET ROLE user_role;
fgedudb=# SELECT * FROM v_user_profile;

4.2 物化视图创建与使用案例

— 物化视图创建与使用案例:销售分析

— 场景:创建物化视图用于销售数据分析,提高查询性能

— 实施步骤:

— 1. 创建销售相关表
$ psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb

— 创建产品表
fgedudb=# CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
category VARCHAR(50) NOT NULL,
price DECIMAL(10,2) NOT NULL
);

— 创建订单表
fgedudb=# CREATE TABLE fgedu_orders (
id SERIAL PRIMARY KEY,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
customer_id INTEGER NOT NULL,
total_amount DECIMAL(10,2) NOT NULL
);

— 创建订单项表
fgedudb=# CREATE TABLE fgedu_order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES fgedu_orders(id),
product_id INTEGER REFERENCES fgedu_products(id),
quantity INTEGER NOT NULL,
unit_price DECIMAL(10,2) NOT NULL
);

— 2. 插入测试数据
— 插入产品数据
fgedudb=# INSERT INTO fgedu_products (name, category, price) VALUES
(‘Product A’, ‘Electronics’, 100.00),
(‘Product B’, ‘Electronics’, 200.00),
(‘Product C’, ‘Clothing’, 50.00),
(‘Product D’, ‘Clothing’, 75.00),
(‘Product E’, ‘Home’, 150.00);

— 插入订单数据
fgedudb=# INSERT INTO fgedu_orders (customer_id, total_amount) VALUES
(1, 300.00),
(2, 125.00),
(1, 250.00),
(3, 450.00),
(2, 150.00);

— 插入订单项数据
fgedudb=# INSERT INTO fgedu_order_items (order_id, product_id, quantity, unit_price) VALUES
(1, 1, 1, 100.00),
(1, 2, 1, 200.00),
(2, 3, 1, 50.00),
(2, 4, 1, 75.00),
(3, 2, 1, 200.00),
(3, 5, 1, 150.00),
(4, 1, 2, 100.00),
(4, 2, 1, 200.00),
(4, 5, 1, 150.00),
(5, 3, 2, 50.00),
(5, 4, 1, 75.00);

— 3. 创建销售分析物化视图
— 按类别分析销售
fgedudb=# CREATE MATERIALIZED VIEW mv_fgedu_sales_by_category AS
SELECT
p.category,
COUNT(DISTINCT o.id) AS order_count,
COUNT(oi.id) AS item_count,
SUM(oi.quantity) AS total_quantity,
SUM(oi.quantity * oi.unit_price) AS total_sales
FROM fgedu_products p
JOIN fgedu_order_items oi ON p.id = oi.product_id
JOIN fgedu_orders o ON oi.order_id = o.id
GROUP BY p.category
ORDER BY total_sales DESC;

— 按日期分析销售
fgedudb=# CREATE MATERIALIZED VIEW mv_fgedu_sales_by_date AS
SELECT
DATE(o.order_date) AS sale_date,
COUNT(*) AS order_count,
SUM(o.total_amount) AS total_sales,
AVG(o.total_amount) AS avg_order_value
FROM fgedu_orders o
GROUP BY DATE(o.order_date)
ORDER BY sale_date;

— 4. 为物化视图创建索引
fgedudb=# CREATE INDEX idx_mv_fgedu_sales_by_category ON mv_fgedu_sales_by_category(category);
fgedudb=# CREATE INDEX idx_mv_fgedu_sales_by_date ON mv_fgedu_sales_by_date(sale_date);

— 5. 刷新物化视图
fgedudb=# REFRESH MATERIALIZED VIEW mv_fgedu_sales_by_category;
fgedudb=# REFRESH MATERIALIZED VIEW mv_fgedu_sales_by_date;

— 6. 查询物化视图
— 查询按类别销售
fgedudb=# SELECT * FROM mv_fgedu_sales_by_category;

— 查询按日期销售
fgedudb=# SELECT * FROM mv_fgedu_sales_by_date;

— 7. 设置定期刷新
— 创建刷新脚本
$ vi /postgresql/scripts/refresh_sales_views.sh

#!/bin/bash
# refresh_sales_views.sh
# from:www.itpux.com.qq113257174.wx:itpux-com
# web: http://www.fgedu.net.cn

psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb -c “REFRESH MATERIALIZED VIEW CONCURRENTLY mv_fgedu_sales_by_category;”
psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb -c “REFRESH MATERIALIZED VIEW CONCURRENTLY mv_fgedu_sales_by_date;”

— 设置定时任务
$ crontab -e

0 * * * * /postgresql/scripts/refresh_sales_views.sh

4.3 性能优化案例

— 性能优化案例:视图与物化视图性能比较

— 场景:比较视图和物化视图在复杂查询中的性能差异

— 实施步骤:

— 1. 创建测试表
$ psql -h 192.168.1.100 -p 5432 -U fgedu -d fgedudb

— 创建大表
fgedudb=# CREATE TABLE fgedu_large_table (
id SERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
category_id INTEGER NOT NULL,
value DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

— 插入测试数据
fgedudb=# INSERT INTO fgedu_large_table (user_id, category_id, value)
SELECT
(random() * 1000)::INTEGER + 1,
(random() * 100)::INTEGER + 1,
random() * 1000
FROM generate_series(1, 1000000);

— 2. 创建视图
fgedudb=# CREATE VIEW v_fgedu_summary AS
SELECT
user_id,
category_id,
COUNT(*) AS count,
SUM(value) AS total_value,
AVG(value) AS avg_value,
MIN(value) AS min_value,
MAX(value) AS max_value
FROM fgedu_large_table
GROUP BY user_id, category_id;

— 3. 创建物化视图
fgedudb=# CREATE MATERIALIZED VIEW mv_fgedu_summary AS
SELECT
user_id,
category_id,
COUNT(*) AS count,
SUM(value) AS total_value,
AVG(value) AS avg_value,
MIN(value) AS min_value,
MAX(value) AS max_value
FROM fgedu_large_table
GROUP BY user_id, category_id;

— 为物化视图创建索引
fgedudb=# CREATE INDEX idx_mv_fgedu_summary_user_category ON mv_fgedu_summary(user_id, category_id);

— 4. 测试查询性能
— 测试视图查询性能
fgedudb=# EXPLAIN ANALYZE SELECT * FROM v_fgedu_summary WHERE user_id = 500;

— 测试物化视图查询性能
fgedudb=# EXPLAIN ANALYZE SELECT * FROM mv_fgedu_summary WHERE user_id = 500;

— 5. 测试数据更新后的性能
— 更新数据
fgedudb=# UPDATE fgedu_large_table SET value = value * 1.1 WHERE user_id = 500;

— 测试视图查询性能(数据已更新)
fgedudb=# EXPLAIN ANALYZE SELECT * FROM v_fgedu_summary WHERE user_id = 500;

— 测试物化视图查询性能(数据未更新)
fgedudb=# EXPLAIN ANALYZE SELECT * FROM mv_fgedu_summary WHERE user_id = 500;

— 刷新物化视图
fgedudb=# REFRESH MATERIALIZED VIEW mv_fgedu_summary;

— 测试物化视图查询性能(数据已更新)
fgedudb=# EXPLAIN ANALYZE SELECT * FROM mv_fgedu_summary WHERE user_id = 500;

— 6. 性能比较结果
— 视图:每次查询都执行聚合操作,性能较慢,但数据始终最新
— 物化视图:查询速度快,但需要定期刷新以保持数据一致性

Part05-风哥经验总结与分享

5.1 视图使用技巧

视图使用技巧:

  • 简化复杂查询:将复杂的JOIN和聚合操作封装为视图,提高代码可读性
  • 数据安全性:通过视图限制用户对敏感数据的访问,只暴露必要的列
  • 逻辑数据独立性:当底层表结构变化时,只需修改视图定义,而不需要修改应用代码
  • 查询重用:将常用查询封装为视图,避免重复编写相同的SQL语句
  • 性能考虑:对于频繁访问的复杂视图,考虑使用物化视图提高性能

5.2 物化视图使用技巧

物化视图使用技巧:

  • 适用于复杂查询:对于包含多个JOIN和聚合的复杂查询,使用物化视图提高性能
  • 定期刷新:根据数据变化频率设置合适的刷新策略,平衡数据一致性和性能
  • 增量刷新:对于大型物化视图,使用增量刷新减少刷新时间
  • 索引优化:为物化视图创建合适的索引,进一步提高查询性能
  • 存储考虑:物化视图会占用存储空间,需要考虑存储成本

5.3 视图管理最佳实践

— 视图管理最佳实践

— 1. 视图设计
— – 保持视图简洁,避免过于复杂的逻辑
— – 只包含必要的列,减少数据传输
— – 使用有意义的视图名称,如v_前缀
— – 考虑使用WITH CHECK OPTION确保数据一致性

— 2. 物化视图设计
— – 用于存储复杂查询的结果,如聚合、连接等
— – 用于频繁访问但不经常变化的数据
— – 使用有意义的名称,如mv_前缀
— – 为物化视图创建适当的索引

— 3. 性能优化
— – 对于频繁访问的复杂查询,使用物化视图
— – 为物化视图创建合适的索引
— – 考虑使用增量刷新减少刷新时间
— – 安排在低峰期刷新物化视图

— 4. 维护管理
— – 定期检查视图的有效性
— – 定期刷新物化视图
— – 监控视图和物化视图的使用情况
— – 定期进行VACUUM和ANALYZE操作

— 5. 安全性
— – 基于视图授予用户权限,而不是直接授予表权限
— – 限制用户对敏感数据的访问
— – 定期审查视图权限

— 6. 监控与故障排除
— – 监控视图和物化视图的性能
— – 排查视图执行计划中的性能问题
— – 解决物化视图刷新失败的问题
— – 监控物化视图的存储空间使用

风哥提示:视图和物化视图是PostgreSQL中强大的功能,可以帮助你简化查询、提高性能、增强安全性。合理使用视图和物化视图,可以显著提高数据库管理和应用开发的效率。

本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html

联系我们

在线咨询:点击这里给我发消息

微信号:itpux-com

工作日:9:30-18:30,节假日休息