你的 SQL 很快,但 API 很慢
你的数据库查询很快。你的缓存工作正常。你的后台任务也没问题。
然而,你的 API 仍然很慢。你的 CPU 使用率很高。
问题不在于数据库,而在于 Ruby 层。
瓶颈通常发生在数据离开数据库并进入应用程序之后。这主要由三个问题引起:
- 臃肿的序列化
- 过度的对象分配
- 重复计算
以下是解决方法。
- 停止臃肿的序列化
许多开发者会将整个模型转换为 JSON。
render json: @shipments
如果一个货运(shipment)有 40 个字段,但你的前端只需要 5 个,你就在浪费 CPU 周期。你还有泄露 API 密钥或成本等私密数据的风险。
解决方法:仅返回你需要的字段。
render json: @shipments.as_json(only: [:id, :tracking_no, :status])
为了获得更快的速度,请使用 pluck 以数组形式获取数据。这可以完全避免构建沉重的 ActiveRecord 对象。
- 减少对象分配
Ruby 创建的每个对象都会消耗内存。在单个请求期间创建数千个对象会迫使垃圾回收器 (GC) 加倍工作。这会拖慢你的整个系统。
避免在循环内部构建新的 hash 或 string。
错误: @shipments.map do |s| { label: "#{s.tracking_no} - #{s.status.upcase}" } end
正确: 将静态数据移到循环之外。在数据库中完成更多工作,而不是在 Ruby 中。
- 避免重复计算
如果你在一个请求中多次调用同一个方法,你就是在浪费时间。
示例: def total_weight shipments.sum(&:weight) end
如果你的 view、helper 和 serializer 都调用了这个方法,你就会计算三次总和。
解决方法:使用记忆化 (memoization)。
def total_weight @total_weight ||= shipments.sum(&:weight) end
这确保了计算在每个请求中只发生一次。
摘要表:
- 臃肿的序列化:仅返回需要的字段或使用
pluck。 - 高分配:在循环中构建更少的对象。
- 重复计算:使用记忆化来复用结果。
数据库优化意味着请求更少的数据。应用程序优化意味着在获取数据后减少琐碎的工作。
Source: https://dev.to/danewu/your-sql-is-fast-but-the-api-is-slow-its-the-ruby-layer-2fno
