【问题标题】:Storing logs in postgres database as text vs json type将日志存储在 postgres 数据库中作为文本与 json 类型
【发布时间】:2021-08-14 23:35:43
【问题描述】:
假设我们要创建一个表来在数据库中存储用户活动日志。我可以想到两种方法:
-
每个日志条目都有一行,其中包含日志 ID、用户的外键和日志内容。这样,我们将为发生的每个活动单独设置一行。
-
每个唯一用户的活动(用户的外键)和一个日志 ID 有一行。我们可以有一个json 类型的列来存储与每个用户关联的日志。每次发生活动时,我们都可以获取关联的日志条目并通过将新活动附加到它来更新其 JSON 列。
方法 1 提供了一种无需更新旧日志条目即可添加新日志条目的简洁方式。但是查询这样的表来获取用户的活动会查询整个表。
方法 2 增加了添加新用户活动的复杂性,因为我们必须获取和更新 JSON 对象,但查询只会返回一行。
我需要帮助来了解一种方法是否明显优于另一种方法。
【问题讨论】:
标签:
sql
postgresql
database-design
【解决方案1】:
数据库经过优化,可以在大表中存储和检索小行。所以选择第一个解决方案。索引使连接变得如此之快。
将用户的所有数据集中到单个 JSON 对象中不会让您满意:每次更新都必须读取、修改和写入整个 JSON,这根本没有效率。
【解决方案2】:
如果您记录大量更改,就属性而言,我会创建一个表:
log_id、user_id (fk) 和 log 采用 json 格式,每一行作为一个活动。
如果您为表编制索引,则不会出现性能问题。在 postgresql 中,您可以对 json 列中的字段进行索引。
随着列大小的增加,每次更新后方法 2 的更新速度都会变慢。此外,查询会更复杂。
【解决方案3】:
另外考虑一个可以parse semi-structured data into database columns的日志框架,比如Serilog。
否则我也会推荐您的选项“1”,即每个日志一行,并在 user_id 上有一个索引,但建议在您的列中添加时间戳,以便查询引擎可以在必须解析之前按事件顺序排序json 本身作为时间戳:
CREATE TABLE user_log
(
log_id bigint, -- (PRIMARY KEY),
log_ts timestamp NOT NULL DEFAULT(now()),
user_id int NOT NULL, --REFERENCES users(user_id),
log_content json
);
CREATE INDEX ON user_log(user_id);
SELECT user_id, log_ts, log_content => 'action' AS user_action FROM user_log WHERE user_id = ? ORDER BY log_ts;