1. 首页 > PostgreSQL教程 > 正文

PostgreSQL教程FG279-PG大对象存储实战:海量文件存储方案

本文档风哥主要介绍PostgreSQL大对象(Large Object)存储的实战方案,包括大对象的基本操作、管理、性能优化等内容。风哥教程参考PostgreSQL官方文档Large Objects内容,适合需要存储和管理海量文件的企业级应用场景。更多视频教程www.fgedu.net.cn

Part01-基础概念与理论知识

1.1 PostgreSQL大对象的概念

PostgreSQL大对象(Large Object,简称LO)是一种用于存储大型二进制数据的机制,支持存储超过TOAST(The Oversized-Attribute Storage Technique)限制的大型数据。大对象的主要特点:

  • 支持存储最大4TB的二进制数据
  • 提供流式读写接口,适合处理大型文件
  • 支持随机访问,可以在文件的任意位置读写数据
  • 提供权限控制,确保数据安全
  • 支持事务,确保数据一致性
大对象与TOAST的区别:

TOAST适合存储中等大小的二进制数据(最大约1GB),而大对象适合存储更大的二进制数据(最大4TB)。TOAST是自动管理的,而大对象需要显式创建和管理。

1.2 大对象存储原理

PostgreSQL大对象的存储原理:

# 大对象存储结构
– 大对象元数据:存储在pg_largeobject_metadata表中
– 大对象数据:存储在pg_largeobject表中,按块存储
– 块大小:默认2KB
– 存储方式:二进制数据被分割成多个块,每个块存储为pg_largeobject表中的一行

# 大对象标识符
– OID(Object Identifier):大对象的唯一标识符
– 通过OID引用和访问大对象

# 大对象操作API
– SQL接口:通过lo_*函数操作大对象
– 客户端接口:通过libpq的大对象API操作

# 大对象事务支持
– 大对象操作在事务中执行
– 支持回滚和提交
– 并发控制:使用共享锁和排他锁

1.3 大对象适用场景

PostgreSQL大对象适用于以下场景:

# 适用场景
1. 存储大型文件:如文档、图像、视频、音频等
2. 需要随机访问的大型数据:如数据库备份、科学数据等
3. 需要事务支持的二进制数据操作
4. 需要细粒度权限控制的二进制数据
5. 与数据库其他数据关联的大型文件

# 不适用场景
1. 频繁访问的小型文件(使用TOAST更合适)
2. 需要文件系统级操作的场景
3. 需要直接文件系统访问的场景
4. 极高并发的文件访问场景

风哥提示:选择大对象存储还是文件系统存储,需要根据具体的业务需求和性能要求来决定。对于需要与数据库事务关联、需要权限控制或需要随机访问的大型数据,大对象是一个不错的选择。

Part02-生产环境规划与建议

2.1 大对象存储规划

在实施大对象存储前,需要进行详细的规划:

# 存储规划要点
1. 数据量评估:估算需要存储的大对象数量和大小
2. 访问模式:分析大对象的读写频率和方式
3. 存储策略:确定使用大对象还是文件系统存储
4. 备份策略:制定大对象的备份和恢复方案
5. 性能要求:评估大对象操作的性能需求

# 数据量估算
– 单个大对象大小:从几MB到GB级
– 大对象数量:从几千到几百万
– 总存储需求:根据单个大小和数量估算

# 访问模式分析
– 读多写少:如文档管理系统
– 写多读少:如数据采集系统
– 随机访问:如科学数据处理
– 顺序访问:如视频流服务

2.2 存储设计与配置

大对象存储设计与配置建议:

# 表空间设计
– 为大对象创建专用表空间
– 使用高性能存储设备
– 考虑使用SSD提高I/O性能

# 数据库配置
– 调整shared_buffers参数
– 配置适当的work_mem
– 调整maintenance_work_mem
– 优化checkpoint参数

# 存储优化
– 合理设置大对象块大小
– 考虑使用压缩(如果适用)
– 定期清理未使用的大对象

# 高可用考虑
– 确保大对象数据包含在备份中
– 配置流复制确保大对象数据同步
– 测试大对象在故障转移后的可用性

2.3 性能考虑因素

大对象存储的性能考虑因素:

风哥教程针对风哥教程针对风哥教程针对生产环境建议:

  • 存储设备:使用高性能存储,如SSD或NVMe
  • 网络带宽:确保足够的网络带宽,特别是在客户端与数据库分离的场景
  • 内存配置:增加shared_buffers和work_mem以提高大对象操作性能
  • 连接池:使用连接池减少连接开销
  • 批量操作:对于大量大对象操作,使用批量处理减少开销

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

3.1 大对象基本操作

3.1.1 创建和管理大对象

# 创建大对象
$ psql -U pgsql -d fgedudb

— 创建大对象并返回OID
SELECT lo_create(0);
lo_create
———–
123456
(1 row)

— 从文件创建大对象
SELECT lo_import(‘/path/to/file.jpg’);
lo_import
———–
123457
(1 row)

— 导出大对象到文件
SELECT lo_export(123457, ‘/path/to/output.jpg’);
lo_export
———–
1
(1 row)

— 删除大对象
SELECT lo_unlink(123456);
lo_unlink
———–
1
(1 row)

— 检查大对象是否存在
SELECT lo_exists(123457);
lo_exists
———–
t
(1 row)

3.1.2 大对象读写操作

# 使用SQL函数操作大对象
— 打开大对象(模式:r=读, w=写, x=读写)
SELECT lo_open(123457, 2); — 2=写模式
lo_open
———-
1
(1 row)

— 写入数据
SELECT lowrite(1, ‘Hello, Large Object!’);
lowrite
———-
18
(1 row)

— 关闭大对象
SELECT lo_close(1);
lo_close
———–
1
(1 row)

— 读取大对象
SELECT loread(lo_open(123457, 1), 100);
loread
——————-
Hello, Large Object!
(1 row)

— 定位到指定位置
SELECT lo_lseek(lo_open(123457, 1), 7, 0); — 0=从开始位置
lo_lseek
———-
7
(1 row)

— 读取从指定位置开始的数据
SELECT loread(lo_open(123457, 1), 10);
loread
———-
Large Obj
(1 row)

3.2 大对象管理

3.2.1 大对象元数据管理

# 查看大对象元数据
SELECT * FROM pg_largeobject_metadata;
lomowner | lomacl | loid
———-+——–+——
10 | | 123457
(1 row)

# 查看大对象数据块
SELECT * FROM pg_largeobject WHERE loid = 123457;
loid | pageno | data
——-+——–+————————
123457 | 0 | \x48656c6c6f2c204c6172…
(1 row)

# 查看大对象大小
SELECT lo_size(123457);
lo_size
———
18
(1 row)

# 大对象权限管理
— 授予权限
SELECT lo_grant(123457, ‘fgedu’, 2); — 2=写权限
— 撤销权限
SELECT lo_revoke(123457, ‘fgedu’, 2);

3.2.2 大对象清理

# 查找未引用的大对象
SELECT DISTINCT loid
FROM pg_largeobject_metadata
WHERE loid NOT IN (
SELECT DISTINCT lo
FROM (
SELECT unnest(lo_from_bytea(0, data)) AS lo
FROM pg_largeobject
) AS l
);

# 清理未引用的大对象
— 创建清理函数
CREATE OR REPLACE FUNCTION clean_unused_large_objects()
RETURNS INTEGER AS $$
DECLARE
loid_oid OID;
cnt INTEGER := 0;
BEGIN
FOR loid_oid IN
SELECT DISTINCT loid
FROM pg_largeobject_metadata
WHERE loid NOT IN (
SELECT DISTINCT lo
FROM (
SELECT unnest(lo_from_bytea(0, data)) AS lo
FROM pg_largeobject
) AS l
)
LOOP
PERFORM lo_unlink(loid_oid);
cnt := cnt + 1;
END LOOP;
RETURN cnt;
END;
$$ LANGUAGE plpgsql;

— 执行清理
SELECT clean_unused_large_objects();
clean_unused_large_objects
—————————
5
(1 row)

3.3 应用集成方案

3.3.1 应用程序集成

# Python应用集成示例
import psycopg2
from psycopg2.extensions import adapt

# 连接数据库
conn = psycopg2.connect(
fgedudb=”fgedudb”,
fgedu=”fgedu”,
password=”password”,
fgedu.net.cn=”localfgedu.net.cn”
)
cur = conn.cursor()

# 上传文件到大对象
def upload_file(file_path):
with open(file_path, ‘rb’) as f:
data = f.read()
cur.execute(“SELECT lo_create(0)”)
loid = cur.fetchone()[0]
cur.execute(“SELECT lo_open(%s, 131072)”, (loid,)) # 131072=读写模式
fd = cur.fetchone()[0]
cur.execute(“SELECT lowrite(%s, %s)”, (fd, data))
cur.execute(“SELECT lo_close(%s)”, (fd,))
conn.commit()
return loid

# 下载大对象到文件
def download_file(loid, file_path):
cur.execute(“SELECT lo_open(%s, 262144)”, (loid,)) # 262144=只读模式
fd = cur.fetchone()[0]
cur.execute(“SELECT loread(%s, 1048576)”, (fd,)) # 读取1MB
data = cur.fetchone()[0]
cur.execute(“SELECT lo_close(%s)”, (fd,))
with open(file_path, ‘wb’) as f:
f.write(data)

# 使用示例
loid = upload_file(‘/path/to/file.jpg’)
print(f”Uploaded file with OID: {loid}”)
download_file(loid, ‘/path/to/output.jpg’)
print(“File downloaded successfully”)

# 关闭连接
cur.close()
conn.close()

3.3.2 大对象与应用数据关联

# 创建文件表关联大对象
CREATE TABLE fgedu_files (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
filetype VARCHAR(100) NOT NULL,
filesize BIGINT NOT NULL,
lo_oid OID NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
created_by VARCHAR(100) NOT NULL
);

# 插入文件记录
INSERT INTO fgedu_files (filename, filetype, filesize, lo_oid, created_by)
VALUES (‘document.pdf’, ‘fgapplication/pdf’, 1048576, 123457, ‘fgedu’);

# 查询文件信息
SELECT * FROM fgedu_files WHERE id = 1;
id | filename | filetype | filesize | lo_oid | created_at | created_by
—-+————-+——————-+———-+——–+—————————-+————
1 | document.pdf | fgapplication/pdf | 1048576 | 123457 | 2026-04-02 10:00:00.000000 | fgedu

# 检索文件
SELECT lo_export(lo_oid, ‘/tmp/’ || filename)
FROM fgedu_files
WHERE id = 1;

# 删除文件及其大对象
BEGIN;
DELETE FROM fgedu_files WHERE id = 1;
SELECT lo_unlink(123457);
COMMIT;

风哥提示:在应用中使用大对象时,建议将大对象的OID存储在应用表中,并建立外键约束或触发器,确保大对象与应用数据的一致性。学习交流加群风哥微信: itpux-com

Part04-生产案例与实战讲解

4.1 大对象存储实战

4.1.1 文档管理系统

# 场景:企业文档管理系统,需要存储大量PDF、Word等文档

# 1. 数据库设计
CREATE TABLE fgedu_documents (
id SERIAL PRIMARY KEY,
document_name VARCHAR(255) NOT NULL,
document_type VARCHAR(100) NOT NULL,
document_size BIGINT NOT NULL,
lo_oid OID NOT NULL,
author VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

# 2. 上传文档函数
CREATE OR REPLACE FUNCTION upload_document(
p_name VARCHAR(255),
p_type VARCHAR(100),
p_size BIGINT,
p_data BYTEA,
p_author VARCHAR(100)
) RETURNS INTEGER AS $$
DECLARE
v_oid OID;
v_id INTEGER;
BEGIN
— 创建大对象
v_oid := lo_from_bytea(0, p_data);

— 插入文档记录
INSERT INTO fgedu_documents (
document_name, document_type, document_size, lo_oid, author
) VALUES (
p_name, p_type, p_size, v_oid, p_author
) RETURNING id INTO v_id;

RETURN v_id;
END;
$$ LANGUAGE plpgsql;

# 3. 下载文档函数
CREATE OR REPLACE FUNCTION download_document(p_id INTEGER) RETURNS BYTEA AS $$
DECLARE
v_oid OID;
v_data BYTEA;
BEGIN
— 获取大对象OID
SELECT lo_oid INTO v_oid FROM fgedu_documents WHERE id = p_id;

— 读取大对象数据
v_data := lo_get(v_oid);

RETURN v_data;
END;
$$ LANGUAGE plpgsql;

# 4. 删除文档函数
CREATE OR REPLACE FUNCTION delete_document(p_id INTEGER) RETURNS BOOLEAN AS $$
DECLARE
v_oid OID;
BEGIN
— 获取大对象OID
SELECT lo_oid INTO v_oid FROM fgedu_documents WHERE id = p_id;

— 删除文档记录
DELETE FROM fgedu_documents WHERE id = p_id;

— 删除大对象
PERFORM lo_unlink(v_oid);

RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

# 5. 使用示例
— 上传文档
SELECT upload_document(‘Annual Report.pdf’, ‘fgapplication/pdf’, 2097152, pg_read_binary_file(‘/path/to/report.pdf’), ‘admin’);

— 下载文档
SELECT download_document(1);

— 删除文档
SELECT delete_document(1);

4.1.2 图像存储系统

# 场景:电商平台产品图片存储

# 1. 数据库设计
CREATE TABLE fgedu_products (
id SERIAL PRIMARY KEY,
product_name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE fgedu_product_images (
id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES fgedu_products(id) ON DELETE CASCADE,
image_name VARCHAR(255) NOT NULL,
image_type VARCHAR(50) NOT NULL,
image_size BIGINT NOT NULL,
lo_oid OID NOT NULL,
is_primary BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

# 2. 插入产品图片
— 插入产品
INSERT INTO fgedu_products (product_name, description, price)
VALUES (‘Smartphone’, ‘Latest model’, 999.99);

— 插入产品图片
INSERT INTO fgedu_product_images (product_id, image_name, image_type, image_size, lo_oid, is_primary)
VALUES (1, ‘front.jpg’, ‘image/jpeg’, 1048576, lo_import(‘/path/to/front.jpg’), TRUE);

INSERT INTO fgedu_product_images (product_id, image_name, image_type, image_size, lo_oid)
VALUES (1, ‘back.jpg’, ‘image/jpeg’, 1048576, lo_import(‘/path/to/back.jpg’));

# 3. 获取产品图片
— 获取产品的主图片
SELECT lo_get(lo_oid) AS image_data
FROM fgedu_product_images
WHERE product_id = 1 AND is_primary = TRUE;

— 获取产品的所有图片
SELECT image_name, image_type, lo_get(lo_oid) AS image_data
FROM fgedu_product_images
WHERE product_id = 1;

# 4. 清理产品图片
— 删除产品(级联删除相关图片)
DELETE FROM fgedu_products WHERE id = 1;

— 注意:需要手动删除大对象
CREATE OR REPLACE FUNCTION cleanup_product_images()
RETURNS TRIGGER AS $$
BEGIN
— 删除关联的大对象
FOR i IN SELECT lo_oid FROM fgedu_product_images WHERE product_id = OLD.id LOOP
PERFORM lo_unlink(i.lo_oid);
END LOOP;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER before_delete_product
BEFORE DELETE ON fgedu_products
FOR EACH ROW
EXECUTE FUNCTION cleanup_product_images();

4.2 大对象性能优化

4.2.1 存储优化

# 大对象存储优化

# 1. 使用专用表空间
— 创建大对象专用表空间
CREATE TABLESPACE lo_ts LOCATION ‘/postgresql/large_objects’;

— 将大对象表移动到专用表空间
ALTER TABLE pg_largeobject SET TABLESPACE lo_ts;
ALTER TABLE pg_largeobject_metadata SET TABLESPACE lo_ts;

# 2. 调整块大小
— 查看当前块大小
SHOW block_size;

— 注意:块大小在初始化数据库时设置,无法在运行时修改
— 建议在初始化数据库时根据大对象大小设置合适的块大小

# 3. 批量操作
— 批量导入大对象
CREATE OR REPLACE FUNCTION batch_import_files(file_paths TEXT[])
RETURNS TABLE(oid OID, path TEXT) AS $$
DECLARE
file_path TEXT;
BEGIN
FOREACH file_path IN ARRAY file_paths LOOP
RETURN QUERY SELECT lo_import(file_path), file_path;
END LOOP;
END;
$$ LANGUAGE plpgsql;

— 使用示例
SELECT * FROM batch_import_files(ARRAY[‘/path/to/file1.jpg’, ‘/path/to/file2.jpg’]);

# 4. 并行处理
— 使用并行查询处理大对象
ALTER TABLE fgedu_files ADD COLUMN processed BOOLEAN DEFAULT FALSE;

— 并行更新处理状态
UPDATE fgedu_files SET processed = TRUE WHERE id IN (
SELECT id FROM fgedu_files WHERE processed = FALSE ORDER BY id LIMIT 100
);

4.2.2 访问优化

# 大对象访问优化

# 1. 使用连接池
— 配置pgBouncer或其他连接池
— 减少连接建立和关闭的开销

# 2. 缓存策略
— 实现应用级缓存
— 缓存频繁访问的大对象

# 3. 异步操作
— 使用异步I/O
— 避免阻塞主线程

# 4. 分段读取
— 对于大型大对象,使用分段读取
— 减少内存使用

# 5. 索引优化
— 为关联表创建合适的索引
CREATE INDEX idx_fgedu_files_created_at ON fgedu_files(created_at);
CREATE INDEX idx_fgedu_files_lo_oid ON fgedu_files(lo_oid);

# 6. 预读取
— 对于顺序访问的场景,实现预读取
— 提高读取性能

4.3 大对象常见问题处理

4.3.1 常见错误及解决方法

# 1. 大对象操作权限错误
# 错误信息:ERROR: permission denied for large object 123456
# 解决方法:
– 确保用户有大对象操作权限
– 使用lo_grant授予权限
– 检查pg_largeobject_metadata表的权限

# 2. 大对象不存在
# 错误信息:ERROR: large object 123456 does not exist
# 解决方法:
– 检查大对象OID是否正确
– 确保大对象未被删除
– 验证引用完整性

# 3. 存储空间不足
# 错误信息:ERROR: could not extend file “base/16384/123456”: No space left on device
# 解决方法:
– 检查磁盘空间
– 清理未使用的大对象
– 扩展存储容量

# 4. 大对象操作超时
# 错误信息:ERROR: canceling statement due to statement timeout
# 解决方法:
– 增加statement_timeout参数
– 优化大对象操作
– 考虑使用分段操作

# 5. 大对象数据损坏
# 错误信息:ERROR: invalid large object descriptor
# 解决方法:
– 检查大对象的完整性
– 从备份恢复
– 实现数据验证机制

4.3.2 大对象监控

# 大对象监控

# 1. 监控大对象数量
SELECT count(*) FROM pg_largeobject_metadata;

# 2. 监控大对象大小
SELECT sum(lo_size(loid)) FROM pg_largeobject_metadata;

# 3. 监控大对象表空间使用
SELECT pg_size_pretty(pg_fgedutbs_size(‘lo_ts’));

# 4. 监控大对象操作
— 启用审计日志
— 监控lo_*函数的使用

# 5. 监控未引用的大对象
SELECT count(*) FROM pg_largeobject_metadata
WHERE loid NOT IN (
SELECT DISTINCT lo_oid FROM fgedu_files
);

# 6. 监控大对象访问性能
— 使用pg_stat_statements监控大对象相关SQL
SELECT query, calls, total_exec_time
FROM pg_stat_statements
WHERE query LIKE ‘%lo_%’
ORDER BY total_exec_time DESC;

风哥教程针对风哥教程针对风哥教程针对生产环境建议:定期监控大对象的使用情况,包括数量、大小、访问频率等,及时清理未使用的大对象,确保存储资源的有效利用。同时,建立大对象操作的性能基准,以便及时发现性能问题。更多学习教程公众号风哥教程itpux_com

Part05-风哥经验总结与分享

5.1 大对象存储最佳实践

PostgreSQL大对象存储最佳实践:

  • 合理规划:根据数据量和访问模式选择合适的存储方案
  • 专用表空间:为大对象创建专用表空间,提高性能和管理效率
  • 引用完整性:建立大对象与应用数据的关联,确保数据一致性
  • 定期清理:清理未使用的大对象,释放存储空间
  • 备份策略:确保大对象数据包含在备份中
  • 性能优化:根据实际使用情况优化存储和访问策略
  • 监控机制:建立大对象使用情况的监控机制
  • 权限管理:合理设置大对象的访问权限,确保数据安全

5.2 大对象实施检查清单

# PostgreSQL大对象实施检查清单

## 规划阶段
– [ ] 评估数据量和访问模式
– [ ] 选择存储方案(大对象 vs 文件系统)
– [ ] 设计表结构和关联关系
– [ ] 规划存储容量和增长策略

## 实施阶段
– [ ] 创建专用表空间(可选)
– [ ] 配置数据库参数
– [ ] 实现大对象操作函数
– [ ] 建立应用集成方案
– [ ] 测试大对象操作性能

## 管理阶段
– [ ] 定期清理未使用的大对象
– [ ] 监控大对象使用情况
– [ ] 备份大对象数据
– [ ] 优化大对象存储和访问
– [ ] 建立大对象操作的审计机制

## 安全阶段
– [ ] 设置大对象访问权限
– [ ] 加密敏感大对象数据
– [ ] 实现数据验证机制
– [ ] 建立访问控制策略

5.3 大对象工具推荐

PostgreSQL大对象相关工具推荐:

  • pg_lo_import/pg_lo_export:命令行工具,用于导入/导出大对象
  • psycopg2:Python库,支持大对象操作
  • libpq:C语言库,提供大对象API
  • JDBC:Java库,支持大对象操作
  • pgAdmin:图形化工具,支持大对象管理
  • Barman:备份工具,支持大对象备份
  • pgBackRest:备份工具,支持大对象备份和恢复
风哥提示:大对象存储是PostgreSQL的一个强大特性,但需要根据具体的业务需求和性能要求来合理使用。在实施大对象存储方案时,建议先进行充分的测试和评估,确保方案的可行性和性能满足要求。from PostgreSQL视频:www.itpux.com

持续改进:大对象存储方案需要根据业务需求的变化和技术的发展不断调整和优化。建议定期回顾大对象的使用情况,评估存储策略的有效性,及时调整配置和优化方案,以确保系统的性能和可靠性。

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

联系我们

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

微信号:itpux-com

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