找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2843

积分

0

好友

366

主题
发表于 9 小时前 | 查看: 2| 回复: 0

上篇文章《PostgreSQL 18 vs Redis:那天我发现缓存层可以删了》发布后,评论区出现了不少质疑声。比如:“这不就是纸上谈兵吗?数据呢?”、“你那点测试能代表什么?生产环境试试?”、“Redis 0.1ms 的响应你用 PG 替代?开玩笑呢?”。

说实话,看到这些评论我还挺高兴的。质疑嘛,说白了就是要证据。你们要数据是吧?行,这次管够。

先回顾一下上次说了啥

上篇文章的核心观点很简单:PostgreSQL 18 引入了异步 IO(io_uring)和 B 树跳跃扫描(Skip Scan)之后,很多原本必须靠 Redis 缓存才能达到的查询速度,现在数据库自己就能搞定。数据库性能的提升幅度,大到可以让你重新审视整个缓存架构。

当时给的数据是我们内部的测试结果,确实比较粗。今天这篇,把自己的、别人的、学术论文的数据全摆出来。看看异步IO和跳跃扫描到底能把数据库性能拉到什么程度。

第一组数据:真实生产环境,1K 并发

这组数据来自一个真实的 SaaS 后端系统,不是那种跑个 SELECT 1 就宣布“性能翻倍”的玩具测试。

测试条件:

  • 1000 个并发用户
  • 90% 读操作,10% 写操作
  • 数据量:1500 万订单,200 万客户
  • 查询类型:客户仪表盘(用户画像 + 近期订单 + 账户状态)

先看查询长啥样:

SELECT o.id, o.total_amount, o.status, o.created_at
FROM orders o
WHERE o.customer_id = $1
AND o.status IN ('PAID', 'SHIPPED')
ORDER BY o.created_at DESC
LIMIT 20;

没啥花哨的,就是按客户查订单,按时间倒序,取最近 20 条。这种查询在电商系统里一天跑个几百万次太正常了。

测试结果:

方案 P95 延迟 缓存命中率 备注
Redis + PostgreSQL 72ms 91% 偶发缓存风暴
PostgreSQL 18(直连) 54ms -- 无外部缓存

你没看错。去掉 Redis 之后,P95 延迟反而降了 25%。

“等等,Redis 不是 0.1ms 吗?怎么加了 Redis 反而更慢?”

好问题。这就是很多人对缓存的最大误解 -- 你看到的 0.1ms 是命中的情况,但缓存不是 100% 命中的。

91% 的命中率听着不错对吧?但剩下那 9% 没命中的时候会发生什么?请求先跑一趟 Redis 发现没有,再跑一趟 PostgreSQL 拿数据,拿完了还得写回 Redis。这一套下来,比直接查数据库还慢。

更要命的是那个“偶发缓存风暴”。热门数据的 TTL 同时过期,几百个请求同时穿透到数据库,Redis 和 PostgreSQL 双双飙高。这种事情在促销活动的时候特别容易发生。

而 PostgreSQL 18 直连呢?每次请求就走一条路,查完就完了。没有命中率的赌博,没有额外的网络跳转。路径简单了,反而更快。

第二组数据:冷热缓存对比

上篇文章里提过的数据,这次完整展开。

同一条查询(用户近 30 天活动聚合),不同 PostgreSQL 版本的表现:

SELECT date, sum(events)
FROM activity
WHERE user_id = $1
AND date > now() - interval '30 days'
GROUP BY date;
场景 PostgreSQL 17 PostgreSQL 18
冷缓存(数据不在内存) 12-18ms 6-8ms
热缓存(数据在内存) 4-6ms 1.8-2.5ms

PostgreSQL 18 的热缓存查询 2ms 左右。

现在算一下 Redis 的真实延迟。不是那个理想化的 0.1ms,是加权平均:

  • 命中(73%):0.1ms + 0.3ms(序列化)+ 0.8ms(网络)= 1.2ms
  • 未命中(27%):1.2ms + 15ms(查数据库)+ 1.5ms(写回缓存)= 17.7ms
  • 加权平均:0.73 x 1.2 + 0.27 x 17.7 = 5.7ms

Redis 的加权平均延迟 5.7ms,PostgreSQL 18 热缓存 2ms。谁快?

“但我们的缓存命中率能到 95% 甚至 99% 啊!”

如果你真能稳定做到 99% 的命中率,那确实,Redis 在延迟上还有优势。但你得问问自己:维护 99% 命中率的代价是什么?预热策略、失效逻辑、双写一致性、TTL 调优......这些工程成本算进去了吗?

第三组数据:第三方基准测试

不信我的数据?没关系,看看其他人怎么说。

pganalyze 的测试(2025 年 5 月)

pganalyze 是 PostgreSQL 圈子里比较权威的监控服务商。PostgreSQL 18 Beta 那会儿他们就做了详细的异步 I/O 基准测试,结论是:io_uring 是在 Postgres 18 中最大化 I/O 性能的推荐设置。

PlanetScale 的对比测试

PlanetScale(就那个做 MySQL 托管的)也跑了 PostgreSQL 17 vs 18 的对比。有意思的是:

  • 顺序扫描和位图堆扫描:io_uring 明显更快
  • 索引扫描:io_uring 暂时还没覆盖,改进有限
  • 整体结论:读性能通过异步 I/O 得到了显著提升

一亿行数据的暴力测试

有人在 1 亿行数据上跑了一个 SELECT count(*) FROM test_data,就这么粗暴:

IO 方法 耗时
worker(默认) ~24,791ms
io_uring ~7,237ms

3.4 倍的速度差距。一亿行全表扫描,从 25 秒干到 7 秒。

学术论文数据(PVLDB 2026)

连学术界都跟进了。PVLDB(数据库顶会)上有篇论文专门研究 PostgreSQL 的 io_uring 集成,应用优化指南后能获得 14% 的额外性能提升。

论文也指出了当前的局限:PostgreSQL 的多进程架构限制了 io_uring 的发挥,如果改成多线程,性能还能再上一个台阶。

为什么去掉 Redis 反而更快?(数据库性能的真实飞跃)

PostgreSQL 18 vs Redis 缓存架构效率对比图

这不是 Redis 慢,是整个缓存架构有隐性成本。就像你家楼下就有超市,你却非要跑到隔壁小区的便利店买东西 -- 便利店东西确实便宜,但花在路上的时间比省下的钱多。

这些隐性成本你可能没细想过:

首先是网络往返。应用到 Redis 一个来回,典型延迟 0.5-1ms。Redis 在另一个可用区的话可能 2-3ms。缓存命不命中,这笔账都得付。

然后是序列化。把 JSON 对象塞进 Redis 要序列化,取出来要反序列化。复杂对象 0.3-0.5ms 不等。监控面板上看不到这个开销,但它实实在在地吃 CPU。

还有双路径分支的问题。代码里有两条路:命中走 A,没命中走 B。每个 API 的响应时间变成了概率分布而不是确定值。P99 延迟飘忽不定,调优起来让人想掀桌。

缓存风暴也是个大坑。热门 key 过期的瞬间,几十上百个请求同时穿透。你得写互斥锁、写预热逻辑、写降级策略。一层缓存带来三层防护代码。

最后是数据一致性。数据库更新了,缓存还是旧的。用户投诉“我刚改的信息怎么没变”,客服工单一张接一张。

PostgreSQL 18 直连把这些成本全部归零。一个数据源,一条路径,一份代码。

配置其实很简单

上篇文章提过,这里再贴一遍,因为确实就这么几行:

# postgresql.conf

# 启用 io_uring 异步 IO(需要 Linux 内核 5.1+)
io_method = 'io_uring'

# 并发读取数量
effective_io_concurrency = 200
maintenance_io_concurrency = 50

# 共享缓冲区(根据你的内存调整,建议总内存的 25%)
shared_buffers = 16GB

# 预读合并
io_combine_limit = 512kB

改完重启就生效。不用改应用代码,不用搞什么六个月的迁移项目。异步 IO 这个特性属于“配置即享受”,改几行配置就能吃到红利。

如果你的 Linux 内核版本低于 5.1,用不了 io_uring,也可以用 io_method = 'worker'。worker 模式也比 PostgreSQL 17 的同步 I/O 快不少,只是没有 io_uring 那么猛。

跳跃扫描(Skip Scan):被低估的杀手级特性

大家都盯着异步 IO 看,其实 B 树跳跃扫描对数据库性能的提升可能更直接影响业务。

举个具体例子。你有一张订单表,在 (user_id, status, created_at) 上建了复合索引。这是标准操作,没什么特别的。

现在有个查询要找所有“待处理”的订单,不按用户筛选:

SELECT * FROM orders
WHERE status = 'pending'
AND created_at > '2025-01-01'
ORDER BY created_at DESC
LIMIT 50;

在 PostgreSQL 17 里,因为 WHERE 条件里没有索引的第一列 user_id,数据库没法高效利用这个复合索引。要么全表扫描,要么走一条歪路。怎么办?加 Redis 缓存,设 60 秒 TTL。

PostgreSQL 18 直接跳过索引第一列,对每个不同的 user_id 做范围扫描。结果:

  • PostgreSQL 17:45ms
  • PostgreSQL 18:8ms

从 45ms 到 8ms,就是数据库引擎变聪明了,没那么多弯弯绕绕。

什么时候你还是需要 Redis

我不是来踩 Redis 的。上篇文章说过,这篇再说一遍 -- 以下场景 Redis 依然是更好的选择:

会话存储,需要亚毫秒级响应、零波动,用户登录状态必须瞬间返回。

限流,INCR + TTL 是 Redis 的看家本领,原子操作,天然适合。

发布订阅,跨服务的实时消息分发,Redis 的 Pub/Sub 比 PostgreSQL 的 NOTIFY 更成熟。

排行榜,ZADD + ZRANGE,有序集合就是为排名设计的。

分布式锁,SETNX 模式久经考验。

这些场景用的是 Redis 的数据结构能力,不是拿它当“PostgreSQL 的读缓存”。

“PostgreSQL 的读缓存”这个用途,在不少场景下已经不再必要了。

什么时候这招不灵

诚实说几句。以下情况别急着删 Redis:

  • P99 要求 2ms 以内:PostgreSQL 18 热缓存能做到 2ms 左右,但严格的亚毫秒级 SLA 还是得靠内存方案。
  • 单热点 key 百万级 QPS:所有请求打同一行数据,连接池都扛不住。
  • Linux 内核低于 5.1:io_uring 用不了,异步 I/O 的效果打折扣。
  • 机械硬盘:异步 I/O 能帮忙但不是魔法,SSD 是前提。
  • 云厂商限制:有些托管 PostgreSQL 服务还没开放 io_uring 配置,升级前先问你的云服务商。

一张图看明白

升级前的架构:

┌──────────────┐
│    应用程序    │
└──┬────────┬──┘
   │        │
   v        v
┌──────┐ ┌──────────┐
│Redis │ │PostgreSQL│
│缓存  │ │  存储     │
│~0.1ms│ │ ~5-15ms  │
└──┬───┘ └──────────┘
   │          ^
   └──────────┘
   缓存失效的噩梦

升级后的架构:

┌──────────────┐
│    应用程序    │
└──────┬───────┘
       │
       v
┌────────────────┐
│ PostgreSQL 18  │
│ io_uring + AIO │
│   ~2-8ms       │
└────────────────┘

少了一个组件,少了一种失败模式。半夜被叫起来排查问题的概率,也跟着降了。

给质疑者的总结

上篇文章靠逻辑推理,这篇靠数据说话。汇总一下:

指标 Redis + PG17 PG18 直连 来源
P95 延迟(1K并发) 72ms 54ms 真实生产环境
热缓存查询 4-6ms 1.8-2.5ms 内部测试
顺序扫描(1亿行) -- 快 3.4 倍 第三方基准
索引跳跃扫描 45ms 8ms 内部测试
需要维护的组件数 2 1 常识

记住这句话:缓存层的真实延迟不是命中时的延迟,是命中和未命中的加权平均。

什么意思?你不能只看 Redis 命中时的 0.1ms 就觉得它快。把没命中时的慢、序列化的开销、网络往返的损耗、缓存风暴的偶发代价全算进去,算出的那个值,在很多场景下比 PostgreSQL 18 直连还高。

下一步该做什么

  1. 测你自己的场景:别信任何人的基准测试,包括我的。把你系统里跑得最多的查询拉出来,在 PostgreSQL 18 上跑一遍。
  2. 看你的缓存命中率:低于 95% 认真考虑直连,低于 80% 说明 Redis 大概率在拖后腿。
  3. 算一笔总账:Redis 基础设施成本 + 处理缓存 bug 的时间 + 开发效率损失。这笔账算清楚了,决策就不难了。
  4. 从低风险接口开始:挑一个读多写少、对延迟要求不极端的接口,暗启动测试。看图表说话。

你的系统里,哪个接口最适合第一个去掉缓存层?

参考资料:

  • pganalyze: Accelerating Disk Reads with Asynchronous I/O
  • PlanetScale: Benchmarking Postgres 17 vs 18
  • Phoronix: PostgreSQL 18.0 Released
  • CYBERTEC: PostgreSQL 18 Better I/O Performance with AIO
  • PVLDB 2026: io_uring for High-Performance DBMSs
  • Neon: PostgreSQL 18 Asynchronous I/O

更多类似的后端架构与数据库性能优化讨论,欢迎访问 云栈社区 进行交流。




上一篇:Python @cache 装饰器:避免重复计算,提升性能百倍的使用场景与原理解析
下一篇:使用Python Remi库快速构建可浏览器访问的Web GUI应用
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-10 18:05 , Processed in 0.386258 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表