插入触发器后的 postgres 高 CPU 使用率

postgres high CPU usage on after insert trigger

我有一个应用程序,我在其中接收报价(商品的买入或卖出)流,并试图生成一个 table 详细的 OHLC(开盘价、最高价、最低价、收盘价)列数据。我在 table 中创建这些而不是从报价 table 中派生它们的原因是因为我得到了大量的报价(每天 10000000)。使用此策略,我可以按计划从数据库中删除所有报价,以保持我的数据库大小可管理。

我的模式与此大致相同(为简洁起见,删除了不必要的列)。

CREATE TABLE tick (
    executed TIMESTAMP WITH TIME ZONE NOT NULL,
    price NUMERIC
);

CREATE TABLE ohlc_minute (
    created TIMESTAMP WITH TIME ZONE NOT NULL PRIMARY KEY,
    open NUMERIC,
    high NUMERIC,
    low NUMERIC,
    close NUMERIC,
);

我的想法是在 tick 上创建一个插入后触发器,它计算 OHLC 的最后一分钟,然后 upserts 将其插入 ohlc_minute table 但启用此触发器后,cpu 数据库的使用率几乎立即跃升至 100%。

CREATE OR REPLACE FUNCTION update_ohlc()
    RETURNS trigger AS
$BODY$
BEGIN
    INSERT INTO ohlc_minute (created, open, high, low, close)
        SELECT
            date_trunc('minute', NEW.executed) executed,
            (array_agg(price ORDER BY executed ASC))[1] as open,
            MAX(price) as high,
            MIN(price) as low,
            (array_agg(price ORDER BY executed DESC))[1] as close
        FROM tick
        WHERE executed BETWEEN date_trunc('minute', NEW.executed) AND date_trunc('minute', NEW.executed) + interval '1 Min'
    ON CONFLICT (created)
    DO UPDATE
    SET open = EXCLUDED.open, high=EXCLUDED.high, low=EXCLUDED.low, close=EXCLUDED.close;
    RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER tick_insert
    AFTER INSERT
    ON tick
    FOR EACH ROW 
    EXECUTE PROCEDURE update_ohlc();

我有的一个可能的替代方法是 运行 手动按计划更新所有 ohlc 柱的等效函数,但我喜欢始终拥有最新部分(例如,当前柱不到一分钟)的想法) 可用的 ohlc 信息。我可以做任何简单的优化来降低触发函数的 CPU 使用率吗?

我解决了我自己的问题,答案很明显没有索引,我创建了一个索引

CREATE INDEX IF NOT EXISTS execute_index ON tick (executed);

和CPU使用率已经下降到可接受的水平,但是我仍然有兴趣看到优化的解决方案。

报价能保证按顺序到达吗?如果插入成功,那么您的聚合只在一行上完成,因此所有聚合的答案就是价格。如果插入冲突,那么您应该能够根据现有的和排除的值计算每个值。

CREATE OR REPLACE FUNCTION update_ohlc()
    RETURNS trigger AS
$BODY$
BEGIN
    INSERT INTO ohlc_minute (created, open, high, low, close)
        values (
            date_trunc('minute', NEW.executed),
            NEW.price,
            NEW.price,
            NEW.price,
            NEW.price 
        )
    ON CONFLICT (created)
    DO UPDATE
    SET high=greatest(ohlc_minute.high,EXCLUDED.high), 
      low=least(ohlc_minute.low,EXCLUDED.low),
      close=EXCLUDED.close;
    RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

如果不能保证它们按顺序到达,那么我认为如果您坚持在累积的分钟内提供部分结果,那么您当前的解决方案将是最佳的。