PostgreSQL教程FG131-PG大对象:创建/存储/读取/删除实操
本文档风哥主要介绍PostgreSQL的大对象(Large Objects)功能,包括创建、存储、读取、删除等操作,风哥教程参考PostgreSQL官方文档Large Objects内容,适合开发人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
Part01-基础概念与理论知识
1.1 大对象概述
PostgreSQL的大对象(Large Objects,简称LO)是一种用于存储大型二进制数据的数据类型,可以存储最大4TB的数据。大对象适用于存储图片、音频、视频、文档等大型文件。更多视频教程www.fgedu.net.cn
- 支持最大4TB的数据存储
- 支持随机访问
- 支持事务
- 支持权限控制
- 支持增量读写
1.2 大对象类型
PostgreSQL提供两种大对象类型:
from oracle:www.itpux.com
- OID(Object Identifier):大对象的唯一标识符,存储在pg_largeobject系统表中
- BYTEA:二进制数据类型,适合存储较小的二进制数据(< 1GB)
— OID类型
— 优点:支持大文件(最大4TB),支持流式读写
— 缺点:需要特殊API操作,管理复杂
— BYTEA类型
— 优点:使用简单,与普通列一样操作
— 缺点:有1GB限制,不适合超大文件
— 选择建议:
— 文件 < 100MB:使用BYTEA
-- 文件 >= 100MB:使用OID大对象
1.3 大对象存储机制
大对象的存储机制:
- 大对象数据存储在pg_largeobject系统表中
- 每个大对象被分割成多个数据块(默认2KB)
- 每个数据块有唯一的标识符(OID + 页号)
- 大对象的元数据存储在pg_largeobject_metadata系统表中
Part02-生产环境规划与建议
2.1 大对象设计
大对象的设计要点:
— 1. 表结构设计
— 创建包含大对象的表
CREATE TABLE fgedu_documents (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
file_size BIGINT NOT NULL,
file_type VARCHAR(50) NOT NULL,
file_data OID, — 大对象引用
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 2. 索引设计
— 为大对象表创建索引
CREATE INDEX idx_fgedu_documents_filename ON fgedu_documents(filename);
CREATE INDEX idx_fgedu_documents_file_type ON fgedu_documents(file_type);
CREATE INDEX idx_fgedu_documents_created_at ON fgedu_documents(created_at);
— 3. 触发器设计
— 创建触发器,在删除记录时自动删除大对象
CREATE OR REPLACE FUNCTION delete_large_object()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.file_data IS NOT NULL THEN
PERFORM lo_unlink(OLD.file_data);
END IF;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_delete_large_object
BEFORE DELETE ON fgedu_documents
FOR EACH ROW
EXECUTE FUNCTION delete_large_object();
2.2 大对象安全
大对象的安全考虑:
- 权限控制:使用GRANT/REVOKE控制大对象的访问权限
- 数据加密:对敏感数据进行加密存储
- 访问审计:记录大对象的访问日志
- 备份恢复:确保大对象能够正确备份和恢复
— 授予大对象权限
GRANT SELECT ON fgedu_documents TO fgedu_fgedu;
GRANT INSERT ON fgedu_documents TO fgedu_fgedu;
GRANT UPDATE ON fgedu_documents TO fgedu_fgedu;
— 授予大对象操作权限
— 注意:大对象权限需要在数据库级别授予
GRANT SELECT ON pg_largeobject TO fgedu_fgedu;
GRANT UPDATE ON pg_largeobject TO fgedu_fgedu;
— 撤销权限
REVOKE ALL ON fgedu_documents FROM fgedu_fgedu;
2.3 大对象性能
大对象的性能优化:
- 存储优化:使用TOAST存储大对象数据
- 读取优化:使用流式读取,避免一次性加载大对象
- 写入优化:使用批量写入,减少IO操作
- 缓存优化:合理配置shared_buffers
Part03-生产环境项目实施方案
3.1 大对象创建
3.1.1 使用SQL创建大对象
— 1. 创建空的大对象
SELECT lo_create(0);
— 返回:16384(OID值)
— 2. 创建大对象并导入文件
SELECT lo_import(‘/path/to/file.pdf’);
— 返回:16385(OID值)
— 3. 创建大对象并导出到文件
SELECT lo_export(16385, ‘/path/to/output.pdf’);
— 返回:1(成功)
— 4. 删除大对象
SELECT lo_unlink(16385);
— 返回:1(成功)
3.1.2 使用libpq创建大对象
// 文件:lo_create.c
// from:www.itpux.com.qq113257174.wx:itpux-com
// web: http://www.fgedu.net.cn
#include int main() { if (PQstatus(conn) != CONNECTION_OK) { // 创建大对象 printf(“大对象创建成功,OID: %u\n”, lobjId); // 打开大对象进行写入
#include
#include
const char *conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”;
PGconn *conn = PQconnectdb(conninfo);
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
Oid lobjId = lo_creat(conn, INV_READ | INV_WRITE);
if (lobjId == 0) {
fprintf(stderr, “创建大对象失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
int lobj_fd = lo_open(conn, lobjId, INV_WRITE);
if (lobj_fd < 0) {
fprintf(stderr, "打开大对象失败: %s\n", PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 写入数据
const char *data = "这是一个大对象测试数据";
int written = lo_write(conn, lobj_fd, data, strlen(data));
if (written < 0) {
fprintf(stderr, "写入大对象失败: %s\n", PQerrorMessage(conn));
lo_close(conn, lobj_fd);
PQfinish(conn);
return 1;
}
printf("写入 %d 字节数据\n", written);
// 关闭大对象
lo_close(conn, lobj_fd);
// 将大对象OID插入到表中
char query[256];
sprintf(query, "INSERT INTO fgedu_documents (filename, file_size, file_type, file_data) VALUES ('test.txt', %d, 'text/plain', %u)", written, lobjId);
PGresult *res = PQexec(conn, query);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "插入记录失败: %s\n", PQerrorMessage(conn));
PQclear(res);
lo_unlink(conn, lobjId);
PQfinish(conn);
return 1;
}
PQclear(res);
printf("大对象记录插入成功\n");
PQfinish(conn);
return 0;
}
// 编译步骤
$ gcc -o lo_create lo_create.c -lpq
// 运行结果
$ ./lo_create
连接成功
大对象创建成功,OID: 16384
写入 33 字节数据
大对象记录插入成功
3.2 大对象存储
3.2.1 导入文件到大对象
— 1. 使用lo_import函数
SELECT lo_import(‘/postgresql/data/documents/report.pdf’);
— 返回:16386(OID值)
— 2. 将导入的大对象关联到表
INSERT INTO fgedu_documents (filename, file_size, file_type, file_data)
VALUES (‘report.pdf’, 1024000, ‘fgapplication/pdf’, 16386);
— 3. 批量导入文件
DO $$
DECLARE
file_oid OID;
file_path TEXT;
file_name TEXT;
BEGIN
FOR file_path, file_name IN
SELECT ‘/postgresql/data/documents/’ || filename, filename
FROM pg_ls_dir(‘/postgresql/data/documents/’) AS filename
WHERE filename LIKE ‘%.pdf’
LOOP
file_oid := lo_import(file_path);
INSERT INTO fgedu_documents (filename, file_size, file_type, file_data)
VALUES (file_name, pg_stat_file(file_path).size, ‘fgapplication/pdf’, file_oid);
END LOOP;
END $$;
3.2.2 使用libpq导入文件
// 文件:lo_import_file.c
// from:www.itpux.com.qq113257174.wx:itpux-com
// web: http://www.fgedu.net.cn
#include int import_file(PGconn *conn, const char *filename, const char *filepath) { // 导入文件到大对象 // 插入记录 if (file_ext != NULL) { sprintf(query, “INSERT INTO fgedu_documents (filename, file_size, file_type, file_data) VALUES (‘%s’, %ld, ‘%s’, %u)”, PGresult *res = PQexec(conn, query); printf(“文件导入成功: %s (OID: %u, 大小: %ld bytes)\n”, filename, lobjId, st.st_size); int main() { if (PQstatus(conn) != CONNECTION_OK) { // 导入文件 PQfinish(conn); // 编译步骤 // 运行结果
#include
#include
#include
#include
// 获取文件大小
struct stat st;
if (stat(filepath, &st) != 0) {
fprintf(stderr, “无法获取文件信息: %s\n”, filepath);
return 0;
}
Oid lobjId = lo_import(conn, filepath);
if (lobjId == 0) {
fprintf(stderr, “导入文件失败: %s\n”, PQerrorMessage(conn));
return 0;
}
char query[512];
const char *file_ext = strrchr(filename, ‘.’);
const char *file_type = “fgapplication/octet-stream”;
if (strcmp(file_ext, “.pdf”) == 0) file_type = “fgapplication/pdf”;
else if (strcmp(file_ext, “.jpg”) == 0 || strcmp(file_ext, “.jpeg”) == 0) file_type = “image/jpeg”;
else if (strcmp(file_ext, “.png”) == 0) file_type = “image/png”;
else if (strcmp(file_ext, “.txt”) == 0) file_type = “text/plain”;
}
filename, st.st_size, file_type, lobjId);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, “插入记录失败: %s\n”, PQerrorMessage(conn));
lo_unlink(conn, lobjId);
PQclear(res);
return 0;
}
PQclear(res);
return 1;
}
const char *conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”;
PGconn *conn = PQconnectdb(conninfo);
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
import_file(conn, “report.pdf”, “/postgresql/data/documents/report.pdf”);
import_file(conn, “image.jpg”, “/postgresql/data/documents/image.jpg”);
import_file(conn, “data.txt”, “/postgresql/data/documents/data.txt”);
return 0;
}
$ gcc -o lo_import_file lo_import_file.c -lpq
$ ./lo_import_file
文件导入成功: report.pdf (OID: 16387, 大小: 1024000 bytes)
文件导入成功: image.jpg (OID: 16388, 大小: 204800 bytes)
文件导入成功: data.txt (OID: 16389, 大小: 1024 bytes)
3.3 大对象读取
3.3.1 使用SQL读取大对象
— 1. 导出大对象到文件
SELECT lo_export(file_data, ‘/postgresql/data/output/report.pdf’)
FROM fgedu_documents WHERE id = 1;
— 2. 获取大对象信息
SELECT
d.id,
d.filename,
d.file_size,
d.file_type,
d.file_data,
lom.lomowner,
lom.lomacl
FROM fgedu_documents d
JOIN pg_largeobject_metadata lom ON d.file_data = lom.oid
WHERE d.id = 1;
— 3. 读取大对象数据(使用lo_get函数,PostgreSQL 9.5+)
SELECT lo_get(file_data) FROM fgedu_documents WHERE id = 1;
— 4. 读取大对象的部分数据(使用lo_get函数,PostgreSQL 9.5+)
SELECT lo_get(file_data, 0, 1024) FROM fgedu_documents WHERE id = 1;
3.3.2 使用libpq读取大对象
// 文件:lo_read.c
// from:www.itpux.com.qq113257174.wx:itpux-com
// web: http://www.fgedu.net.cn
#include int export_large_object(PGconn *conn, Oid lobjId, const char *filename) { fclose(fp); printf(“导出成功: %s (%d bytes)\n”, filename, total_read); int main() { if (PQstatus(conn) != CONNECTION_OK) { // 查询大对象信息 int nrows = PQntuples(res);
#include
#include
// 打开大对象进行读取
int lobj_fd = lo_open(conn, lobjId, INV_READ);
if (lobj_fd < 0) {
fprintf(stderr, "打开大对象失败: %s\n", PQerrorMessage(conn));
return 0;
}
// 创建输出文件
FILE *fp = fopen(filename, "wb");
if (fp == NULL) {
fprintf(stderr, "创建文件失败: %s\n", filename);
lo_close(conn, lobj_fd);
return 0;
}
// 读取大对象数据并写入文件
char buffer[8192];
int total_read = 0;
int bytes_read;
while ((bytes_read = lo_read(conn, lobj_fd, buffer, sizeof(buffer))) > 0) {
fwrite(buffer, 1, bytes_read, fp);
total_read += bytes_read;
}
lo_close(conn, lobj_fd);
return 1;
}
const char *conninfo = “fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password”;
PGconn *conn = PQconnectdb(conninfo);
fprintf(stderr, “连接失败: %s\n”, PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
PGresult *res = PQexec(conn, “SELECT id, filename, file_data FROM fgedu_documents LIMIT 3”);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, “查询失败: %s\n”, PQerrorMessage(conn));
PQclear(res);
PQfinish(conn);
return 1;
}
for (int i = 0; i < nrows; i++) {
int id = atoi(PQgetvalue(res, i, 0));
char *filename = PQgetvalue(res, i, 1);
Oid lobjId = (Oid)atoi(PQgetvalue(res, i, 2));
char output_path[256];
sprintf(output_path, "/postgresql/data/output/%s", filename);
export_large_object(conn, lobjId, output_path);
}
PQclear(res);
PQfinish(conn);
return 0;
}
// 编译步骤
$ gcc -o lo_read lo_read.c -lpq
// 运行结果
$ ./lo_read
导出成功: /postgresql/data/output/report.pdf (1024000 bytes)
导出成功: /postgresql/data/output/image.jpg (204800 bytes)
导出成功: /postgresql/data/output/data.txt (1024 bytes)
3.4 大对象删除
3.4.1 使用SQL删除大对象
— 1. 删除单个大对象
SELECT lo_unlink(file_data) FROM fgedu_documents WHERE id = 1;
— 2. 删除记录并自动删除大对象(使用触发器)
DELETE FROM fgedu_documents WHERE id = 1;
— 3. 批量删除大对象
DO $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT file_data FROM fgedu_documents WHERE created_at < CURRENT_DATE - INTERVAL '1 year'
LOOP
PERFORM lo_unlink(rec.file_data);
END LOOP;
DELETE FROM fgedu_documents WHERE created_at < CURRENT_DATE - INTERVAL '1 year';
END $$;
-- 4. 清理孤立的大对象(没有引用的大对象)
DO $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN
SELECT lom.oid
FROM pg_largeobject_metadata lom
LEFT JOIN fgedu_documents d ON lom.oid = d.file_data
WHERE d.id IS NULL
LOOP
PERFORM lo_unlink(rec.oid);
RAISE NOTICE '删除孤立大对象: %', rec.oid;
END LOOP;
END $$;
3.4.2 使用libpq删除大对象
// 文件:lo_delete.c
// from:www.itpux.com.qq113257174.wx:itpux-com
// web: http://www.fgedu.net.cn
#include int delete_large_object(PGconn *conn, int document_id) { PGresult *res = PQexec(conn, query); Oid lobjId = (Oid)atoi(PQgetvalue(res, 0, 0)); // 删除大对象
#include
#include
// 查询大对象OID
char query[256];
sprintf(query, “SELECT file_data FROM fgedu_documents WHERE id = %d”, document_id);
if (PQresultStatus(res) != PGRES_TUPLES_OK || PQntuples(res) == 0) {
fprintf(stderr, “查询失败或记录不存在\n”);
PQclear(res);
return 0;
}
PQclear(res);
if (lo_unlink(conn, lobjId) < 0) {
fprintf(stderr, "删除大对象失败: %s\n", PQerrorMessage(conn));
return 0;
}
// 删除记录
sprintf(query, "DELETE FROM fgedu_documents WHERE id = %d", document_id);
res = PQexec(conn, query);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "删除记录失败: %s\n", PQerrorMessage(conn));
PQclear(res);
return 0;
}
PQclear(res);
printf("删除成功: 文档ID %d, 大对象OID %u\n", document_id, lobjId);
return 1;
}
int cleanup_orphaned_lobs(PGconn *conn) {
PGresult *res = PQexec(conn,
"SELECT lom.oid FROM pg_largeobject_metadata lom "
"LEFT JOIN fgedu_documents d ON lom.oid = d.file_data "
"WHERE d.id IS NULL");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "查询失败: %s\n", PQerrorMessage(conn));
PQclear(res);
return 0;
}
int nrows = PQntuples(res);
int deleted = 0;
for (int i = 0; i < nrows; i++) {
Oid lobjId = (Oid)atoi(PQgetvalue(res, i, 0));
if (lo_unlink(conn, lobjId) == 0) {
deleted++;
printf("删除孤立大对象: %u\n", lobjId);
}
}
PQclear(res);
printf("清理完成,删除 %d 个孤立大对象\n", deleted);
return deleted;
}
int main() {
const char *conninfo = "fgedu.net.cn=localfgedu.net.cn port=5432 fgedudb=fgedudb fgedu=pgsql password=postgres_password";
PGconn *conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr, "连接失败: %s\n", PQerrorMessage(conn));
PQfinish(conn);
return 1;
}
// 删除指定文档
delete_large_object(conn, 1);
// 清理孤立大对象
cleanup_orphaned_lobs(conn);
PQfinish(conn);
return 0;
}
// 编译步骤
$ gcc -o lo_delete lo_delete.c -lpq
// 运行结果
$ ./lo_delete
删除成功: 文档ID 1, 大对象OID 16387
删除孤立大对象: 16390
删除孤立大对象: 16391
清理完成,删除 2 个孤立大对象
Part04-生产案例与实战讲解
4.1 大对象问题
在使用大对象时,可能会遇到以下问题:
4.1.1 存储问题
# 1. 存储空间不足
# 症状:导入大对象时失败
# 原因:磁盘空间不足
# 解决方案:
# – 清理不必要的大对象
# – 扩展存储空间
# – 归档历史数据
# 2. 大对象孤立
# 症状:存在没有引用的大对象
# 原因:删除记录时未删除大对象
# 解决方案:
# – 使用触发器自动删除
# – 定期清理孤立大对象
# – 使用外键约束(PostgreSQL 11+)
# 3. 权限问题
# 症状:无法访问大对象
# 原因:用户权限不足
# 解决方案:
# – 授予大对象权限
# – 使用SET SESSION AUTHORIZATION
# – 调整pg_largeobject权限
4.1.2 性能问题
# 1. 读取速度慢
# 症状:读取大对象时响应慢
# 原因:大对象数据分散存储
# 解决方案:
# – 使用流式读取
# – 增加shared_buffers
# – 使用SSD存储
# 2. 写入速度慢
# 症状:导入大对象时速度慢
# 原因:大量随机IO
# 解决方案:
# – 使用批量导入
# – 调整checkpoint参数
# – 使用异步提交
# 3. 备份恢复慢
# 症状:备份和恢复大对象时速度慢
# 原因:大对象数据量大
# 解决方案:
# – 使用pg_dump的–blobs选项
# – 使用文件系统备份
# – 归档旧数据
4.2 大对象解决方案
# 1. 存储优化
– 使用TOAST存储
– 定期清理孤立大对象
– 归档历史数据
– 使用压缩存储
# 2. 性能优化
– 使用流式读写
– 增加缓存大小
– 使用SSD存储
– 优化查询语句
# 3. 安全优化
– 使用权限控制
– 加密敏感数据
– 审计访问日志
– 定期备份
# 4. 管理优化
– 使用触发器自动管理
– 定期监控存储使用
– 建立数据保留策略
– 自动化运维脚本
4.3 大对象实战案例
# 1. 需求分析
# – 存储PDF、Word、图片等文档
# – 支持文档上传、下载、删除
# – 支持文档分类和检索
# – 支持版本控制
# 2. 数据库设计
## 文档表
CREATE TABLE fgedu_documents (
id SERIAL PRIMARY KEY,
filename VARCHAR(255) NOT NULL,
file_size BIGINT NOT NULL,
file_type VARCHAR(50) NOT NULL,
file_data OID,
category_id INTEGER REFERENCES fgedu_categories(id),
version INTEGER DEFAULT 1,
created_by INTEGER REFERENCES fgedu_fgedus(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
## 文档版本表
CREATE TABLE fgedu_document_versions (
id SERIAL PRIMARY KEY,
document_id INTEGER REFERENCES fgedu_documents(id),
version INTEGER NOT NULL,
file_data OID,
created_by INTEGER REFERENCES fgedu_fgedus(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
## 触发器:删除文档时删除大对象
CREATE OR REPLACE FUNCTION delete_document_large_object()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.file_data IS NOT NULL THEN
PERFORM lo_unlink(OLD.file_data);
END IF;
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_delete_document_large_object
BEFORE DELETE ON fgedu_documents
FOR EACH ROW
EXECUTE FUNCTION delete_document_large_object();
# 3. 实施效果
# – 支持存储最大4TB的文档
# – 支持流式上传下载
# – 自动清理孤立大对象
# – 支持文档版本控制
# 4. 运维管理
# – 定期备份大对象数据
# – 监控存储使用情况
# – 归档历史版本
# – 清理过期文档
Part05-风哥经验总结与分享
5.1 大对象使用技巧
大对象使用技巧:
- 选择合适类型:小文件用BYTEA,大文件用OID
- 使用触发器:自动管理大对象生命周期
- 流式读写:避免一次性加载大对象
- 定期清理:清理孤立大对象,释放空间
- 权限控制:合理设置大对象访问权限
- 备份策略:制定完善的大对象备份策略
- 监控存储:监控大对象存储使用情况
- 归档策略:归档历史数据,优化性能
5.2 大对象监控
— 1. 查看大对象统计信息
SELECT
COUNT(*) as total_lobs,
SUM(pg_largeobject_metadata.len) as total_size,
AVG(pg_largeobject_metadata.len) as avg_size,
MAX(pg_largeobject_metadata.len) as max_size
FROM pg_largeobject_metadata;
— 2. 查看大对象分布
SELECT
CASE
WHEN pg_largeobject_metadata.len < 1024 THEN '< 1KB'
WHEN pg_largeobject_metadata.len < 1024*1024 THEN '1KB - 1MB'
WHEN pg_largeobject_metadata.len < 10*1024*1024 THEN '1MB - 10MB'
WHEN pg_largeobject_metadata.len < 100*1024*1024 THEN '10MB - 100MB'
ELSE '> 100MB’
END as size_range,
COUNT(*) as count,
pg_size_pretty(SUM(pg_largeobject_metadata.len)) as total_size
FROM pg_largeobject_metadata
GROUP BY 1
ORDER BY 2 DESC;
— 3. 查看孤立大对象
SELECT
COUNT(*) as orphaned_count,
pg_size_pretty(SUM(lom.len)) as orphaned_size
FROM pg_largeobject_metadata lom
LEFT JOIN fgedu_documents d ON lom.oid = d.file_data
WHERE d.id IS NULL;
— 4. 查看大对象增长趋势
SELECT
DATE_TRUNC(‘day’, lom.lomcreated) as date,
COUNT(*) as new_lobs,
pg_size_pretty(SUM(lom.len)) as new_size
FROM pg_largeobject_metadata lom
GROUP BY 1
ORDER BY 1 DESC
LIMIT 30;
5.3 资源与工具
大对象相关资源与工具:
- PostgreSQL官方文档:https://www.postgresql.org/docs/current/largeobjects.html
- pg_largeobject系统表:存储大对象数据
- pg_largeobject_metadata系统表:存储大对象元数据
- lo_*函数:大对象操作函数
- libpq库:提供大对象编程接口
- pgAdmin:图形化管理大对象
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
