Postgres 杀疯了,堪称 “六边形战士”,还要 Redis 干啥?
在过去的几个月中,有一个现象一直萦绕在我心头,令我深感困扰:我目睹了太多独立开发者和初创公司创始人在技术选型时,不假思索地堆砌各种专用工具——用Redis作缓存,用RabbitMQ处理消息队列,用Elasticsearch实现搜索,甚至引入MongoDB等非关系型数据库。我能够更快地交付功能,需要调试的组件更少,基础设施成本降至最低。对比之下,管理多个以不同方式扩展的独立服务——Redis可能内存
在过去的几个月中,有一个现象一直萦绕在我心头,令我深感困扰:我目睹了太多独立开发者和初创公司创始人在技术选型时,不假思索地堆砌各种专用工具——用Redis作缓存,用RabbitMQ处理消息队列,用Elasticsearch实现搜索,甚至引入MongoDB等非关系型数据库。这究竟是为了什么?
坦白说,我也曾陷入同样的思维定式。当我开始构思UserJot——一个用户反馈与产品路线图管理工具时,我的第一反应同样是规划一个“标准”的分布式架构,为每个功能模块配备独立的技术组件。但就在那一刻,我停下来问了自己一个关键问题:如果我只用PostgreSQL来实现所有功能,会怎样?
这个问题的答案,揭示了一个许多人刻意回避的事实:
PostgreSQL几乎能满足你的所有需求,而且它的表现远超你的想象。
🔍 关于“PostgreSQL无法扩展”的迷思
你是否也曾被这样的观点影响:PostgreSQL“仅仅是个关系型数据库”,而专业的工作必须交给专业的工具?我曾经也深信不疑,直到我发现:
-
Instagram在单个PostgreSQL实例上支撑了1400万用户
-
Discord处理着数十亿条消息的庞大规模
-
Notion的整个产品体系都构建于PostgreSQL之上
关键在于,这些公司早已不再以2005年的方式来使用PostgreSQL。
🚀 队列系统:告别Redis与RabbitMQ
PostgreSQL原生支持LISTEN/NOTIFY机制,其作业队列的处理能力甚至优于许多专用方案:
sql
-- 基于PostgreSQL的轻量级作业队列
CREATE TABLE job_queue (
id SERIAL PRIMARY KEY,
job_type VARCHAR(50),
payload JSONB,
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW(),
processed_at TIMESTAMP
);
-- 支持ACID的事务性作业处理
BEGIN;
UPDATE job_queue
SET status = 'processing', processed_at = NOW()
WHERE id = (
SELECT id FROM job_queue
WHERE status = 'pending'
ORDER BY created_at
FOR UPDATE SKIP LOCKED
LIMIT 1
)
RETURNING *;
COMMIT;
这种方式天然支持精确一次(Exactly-Once)处理语义。相比之下,用Redis实现相同功能将面临诸多挑战。
在UserJot中,我正是采用这种模式来处理反馈提交、通知发送和路线图更新等场景。单一事务即可确保一致性,完全无需引入消息代理的复杂度。
💾 键值存储:Redis的替代方案
各大云平台的Redis服务起价约为20美元/月。而PostgreSQL的JSONB功能已内置于您现有的数据库中,足以应对大多数使用场景:
sql
-- 实现键值存储的替代方案
CREATE TABLE kv_store (
key VARCHAR(255) PRIMARY KEY,
value JSONB,
expires_at TIMESTAMP
);
-- 为JSON查询创建GIN索引
CREATE INDEX idx_kv_value ON kv_store USING GIN (value);
-- 支持嵌套JSON的高效查询
SELECT * FROM kv_store
WHERE value @> '{"user_id": 12345}';
@>操作符是PostgreSQL处理JSON的利器,其查询性能甚至超越多数NoSQL数据库,同时保持数据一致性。
🔍 全文搜索:Elasticsearch并非必需
Elasticsearch集群不仅成本高昂,维护也相当复杂。PostgreSQL内置的全文搜索功能已经足够强大:
sql
-- 为任意表添加搜索功能
ALTER TABLE posts ADD COLUMN search_vector tsvector;
-- 自动更新搜索索引
CREATE OR REPLACE FUNCTION update_search_vector()
RETURNS trigger AS $$
BEGIN
NEW.search_vector := to_tsvector('english',
COALESCE(NEW.title, '') || ' ' ||
COALESCE(NEW.content, '')
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 支持相关性排序的搜索结果
SELECT title, ts_rank(search_vector, query) as rank
FROM posts, to_tsquery('startup & postgres') query
WHERE search_vector @@ query
ORDER BY rank DESC;
该方案完整支持模糊匹配、词干提取和相关性排序。
在UserJot的反馈搜索功能中,用户可以通过此实现跨标题、描述和评论快速查找功能请求,完全无需部署Elasticsearch集群。
📡 实时功能:简化WebSocket架构
基于PostgreSQL的LISTEN/NOTIFY机制,您可以轻松实现实时更新,无需额外的基础设施:
sql
-- 向客户端推送变更通知
CREATE OR REPLACE FUNCTION notify_changes()
RETURNS trigger AS $$
BEGIN
PERFORM pg_notify('table_changes',
json_build_object(
'table', TG_TABLE_NAME,
'action', TG_OP,
'data', row_to_json(NEW)
)::text
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
应用程序只需监听这些通知即可向用户推送实时更新,无需依赖Redis的发布/订阅功能。
💰 专业工具的隐性成本
让我们算一笔经济账,一个典型的“现代”技术栈包含:
-
Redis:20美元/月
-
消息队列:25美元/月
-
搜索服务:50美元/月
-
三项服务的监控:30美元/月
总计:每月125美元
但这仅仅是托管成本,真正的痛点在于:
运维复杂度
-
需要监控、更新和调试三种不同的服务
-
各自独立的扩展模式与故障模式
-
多套配置需要维护
-
分离的备份与灾难恢复流程
-
每项服务都有不同的安全考量
开发复杂度
-
不同的客户端库和连接模式
-
多服务协同部署
-
数据一致性问题
-
复杂的测试场景构建
-
不同的性能调优方法
如果您选择自托管,还需要额外承担服务器管理、安全补丁更新,以及Redis在凌晨3点突然内存泄漏时的紧急调试。
而PostgreSQL让您通过单一服务管理所有功能。
📈 单一数据库的扩展能力
许多人尚未意识到:单个PostgreSQL实例就能够处理惊人的负载——我们说的是每日数百万事务、TB级数据存储和数千个并发连接。
真实世界的例证:
-
Airbnb:单个PostgreSQL集群处理数百万笔预订
-
Robinhood:支撑数十亿金融交易
-
GitLab:完整的DevOps平台基于PostgreSQL
PostgreSQL的架构设计极具魅力,它具备卓越的垂直扩展能力。当您最终需要水平扩展时,成熟的方案包括:
-
基于读副本的查询扩展
-
大表分区
-
连接池优化
-
逻辑复制构建分布式架构
绝大多数企业永远达不到这些扩展极限。在真正需要应对数百万用户或复杂分析工作负载之前,单个实例通常绰绰有余。
对比之下,管理多个以不同方式扩展的独立服务——Redis可能内存不足,消息队列遭遇吞吐量瓶颈,搜索服务需要完全不同的硬件配置——这种复杂度是完全可以避免的。
🛠️ 从第一天起避免过度设计
现代软件开发的最大陷阱是“宇航员架构”——我们为从未遇到过的问题、从未见过的流量规模、可能永远达不到的性能要求而过度设计系统。
典型的过度设计循环:
-
“我们某天可能需要扩展”
-
引入Redis、消息队列、微服务、多数据库
-
花费数月调试集成问题
-
向47个用户发布产品
-
每月支付200美元的基础设施费用,而这些完全可以在5美元的VPS上运行
与此同时,您的竞争对手因为不在需要分布式系统之前就管理分布式系统,而能够更快地交付产品。
🏆 更明智的技术选型策略
-
从PostgreSQL开始——抵制添加其他数据库的冲动
-
善用JSONB获得灵活性——在享受无模式开发便利的同时,保留SQL的强大能力
-
在PostgreSQL内实现队列——节省成本并降低复杂度
-
仅在达到实际瓶颈时引入专用工具——而非想象中的瓶颈
📝 实践经验分享
UserJot的开发过程成为这一理念的完美验证。作为一个反馈和路线图工具,它需要:
-
提交反馈时的实时更新
-
针对数千个功能请求的全文搜索
-
发送通知的后台作业
-
频繁访问数据的缓存
-
用户偏好和设置的键值存储
我的整个后端仅使用一个PostgreSQL数据库。没有Redis,没有Elasticsearch,没有消息队列。从用户认证到实时WebSocket通知,一切均由PostgreSQL处理。
结果如何?我能够更快地交付功能,需要调试的组件更少,基础设施成本降至最低。当用户提交反馈、搜索功能或获取路线图变更的实时更新时,所有操作都由PostgreSQL高效完成。
这不再是理论探讨,而是经过真实用户和数据验证的生产实践。
💡 核心洞察
PostgreSQL的强大之处在于它能同时担任您的主数据库、缓存、队列、搜索引擎和实时系统——同时在所有层面保持ACID事务一致性:
sql
-- 单一事务,多重操作
BEGIN;
INSERT INTO users (email) VALUES ('user@example.com');
INSERT INTO job_queue (job_type, payload)
VALUES ('send_welcome_email', '{"user_id": 123}');
UPDATE kv_store SET value = '{"last_signup": "2024-01-15"}'
WHERE key = 'stats';
COMMIT;
尝试在Redis、RabbitMQ和Elasticsearch上实现相同的原子性操作,您将深刻体会到其中的差异。
🎯 务实的技术选择
PostgreSQL可能不够“炫酷”,它没有华丽的官网,也不会在TikTok上病毒式传播。但在其他数据库兴起又衰落的几十年间,它始终默默地支撑着互联网的发展。
选择简单、可靠且高效的技术,本身就是一种智慧。
行动建议
当下次有人建议添加Redis来“提升性能”或引入MongoDB来“增加灵活性”时,不妨反问:“您是否真的尝试过在PostgreSQL中实现这个需求?”
答案可能会让您惊讶。就我个人而言,基于PostgreSQL构建UserJot的体验证明:简单而专注的技术选择,往往能带来最稳健的产品基础。
更多推荐



所有评论(0)