PostgreSQL教程FG132-PG大对象适用场景:文件/大二进制数据存储
本文档风哥主要介绍PostgreSQL大对象的适用场景,包括文件存储、二进制数据存储等实际应用场景,风哥教程参考PostgreSQL官方文档,适合开发人员和DBA在学习和测试中使用。
更多视频教程www.fgedu.net.cn
Part01-基础概念与理论知识
1.1 大对象适用场景概述
PostgreSQL大对象适用于需要存储大型二进制数据的场景,最大支持4TB的数据存储。以下是主要的适用场景:
- 文档管理:PDF、Word、Excel等办公文档
- 媒体文件:图片、音频、视频等多媒体文件
- 备份数据:数据库备份、日志文件等
- 二进制数据:序列化对象、加密数据等
- 科学数据:实验数据、观测数据等
1.2 文件存储场景
文件存储是大对象最常见的应用场景之一:
— 1. 文档管理系统
— 适用:企业文档、合同、报告等
— 优势:统一管理、版本控制、权限控制
— 2. 内容管理系统(CMS)
— 适用:网站内容、文章附件、用户上传文件
— 优势:与元数据关联、全文检索支持
— 3. 电子邮件系统
— 适用:邮件附件存储
— 优势:事务支持、备份恢复方便
— 4. 电子档案系统
— 适用:扫描文档、电子档案
— 优势:长期保存、安全可靠
1.3 二进制数据存储场景
二进制数据存储场景:
— 1. 序列化对象存储
— 适用:Java对象、Python对象等序列化数据
— 优势:保持对象完整性、支持复杂数据结构
— 2. 加密数据存储
— 适用:敏感信息、加密文件
— 优势:与数据库安全机制集成
— 3. 压缩数据存储
— 适用:压缩日志、压缩备份
— 优势:节省存储空间
— 4. 科学数据存储
— 适用:实验数据、观测数据、传感器数据
— 优势:支持大容量、支持随机访问
Part02-生产环境规划与建议
2.1 文档管理系统
文档管理系统是大对象的典型应用场景:
— 1. 文档表设计
CREATE TABLE fgedu_documents (
id SERIAL PRIMARY KEY,
document_name VARCHAR(255) NOT NULL,
document_type VARCHAR(50) NOT NULL,
file_size BIGINT NOT NULL,
file_data OID NOT NULL,
category_id INTEGER REFERENCES fgedu_categories(id),
department_id INTEGER REFERENCES fgedu_departments(id),
created_by INTEGER REFERENCES fgedu_fgedus(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
version INTEGER DEFAULT 1,
status VARCHAR(20) DEFAULT ‘active’
);
— 2. 文档版本表
CREATE TABLE fgedu_document_versions (
id SERIAL PRIMARY KEY,
document_id INTEGER REFERENCES fgedu_documents(id),
version INTEGER NOT NULL,
file_data OID NOT NULL,
change_description TEXT,
created_by INTEGER REFERENCES fgedu_fgedus(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 3. 文档权限表
CREATE TABLE fgedu_document_permissions (
id SERIAL PRIMARY KEY,
document_id INTEGER REFERENCES fgedu_documents(id),
fgedu_id INTEGER REFERENCES fgedu_fgedus(id),
permission_type VARCHAR(20) NOT NULL, — read, write, delete
granted_by INTEGER REFERENCES fgedu_fgedus(id),
granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 4. 触发器:自动管理大对象
CREATE OR REPLACE FUNCTION manage_document_large_object()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = ‘DELETE’ THEN
— 删除旧版本的大对象
IF OLD.file_data IS NOT NULL THEN
PERFORM lo_unlink(OLD.file_data);
END IF;
RETURN OLD;
ELSIF TG_OP = ‘UPDATE’ THEN
— 如果文件数据变更,删除旧的大对象
IF OLD.file_data IS NOT NULL AND OLD.file_data != NEW.file_data THEN
PERFORM lo_unlink(OLD.file_data);
END IF;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_manage_document_large_object
BEFORE DELETE OR UPDATE ON fgedu_documents
FOR EACH ROW
EXECUTE FUNCTION manage_document_large_object();
2.2 媒体文件存储
媒体文件存储场景:
— 1. 媒体文件表
CREATE TABLE fgedu_media_files (
id SERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
original_name VARCHAR(255) NOT NULL,
file_type VARCHAR(50) NOT NULL, — image, video, audio
mime_type VARCHAR(100) NOT NULL,
file_size BIGINT NOT NULL,
file_data OID NOT NULL,
width INTEGER, — 图片/视频宽度
height INTEGER, — 图片/视频高度
duration INTEGER, — 音视频时长(秒)
thumbnail_oid OID, — 缩略图
uploaded_by INTEGER REFERENCES fgedu_fgedus(id),
uploaded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
download_count INTEGER DEFAULT 0,
status VARCHAR(20) DEFAULT ‘active’
);
— 2. 媒体分类表
CREATE TABLE fgedu_media_categories (
id SERIAL PRIMARY KEY,
category_name VARCHAR(100) NOT NULL,
parent_id INTEGER REFERENCES fgedu_media_categories(id),
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 3. 媒体标签表
CREATE TABLE fgedu_media_tags (
id SERIAL PRIMARY KEY,
tag_name VARCHAR(50) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 4. 媒体文件标签关联表
CREATE TABLE fgedu_media_file_tags (
media_id INTEGER REFERENCES fgedu_media_files(id),
tag_id INTEGER REFERENCES fgedu_media_tags(id),
PRIMARY KEY (media_id, tag_id)
);
— 5. 查询媒体文件
SELECT
m.id,
m.file_name,
m.file_type,
m.mime_type,
pg_size_pretty(m.file_size) as file_size,
m.width,
m.height,
m.duration,
m.uploaded_at,
u.fgeduname as uploaded_by
FROM fgedu_media_files m
JOIN fgedu_fgedus u ON m.uploaded_by = u.id
WHERE m.file_type = ‘image’
AND m.status = ‘active’
ORDER BY m.uploaded_at DESC
LIMIT 20;
2.3 备份数据存储
备份数据存储场景:
from oracle:www.itpux.com
— 1. 备份记录表
CREATE TABLE fgedu_backup_records (
id SERIAL PRIMARY KEY,
backup_name VARCHAR(255) NOT NULL,
backup_type VARCHAR(50) NOT NULL, — full, incremental, differential
fgedudb_name VARCHAR(100) NOT NULL,
file_size BIGINT NOT NULL,
file_data OID NOT NULL,
compression_ratio NUMERIC(5,2),
backup_start_time TIMESTAMP NOT NULL,
backup_end_time TIMESTAMP,
duration_seconds INTEGER,
status VARCHAR(20) DEFAULT ‘running’, — running, completed, failed
error_message TEXT,
created_by INTEGER REFERENCES fgedu_fgedus(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 2. 备份策略表
CREATE TABLE fgedu_backup_policies (
id SERIAL PRIMARY KEY,
policy_name VARCHAR(100) NOT NULL,
fgedudb_name VARCHAR(100) NOT NULL,
backup_type VARCHAR(50) NOT NULL,
schedule_cron VARCHAR(100) NOT NULL, — cron表达式
retention_days INTEGER NOT NULL,
compression_enabled BOOLEAN DEFAULT true,
encryption_enabled BOOLEAN DEFAULT false,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 3. 查询备份记录
SELECT
id,
backup_name,
backup_type,
fgedudb_name,
pg_size_pretty(file_size) as file_size,
compression_ratio,
backup_start_time,
backup_end_time,
duration_seconds,
status
FROM fgedu_backup_records
WHERE fgedudb_name = ‘fgedudb’
AND status = ‘completed’
ORDER BY backup_start_time DESC
LIMIT 10;
Part03-生产环境项目实施方案
3.1 实施策略
3.1.1 存储策略选择
— 1. 小文件存储策略(< 1MB)
-- 使用BYTEA类型
CREATE TABLE fgedu_small_files (
id SERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
file_data BYTEA NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 2. 中等文件存储策略(1MB - 100MB)
-- 使用OID大对象
CREATE TABLE fgedu_medium_files (
id SERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
file_size BIGINT NOT NULL,
file_data OID NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 3. 大文件存储策略(> 100MB)
— 使用OID大对象 + 分块存储
CREATE TABLE fgedu_large_files (
id SERIAL PRIMARY KEY,
file_name VARCHAR(255) NOT NULL,
file_size BIGINT NOT NULL,
chunk_size INTEGER DEFAULT 10485760, — 10MB
chunk_count INTEGER NOT NULL,
file_data OID NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 4. 混合存储策略
— 根据文件大小自动选择存储方式
CREATE OR REPLACE FUNCTION store_file(
p_file_name VARCHAR,
p_file_data BYTEA
) RETURNS INTEGER AS $$
DECLARE
v_file_size INTEGER;
v_file_id INTEGER;
v_oid OID;
BEGIN
v_file_size := LENGTH(p_file_data);
IF v_file_size < 1048576 THEN -- 小文件:使用BYTEA INSERT INTO fgedu_small_files (file_name, file_data) VALUES (p_file_name, p_file_data) RETURNING id INTO v_file_id; ELSIF v_file_size < 104857600 THEN -- 中等文件:使用OID v_oid := lo_create(0); -- 写入大对象数据... INSERT INTO fgedu_medium_files (file_name, file_size, file_data) VALUES (p_file_name, v_file_size, v_oid) RETURNING id INTO v_file_id; ELSE -- 大文件:使用OID + 分块 v_oid := lo_create(0); -- 分块写入大对象数据... INSERT INTO fgedu_large_files (file_name, file_size, chunk_count, file_data) VALUES (p_file_name, v_file_size, CEIL(v_file_size::NUMERIC / 10485760), v_oid) RETURNING id INTO v_file_id; END IF; RETURN v_file_id; END; $$ LANGUAGE plpgsql;
3.2 性能考虑
3.2.1 读取性能优化
— 1. 使用流式读取
— 避免一次性加载大对象到内存
— 2. 缓存热点数据
— 使用Redis或Memcached缓存频繁访问的文件
— 3. 数据库参数优化
— 增加shared_buffers
ALTER SYSTEM SET shared_buffers = ‘2GB’;
— 增加work_mem
ALTER SYSTEM SET work_mem = ‘256MB’;
— 4. 使用CDN加速
— 对于公开访问的媒体文件,使用CDN分发
— 5. 异步加载
— 对于大文件,使用异步加载方式
3.3 安全考虑
3.3.1 访问控制
— 1. 权限控制
— 限制大对象访问权限
REVOKE ALL ON pg_largeobject FROM PUBLIC;
REVOKE ALL ON pg_largeobject_metadata FROM PUBLIC;
— 只允许特定用户访问
GRANT SELECT ON pg_largeobject TO fgedu_fgfgapp_fgedu;
GRANT SELECT ON pg_largeobject_metadata TO fgedu_fgfgapp_fgedu;
— 2. 数据加密
— 存储加密后的数据
CREATE EXTENSION IF NOT EXISTS pgcrypto;
— 加密存储
INSERT INTO fgedu_documents (document_name, file_data)
VALUES (‘sensitive.pdf’,
pgp_sym_encrypt_bytea(lo_get(file_oid), ‘secret_key’));
— 3. 审计日志
— 记录文件访问日志
CREATE TABLE fgedu_file_access_logs (
id SERIAL PRIMARY KEY,
file_id INTEGER NOT NULL,
fgedu_id INTEGER REFERENCES fgedu_fgedus(id),
access_type VARCHAR(20) NOT NULL, — read, write, delete
access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ip_address INET,
fgedu_agent TEXT
);
— 4. 数据脱敏
— 对于敏感文件,存储脱敏后的版本
Part04-生产案例与实战讲解
4.1 实战案例
4.1.1 电商平台商品图片存储
— 1. 商品图片表
CREATE TABLE fgedu_product_images (
id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES fgedu_products(id),
image_name VARCHAR(255) NOT NULL,
image_type VARCHAR(20) NOT NULL, — main, detail, thumbnail
file_size BIGINT NOT NULL,
image_data OID NOT NULL,
width INTEGER,
height INTEGER,
sort_order INTEGER DEFAULT 0,
is_primary BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 2. 图片访问统计
CREATE TABLE fgedu_image_access_stats (
image_id INTEGER REFERENCES fgedu_product_images(id),
access_date DATE NOT NULL,
access_count INTEGER DEFAULT 0,
PRIMARY KEY (image_id, access_date)
);
— 3. 查询商品主图
SELECT
pi.id,
pi.image_name,
pi.width,
pi.height,
pg_size_pretty(pi.file_size) as file_size
FROM fgedu_product_images pi
WHERE pi.product_id = 123
AND pi.is_primary = true;
— 4. 批量上传图片
CREATE OR REPLACE FUNCTION upload_product_images(
p_product_id INTEGER,
p_images JSONB
) RETURNS INTEGER AS $$
DECLARE
v_image JSONB;
v_count INTEGER := 0;
v_oid OID;
BEGIN
FOR v_image IN SELECT * FROM jsonb_array_elements(p_images)
LOOP
— 创建大对象
v_oid := lo_create(0);
— 写入图片数据…
INSERT INTO fgedu_product_images (
product_id, image_name, image_type, file_size,
image_data, width, height, sort_order
) VALUES (
p_product_id,
v_image->>’name’,
v_image->>’type’,
(v_image->>’size’)::BIGINT,
v_oid,
(v_image->>’width’)::INTEGER,
(v_image->>’height’)::INTEGER,
(v_image->>’sort’)::INTEGER
);
v_count := v_count + 1;
END LOOP;
RETURN v_count;
END;
$$ LANGUAGE plpgsql;
4.1.2 医疗影像存储系统
— 1. 影像记录表
CREATE TABLE fgedu_medical_images (
id SERIAL PRIMARY KEY,
patient_id INTEGER REFERENCES fgedu_patients(id),
study_id VARCHAR(50) NOT NULL,
series_id VARCHAR(50) NOT NULL,
image_id VARCHAR(50) NOT NULL,
modality VARCHAR(20) NOT NULL, — CT, MRI, X-Ray, etc.
body_part VARCHAR(100),
file_size BIGINT NOT NULL,
image_data OID NOT NULL,
width INTEGER,
height INTEGER,
bits_allocated INTEGER,
pixel_representation INTEGER,
acquisition_date TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 2. DICOM标签表
CREATE TABLE fgedu_dicom_tags (
id SERIAL PRIMARY KEY,
image_id INTEGER REFERENCES fgedu_medical_images(id),
tag_group VARCHAR(4) NOT NULL,
tag_element VARCHAR(4) NOT NULL,
tag_name VARCHAR(100),
tag_value TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 3. 影像访问控制
CREATE TABLE fgedu_image_access_control (
image_id INTEGER REFERENCES fgedu_medical_images(id),
fgedu_id INTEGER REFERENCES fgedu_fgedus(id),
access_level VARCHAR(20) NOT NULL, — view, download, admin
granted_by INTEGER REFERENCES fgedu_fgedus(id),
granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP,
PRIMARY KEY (image_id, fgedu_id)
);
— 4. 查询患者影像
SELECT
mi.id,
mi.study_id,
mi.series_id,
mi.modality,
mi.body_part,
mi.acquisition_date,
pg_size_pretty(mi.file_size) as file_size
FROM fgedu_medical_images mi
WHERE mi.patient_id = 12345
ORDER BY mi.acquisition_date DESC;
4.2 最佳实践
# 1. 设计原则
– 元数据与数据分离:将文件元数据存储在普通表中,文件内容存储为大对象
– 统一命名规范:使用统一的文件命名规范,便于管理
– 版本控制:对于重要文件,实施版本控制
– 权限管理:建立完善的权限管理机制
# 2. 性能优化
– 使用流式读写:避免一次性加载大对象
– 实施缓存策略:缓存热点数据
– 优化数据库参数:调整shared_buffers、work_mem等参数
– 使用CDN:对于公开访问的文件,使用CDN加速
# 3. 安全策略
– 权限控制:限制大对象访问权限
– 数据加密:对敏感数据进行加密存储
– 审计日志:记录文件访问日志
– 备份恢复:建立完善的数据备份恢复机制
# 4. 运维管理
– 监控存储使用:定期监控大对象存储使用情况
– 清理孤立对象:定期清理孤立的大对象
– 归档历史数据:归档不再需要的历史数据
– 自动化运维:使用脚本自动化日常运维任务
4.3 经验教训
大对象存储的经验教训:
- 避免存储超大文件:虽然大对象支持4TB,但建议将超大文件分割存储
- 注意内存使用:避免一次性加载大对象到内存,使用流式读写
- 定期清理:定期清理孤立的大对象,释放存储空间
- 备份策略:制定完善的大对象备份策略
- 权限管理:严格控制大对象的访问权限
- 监控告警:建立存储使用监控和告警机制
Part05-风哥经验总结与分享
5.1 选型指南
大对象存储选型指南:
# 1. 文件大小
– < 1MB:使用BYTEA类型
- 1MB - 100MB:使用OID大对象
- > 100MB:使用OID大对象 + 分块存储
# 2. 访问模式
– 频繁读取:使用缓存 + CDN
– 频繁写入:使用批量写入 + 异步处理
– 随机访问:使用大对象(支持随机访问)
– 顺序访问:使用BYTEA或文件系统
# 3. 数据类型
– 结构化数据:使用普通表
– 半结构化数据:使用JSONB
– 二进制数据:使用BYTEA或OID
– 大文件:使用OID大对象
# 4. 安全要求
– 高安全要求:使用加密存储 + 严格权限控制
– 一般安全要求:使用标准权限控制
– 公开数据:使用CDN分发
# 5. 性能要求
– 高性能要求:使用缓存 + 优化数据库参数
– 一般性能要求:使用标准配置
– 低性能要求:使用文件系统存储
5.2 实施技巧
大对象实施技巧:
- 渐进式实施:先在小范围试点,再逐步推广
- 充分测试:在生产环境使用前充分测试
- 监控优化:持续监控性能,及时优化
- 文档记录:记录实施过程和配置
- 培训团队:培训运维团队掌握大对象管理
- 制定规范:制定大对象使用规范
5.3 发展趋势
大对象存储的发展趋势:
- 云原生:与云存储服务集成
- 对象存储:与S3等对象存储集成
- 分布式存储:支持分布式存储架构
- 智能管理:AI驱动的存储优化
- 边缘计算:支持边缘存储和计算
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
