S3对象存储上生产:务必做的3组读压测,避开冷读、并发、List线上坑 读密集业务的上线事故很少发生在峰值吞吐上更多发生在没人测过的角落服务重启后的第一批请求、并发上量之后的长尾、某个业务线程里的一次全量 List。官方 benchmark 和第三方跑分回答的是「这套软件在别人的数据形状上多快」回答不了「在你的桶里多快」。这篇给三组成本不高的自查测试每组都是拿自己的数据形状去问存储动作就是跑命令、看数字、对预算一个下午能全部跑完。示例全部以 RustFS 演示方法适用于 MinIO 在内的任何 S3 兼容存储。开始前先把客户端配好。下文的s3rustfs是 mc 的别名第一次用先设好后面所有命令都走它mc alias set s3rustfs http://rustfs-host:9000 YOURACCESSKEY YOURSECRETKEY三组测试的分工一图看全后面每节展开一组benchmark 替代不了自查跑分类的测试有个共同点数据形状是均匀的。对象大小固定、前缀结构平坦、并发节奏由压测工具说了算。真实的读密集负载没有这么干净小对象点读占大头前缀有局部性并发来自业务自己的节奏。跑分能证明软件路径没问题但证明不了你的前缀设计不会拖慢 List也证明不了你的对象大小不会让冷读变贵。三组自查的目的是把「跑分没问题应该也没问题」换成「这三项我拿自己的数据测过」。每组的产出都是数字加一条业务侧的预算线冷热差、并发平台点、List 耗时。数字和预算线对上了上生产才有依据。第一组冷读缓存热起来之前有多慢读路径上有缓存层的存储系统热数据的读延迟和冷数据回源的延迟可能差出量级这个差距官方通常不给数字因为它取决于你的对象大小和网络。测法很直接同一个前缀拉两遍比时间time rclone copy s3rustfs:bucket/cold-prefix-a /tmp/empty-1 --transfers 1 time rclone copy s3rustfs:bucket/cold-prefix-a /tmp/empty-2 --transfers 1一个容易踩的坑目标目录要保证不存在rclone 复制时会保留源对象的修改时间目录已有时重跑会被判定为「没变化」直接跳过传输计时测的就不再是下载而是文件比对。每轮测试前把/tmp/empty-1、/tmp/empty-2删掉重来。两个时间的差就是回源成本的真实大小。做的时候有几个容易翻车的地方。前缀要真的没读过新造一批数据这步要是偷懒拿热前缀测差值会小得让你以为自己缓存放得很大白忙一场冷数据造起来也省事本地随机生成一批不同大小的对象用 mc cp 传上去大小分布照着生产的来。单次计时会被网络抖动污染多来几轮看分布重点盯最差的那几轮。最后对照的是业务超时预算接口超时 3 秒冷读一批对象就得装进这条线里绝对值漂不漂亮反而不要紧。写目标选/tmp或内存盘这类快路径也有讲究客户端自己写盘太慢会把测量的水搅浑你以为在测回源其实卡在测试机的磁盘上。想要干净的数字要么换更快的落盘路径要么直接 /dev/null丢弃写入只留读。这组测试还顺带回答一个上线后必问的问题服务重启之后读延迟多久能回到正常水位。答案是第一遍和第二遍时间差收敛的速度自己测出来比猜靠谱。真正的压力场景是两组条件叠加缓存刚清空又来一波并发。可以简单模拟一下重启服务后立刻用--transfers 32拉一个冷前缀看错误数和最差延迟。很多线上事故的形态就是这个组合维护窗口刚结束调度系统把积压任务一起放出来缓存没热、并发先到。这个数字如果难看值得给重启后的恢复期设计一个预热或限流步骤而不是让业务来当预热器。第二组并发读加压之后看吞吐和长尾单流快说明不了并发行为。读放大之后瓶颈可能从网络挪到磁盘或 CPU位置不同表现完全不同。工具二选一快速粗测用 rclone 拉并发rclone copy s3rustfs:bucket/mixed-prefix /dev/shm/out --transfers 32 --checkers 32要更规范的数字用 warpMinIO 团队开源的 S3 基准工具get 模式只压读路径README 里的标准用法warp get --hostrustfs-host:9000 --access-keyYOURKEY --secret-keyYOURSECRET --duration3m --concurrent32 --obj.size1MiB--obj.size1MiB只是示例值压测前改成贴近业务真实的对象大小大小分布照生产的来否则测出来的并发平台点会失真。先看吞吐曲线。并发往上加吞吐跟着涨涨到某个点就平了那个位置就是这套部署的舒适并发数warp 默认并发 20--concurrent可以调。再看错误和长尾重试和错误数应该是 0p99 和均值的差距有没有越压越大长尾一旦拉开多半是哪里开始排队了。业务客户端数要是远超变平点出路是拆前缀分流量或者加节点硬顶是顶不住的。warp 跑完会在当前目录留下压缩的结果文件用warp analyze拆开看每个操作的延迟分布比盯一个总吞吐数字有用。压完单端点还可以把多个端点逗号分隔传给--host模拟客户端分散到多节点入口的行为README 里对这两种用法都有示例。注意 warp 会先上传一批对象作为测试池再开压测完记得清掉它建的桶别让基准数据混进你的生产数据。第三组大桶 List元数据路径的隐藏成本S3 的 ListObjectsV2 按页返回一页最多 1000 个对象百万级对象的桶全量扫一遍就是上千次分页请求元数据路径的成本从这里来。而这个问题在常规压测里最容易漏因为压测工具通常只压 GET 不压 List业务代码里那句「列出目录下所有文件再逐个处理」上线后才会暴露。测法time mc ls --recursive s3rustfs:bucket /dev/null time mc ls --recursive s3rustfs:bucket/2026/10/ /dev/nullwarp 也有 list 模式但它会自己造一批对象再列数据形状归它管你要测的是自己桶的形状所以主命令用 mc 计时warp 的结果当参照就好。全量列一遍的耗时是一个数按前缀收窄后再列是另一个数两个数的降幅能直接告诉你前缀设计有没有起作用。如果业务里存在「全量扫桶再过滤」的代码路径这组数字大概率会让你去改代码按前缀取数或客户端维护索引比压存储端有用得多。这组测试的真正产出其实是一份前缀设计评审。把业务里所有 List 调用列出来每个都问一句能不能用更窄的前缀能不能用更少的页。前缀设计是上传时定下的等读不动了再改就要迁移数据这是三组测试里唯一一个「测出来晚了就改不动」的项目所以哪怕前两组全绿这组也建议在上传路径定型之前跑。环境怎么搭测试才不作废测试结果可不可信基本看环境搭得对不对。数据形状要同形对象大小分布、前缀层级、数量级都照生产的样子造规模可以缩形状不能缩缩十倍之后前缀深度和大小分布都得保住一百个对象的桶测不出百万对象的 List 行为。还得想清楚单机到底能回答什么docker-compose 单机环境测的是软件路径和操作手感签名、HTTP、对象组装、元数据操作都在但多节点扩展性它答不了冷读和 List 两组的单机数据有参考价值并发那组的数字只在同拓扑下作数。客户端也要同形业务用什么 SDK 就用什么测mc 和 rclone 是测量工具替不了业务路径SDK 自己的重试和超时逻辑跟测量工具是两回事最后拿业务客户端跑一遍冒烟才算闭环。测试机的位置也别忽略和生产环境隔着一个公网或跨机房链路测出来的延迟就掺了链路成本条件允许就把测试机放进同内网段条件不允许就把链路延迟单独测出来记账上。最后把每组数字记下来存好时间戳连同当时的对象规模和并发参数一起记。升级 RustFS 版本之后把三组重跑一遍对比两组数字这就是你自己的回归基线比任何外部跑分都贴身。数字有了跟什么比三组数字都测出来了还有件事悬着跟什么比说实话别跟 benchmark 比跟业务自己的数字比。冷读那组对接口超时和批处理窗口最差轮次也要装得进去。并发那组对业务的真实客户端规模舒适并发点要盖得住它盖不住就得拆分或扩容。List 那组对业务里扫桶调用的频率和容忍度单次耗时乘以调用频率就是它在业务线程里占掉的时间份额。这三个数字产品侧都有现成的测试之前先问出来别等测完再拍脑袋定线。这些测试防的是什么回头看冷读挡的是重启后第一批请求超时并发挡的是上量之后吞吐顶不住List 挡的是藏在业务代码里的全量扫描悄悄拖慢整个服务。三组全绿再谈上线哪组不绿先查前缀设计、缓存策略和业务取数最后才轮到怀疑存储本身。顺手把三条主命令各存成一个脚本参数化前缀和并发数以后扩容、升级、换硬件都直接复跑。这套自查不给你任何性能承诺它能保证的只有一件事变更窗口里不用靠猜。