上篇文章《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 反而更快?(数据库性能的真实飞跃)

这不是 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 直连还高。
下一步该做什么
- 测你自己的场景:别信任何人的基准测试,包括我的。把你系统里跑得最多的查询拉出来,在 PostgreSQL 18 上跑一遍。
- 看你的缓存命中率:低于 95% 认真考虑直连,低于 80% 说明 Redis 大概率在拖后腿。
- 算一笔总账:Redis 基础设施成本 + 处理缓存 bug 的时间 + 开发效率损失。这笔账算清楚了,决策就不难了。
- 从低风险接口开始:挑一个读多写少、对延迟要求不极端的接口,暗启动测试。看图表说话。
你的系统里,哪个接口最适合第一个去掉缓存层?
参考资料:
- 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
更多类似的后端架构与数据库性能优化讨论,欢迎访问 云栈社区 进行交流。