
在用户行为日志(每天100万条,需实时分析)场景下,推荐采用PostgreSQL作为主存储,并结合按日期分库分表(分片键为action_time的年月字段),通过时间范围索引和物化视图优化,满足高并发写入与实时查询需求。
老师解释数据库核心特性:
| 数据库 | 定义 | 核心特性 | 使用场景 | 注意点 |
|---|---|---|---|---|
| MySQL | 关系型数据库(InnoDB引擎) | 强事务(ACID),表结构固定,B-tree索引优化成熟 | 事务性数据(订单、用户表),结构化数据,高并发写 | 分表后索引维护复杂(需为每个分表建时间范围索引),JSON存储需额外列或存储过程,实时分析需ETL |
| PostgreSQL | 关系型数据库(支持JSONB) | JSONB半结构化存储,复杂查询(窗口函数),ACID,扩展性强 | 结构化+半结构化数据(日志+用户信息),实时分析(如时间序列聚合) | JSONB需Gin索引优化,大表分片需考虑时间范围索引,物化视图支持实时聚合 |
| MongoDB | 文档型NoSQL | BSON文档模型,灵活,4.0+多文档事务(复杂事务有限) | 非结构化/半结构化数据(日志、内容),高并发读 | 事务支持弱(复杂事务失败率高),复杂查询性能(如聚合)不如关系型,数据一致性依赖应用 |
action_time的年月):
CREATE TABLE user_action_log_202401 (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
action_time TIMESTAMP NOT NULL,
action_type VARCHAR(50) NOT NULL,
action_details JSON NULL,
CONSTRAINT fk_date CHECK (EXTRACT(YEAR FROM action_time)=2024 AND EXTRACT(MONTH FROM action_time)=1)
);
-- 时间范围索引(B-tree+Gin)
CREATE INDEX idx_action_time ON user_action_log_202401 (action_time);
CREATE INDEX idx_action_time_gin ON user_action_log_202401 (action_details GIN);
CREATE TABLE user_action_log (
id SERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
action_time TIMESTAMPTZ NOT NULL,
action_type TEXT NOT NULL,
action_details JSONB NULL
);
-- 分表约束
CREATE TABLE user_action_log_202401 AS
SELECT * FROM user_action_log
WHERE EXTRACT(YEAR FROM action_time)=2024 AND EXTRACT(MONTH FROM action_time)=1;
-- 时间范围索引(B-tree+Gin)
CREATE INDEX idx_action_time ON user_action_log_202401 (action_time);
CREATE INDEX idx_action_time_gin ON user_action_log_202401 (action_details GIN);
{
"_id": ObjectId("..."),
"user_id": 12345,
"action_time": ISODate("2024-01-10T10:30:00Z"),
"action_type": "click",
"action_details": {
"page": "home",
"element": "banner"
}
}
CREATE MATERIALIZED VIEW user_action_agg AS
SELECT user_id, action_type, COUNT(*) as count
FROM user_action_log_202401
WHERE action_time BETWEEN '2024-01-01' AND '2024-01-31'
GROUP BY user_id, action_type;
-- 刷新物化视图(实时更新)
REFRESH MATERIALIZED VIEW user_action_agg;
“面试官您好,针对用户行为日志的存储与实时分析需求,我分析如下:首先,MySQL作为传统关系型数据库,强事务适合事务性数据,但日志的半结构化操作详情需额外JSON列,分表后索引维护复杂,实时分析需ETL;PostgreSQL支持JSONB列(半结构化存储),事务与ACID特性满足数据一致性,且具备复杂查询能力(如时间序列聚合),适合结构化+半结构化数据;MongoDB文档模型灵活,但事务支持弱(4.0+多文档事务,复杂事务仍有限),复杂查询性能不如关系型。综合来看,推荐PostgreSQL,并结合按日期分库分表(分片键为action_time的年月字段),通过时间范围索引和物化视图优化,满足每天100万条的高并发写入与实时查询需求。后续优化方案:1. 对action_time列建时间范围索引(B-tree+Gin),加速时间范围查询;2. 对action_type列建复合索引,优化聚合查询;3. 使用物化视图预计算聚合结果,支持实时分析;4. 按日期分表,每个分表存储一个月数据,查询时通过时间范围+分片键快速定位,避免全表扫描。”
SELECT user_id, COUNT(*), SUM(1) OVER (PARTITION BY user_id ORDER BY action_time) FROM user_action_log WHERE action_time BETWEEN '2024-01-01' AND '2024-01-31'),MySQL需额外计算,MongoDB聚合框架也可,但性能不如PostgreSQL。action_time的B-tree索引)维护成本高,查询性能下降,分表后需为每个分表建立时间范围索引。action_details)建立Gin索引,查询时无法高效过滤,影响实时分析效率(如查询包含特定page的日志,需扫描整个表)。user_id分片),导致查询时需要跨多个分片聚合,性能下降;正确选择分片键(如按日期分片)可提高查询效率。