用 PostgreSQL 一库模拟 MySQL / MongoDB / Redis / Elasticsearch(附 ts_rank 详解)

一、背景

在实际工程中,我们通常会组合使用多种数据库:

  • MySQL:关系型事务处理

  • MongoDB:文档存储

  • Redis:缓存 & KV

  • Elasticsearch:全文检索

但问题是:

👉 多数据库架构复杂、运维成本高、数据同步困难

那么有没有一种方案:

用一个数据库覆盖多种能力?

答案是:PostgreSQL 可以做到 70% 的能力整合


二、核心思路

不是"让 PostgreSQL 变成这些数据库",而是:

用一张统一表 + 不同索引 + 不同字段结构,模拟不同数据库的访问模式


三、统一表设计

复制代码
CREATE TABLE unified_entity (
    id              BIGSERIAL PRIMARY KEY,
    entity_type     TEXT NOT NULL,
    entity_key      TEXT NOT NULL,
    tenant_id       BIGINT NOT NULL,

    name            TEXT,
    description     TEXT,
    status          INT,
    score           NUMERIC,

    data            JSONB,
    search_vector   tsvector,

    created_at      TIMESTAMP NOT NULL DEFAULT now(),
    updated_at      TIMESTAMP NOT NULL DEFAULT now(),
    expire_at       TIMESTAMP
);

字段设计思想:

字段 用途
entity_key 模拟 Redis key
data(JSONB) 模拟 MongoDB
search_vector 模拟 Elasticsearch
expire_at 模拟 TTL
普通字段 模拟 MySQL

四、索引设计(关键)

1️⃣ MySQL 风格(B-Tree)

复制代码
CREATE UNIQUE INDEX ux_entity_type_key
ON unified_entity (entity_type, entity_key);

CREATE INDEX idx_entity_tenant_status_created
ON unified_entity (tenant_id, status, created_at DESC);

2️⃣ MongoDB 风格(JSONB + GIN)

复制代码
CREATE INDEX idx_entity_data_gin
ON unified_entity USING GIN (data);

CREATE INDEX idx_entity_user_id
ON unified_entity ((data->>'user_id'));

3️⃣ Elasticsearch 风格(全文检索)

复制代码
CREATE INDEX idx_entity_search
ON unified_entity USING GIN (search_vector);

4️⃣ Redis 风格(TTL)

复制代码
CREATE INDEX idx_entity_expire_at
ON unified_entity (expire_at);

五、模拟数据

复制代码
INSERT INTO unified_entity (
    entity_type, entity_key, tenant_id,
    name, description, status, score,
    data, created_at, expire_at
)
VALUES
('user', 'user:1001', 1,
 'Alice', 'VIP user from US', 1, 9.5,
 '{"user_id":"1001","region":"US","level":"vip"}',
 now(), NULL),

('user', 'user:1002', 1,
 'Bob', 'Normal user from CN', 1, 7.2,
 '{"user_id":"1002","region":"CN","level":"normal"}',
 now(), NULL),

('article', 'article:3001', 1,
 'PostgreSQL Index', 'PostgreSQL indexing is powerful',
 1, 8.8,
 '{"category":"tech"}',
 now(), NULL);

六、更新全文索引

复制代码
UPDATE unified_entity
SET search_vector =
    to_tsvector('simple', coalesce(name,'') || ' ' || coalesce(description,''));

七、模拟不同数据库查询


✅ 1. MySQL(关系查询)

复制代码
SELECT *
FROM unified_entity
WHERE entity_type = 'user'
ORDER BY created_at DESC;

✅ 2. MongoDB(文档查询)

复制代码
SELECT *
FROM unified_entity
WHERE data @> '{"region":"US"}';

✅ 3. Redis(KV + TTL)

复制代码
SELECT data
FROM unified_entity
WHERE entity_key = 'user:1001'
  AND (expire_at IS NULL OR expire_at > now());

✅ 4. Elasticsearch(全文检索)

复制代码
SELECT id, name,
       ts_rank(search_vector, plainto_tsquery('postgresql')) AS rank
FROM unified_entity
WHERE search_vector @@ plainto_tsquery('postgresql')
ORDER BY rank DESC;

八、重点:ts_rank 是怎么计算的?

很多人用 PostgreSQL 搜索时,都会疑惑:

👉 rank 是怎么来的?


1️⃣ 核心本质

rank = "相关性评分"

由多个因素共同决定:


2️⃣ 影响因素

(1)词频(Term Frequency)

出现越多 → 分越高

复制代码
Bob Bob Bob → 高
Bob         → 低

(2)匹配词数量
复制代码
plainto_tsquery('Bob Alice')
文档 rank
只含 Bob
Bob + Alice

(3)位置(Position)

越靠前越重要:

复制代码
Bob is user → 高
user is Bob → 低

(4)权重(A/B/C/D)

PostgreSQL 支持 4 级权重:

权重 数值
A 1.0
B 0.4
C 0.2
D 0.1

示例:

复制代码
setweight(to_tsvector(name), 'A') ||
setweight(to_tsvector(description), 'C')

👉 title 比 content 更重要


(5)文档长度归一化

文档越长 → 分数被稀释


3️⃣ 简化公式

复制代码
rank ≈ Σ(词频 × 权重 × 位置系数) / 文档长度

4️⃣ 为什么你的 rank 可能都一样?

如果:

  • 每条数据只出现一次关键词

那么:

👉 rank 基本一样(很常见)


5️⃣ ts_rank vs ts_rank_cd

函数 特点
ts_rank 基础
ts_rank_cd 更接近搜索引擎(推荐)

6️⃣ 进阶(归一化参数)

复制代码
ts_rank(vector, query, normalization)

常见:

参数 作用
0 不处理
1 除以长度
2 除以词数

九、优缺点分析


优点

  • 一套数据库覆盖多场景

  • 减少系统复杂度

  • 无需多数据同步

  • 学习成本低


缺点

  • 写入成本高(索引多)

  • 不如专用数据库极致性能

  • JSONB 更新有写放大

  • 搜索能力弱于 Elasticsearch

  • 无法替代 Redis 的低延迟

相关推荐
Trouvaille ~2 小时前
【MySQL篇】表的约束:保证数据完整性
数据库·mysql·约束·数据完整性·实体完整性·域完整性·参照完整性
XDHCOM13 小时前
PostgreSQL 25001: active_sql_transaction 报错原因分析,故障修复步骤详解,远程处理解决方案
数据库·sql·postgresql
卤炖阑尾炎14 小时前
PostgreSQL 日常运维全指南:从基础操作到备份恢复
运维·数据库·postgresql
计算机毕设vx_bysj686915 小时前
【免费领源码】77196基于java的手机银行app管理系统的设计与实现 计算机毕业设计项目推荐上万套实战教程JAVA,node.js,C++、python、大屏数据可视化
java·mysql·智能手机·课程设计
吴声子夜歌15 小时前
ES6——正则的扩展详解
前端·mysql·es6
xixingzhe215 小时前
Mysql统计空间增量
数据库·mysql
程序员萌萌15 小时前
Java之mysql实战讲解(三):聚簇索引与非聚簇索引
java·mysql·聚簇索引
cozil16 小时前
记录mysql创建数据库未指定字符集引发的问题及解决方法
数据库·mysql
AC赳赳老秦16 小时前
OpenClaw数据库高效操作指南:MySQL/PostgreSQL批量处理与数据迁移实战
大数据·数据库·mysql·elasticsearch·postgresql·deepseek·openclaw