You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何定位向pg_largeobject溢出数据的表与列?

定位pg_largeobject数据来源及监控写入事件

一、回溯现有大对象的来源表和列

PostgreSQL默认没有记录pg_largeobject写入来源的内置日志,需要通过关联业务表的大对象类型列来排查:

1. 找出所有存储大对象引用的列

pg_largeobject中的loid对应业务表中oid或lo类型列的值,先查询数据库中所有这类列:

SELECT n.nspname AS schema_name,
       c.relname AS table_name,
       a.attname AS column_name
FROM pg_attribute a
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE a.atttypid IN (SELECT oid FROM pg_type WHERE typname IN ('oid', 'lo'))
  AND a.attnum > 0
  AND NOT a.attisdropped
  AND c.relkind = 'r';

2. 关联pg_largeobject统计来源数据

对上述查询到的列,统计哪些loid存在于pg_largeobject中,并计算对应数据大小。可以用以下PL/pgSQL函数批量遍历查询:

CREATE OR REPLACE FUNCTION find_lo_owners()
RETURNS TABLE(schema_name text, table_name text, column_name text, loid oid, total_size text) AS $$
DECLARE
    col_rec record;
BEGIN
    FOR col_rec IN
        SELECT n.nspname, c.relname, a.attname
        FROM pg_attribute a
        JOIN pg_class c ON a.attrelid = c.oid
        JOIN pg_namespace n ON c.relnamespace = n.oid
        WHERE a.atttypid IN (SELECT oid FROM pg_type WHERE typname IN ('oid', 'lo'))
          AND a.attnum > 0
          AND NOT a.attisdropped
          AND c.relkind = 'r'
    LOOP
        RETURN QUERY EXECUTE format(
            'SELECT %L, %L, %L, %I, pg_size_pretty(sum(l.length))
             FROM %I.%I t
             JOIN pg_largeobject l ON t.%I = l.loid
             GROUP BY t.%I',
            col_rec.nspname, col_rec.relname, col_rec.attname,
            col_rec.attname, col_rec.nspname, col_rec.relname,
            col_rec.attname, col_rec.attname
        );
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 执行查询获取结果
SELECT * FROM find_lo_owners();

3. 定位体积最大的大对象来源

先找出pg_largeobject中体积最大的loid,再关联到业务表:

WITH top_large_objects AS (
    SELECT loid, sum(length) AS total_bytes
    FROM pg_largeobject
    GROUP BY loid
    ORDER BY total_bytes DESC
    LIMIT 10
)
SELECT t.loid,
       pg_size_pretty(t.total_bytes) AS size,
       n.nspname AS schema,
       c.relname AS table,
       a.attname AS column
FROM top_large_objects t
JOIN pg_attribute a ON EXISTS (
    SELECT 1 FROM pg_class tbl
    WHERE tbl.oid = a.attrelid
      AND a.atttypid IN (SELECT oid FROM pg_type WHERE typname IN ('oid', 'lo'))
      AND a.attnum > 0
      AND NOT a.attisdropped
      AND EXISTS (SELECT 1 FROM tbl WHERE (tbl).a.attname = t.loid)
)
JOIN pg_class c ON a.attrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid;

二、监控未来的pg_largeobject写入事件

可以通过自定义日志表+包装函数的方式捕获写入行为:

1. 创建写入日志表

CREATE TABLE lo_operation_log (
    log_id SERIAL PRIMARY KEY,
    op_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
    username TEXT DEFAULT current_user,
    client_ip INET DEFAULT inet_client_addr(),
    operation TEXT, -- 如lo_create、lo_import、lo_write
    loid OID,
    source_query TEXT DEFAULT current_query()
);

2. 包装大对象操作函数

PostgreSQL的大对象操作通过pg_catalog下的函数完成,我们可以包装这些函数,写入日志:

-- 备份原始lo_create函数
CREATE OR REPLACE FUNCTION pg_catalog.lo_create_orig(oid) RETURNS oid AS 'pg_catalog.lo_create' LANGUAGE C;

-- 创建带日志的lo_create包装函数
CREATE OR REPLACE FUNCTION pg_catalog.lo_create(oid) RETURNS oid AS $$
DECLARE
    new_oid oid;
BEGIN
    new_oid := pg_catalog.lo_create_orig($1);
    INSERT INTO lo_operation_log (operation, loid)
    VALUES ('lo_create', new_oid);
    RETURN new_oid;
END;
$$ LANGUAGE plpgsql;

-- 同理包装lo_import函数
CREATE OR REPLACE FUNCTION pg_catalog.lo_import_orig(text) RETURNS oid AS 'pg_catalog.lo_import' LANGUAGE C;

CREATE OR REPLACE FUNCTION pg_catalog.lo_import(text) RETURNS oid AS $$
DECLARE
    new_oid oid;
BEGIN
    new_oid := pg_catalog.lo_import_orig($1);
    INSERT INTO lo_operation_log (operation, loid)
    VALUES ('lo_import', new_oid);
    RETURN new_oid;
END;
$$ LANGUAGE plpgsql;

后续调用lo_create、lo_import等函数时,操作记录会自动写入lo_operation_log,通过source_query字段可以查看触发操作的业务SQL,从而定位来源表和列。

3. 实时监控活跃写入会话

如果需要临时排查正在写入的会话,可以查询pg_stat_activity:

SELECT pid, usename, client_addr, query, state
FROM pg_stat_activity
WHERE query LIKE '%pg_largeobject%' OR query LIKE '%lo_%'
  AND state = 'active';

内容的提问来源于stack exchange,提问作者Carla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:18:24