You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails 按日期范围统计记录数的性能优化方案咨询

核心性能瓶颈

现有代码的性能瓶颈来自循环产生的大量重复SQL查询:假设按1小时间隔统计1个月的数据,就会产生720次独立SQL查询,每次查询都需要扫描表、关联父表,重复开销被放大,数据量较大时必然触发超时。

最优重构方案

1. 替换循环查询为2次分组聚合查询

核心思路是先限定总时间范围,直接在数据库层完成分组统计,仅需2次查询即可拿到所有区间的计数结果,再遍历时间区间补全空值即可,代码示例(以MySQL为例,PostgreSQL仅需替换时间戳提取函数为EXTRACT(EPOCH FROM 字段名)):

# 1. 单次查询统计所有区间的created_at计数
created_counts = recs.actions_dated('created_at', period_start, period_end)
  .group("FLOOR(UNIX_TIMESTAMP(created_at) / #{period_offset.to_i}) * #{period_offset.to_i}")
  .count
# 把时间戳键转为Time类型
created_counts.transform_keys! { |ts| Time.at(ts) }

# 2. 单次查询统计所有区间的confirmed_at计数
confirmed_counts = recs.confirmed.actions_dated('confirmed_at', period_start, period_end)
  .group("FLOOR(UNIX_TIMESTAMP(confirmed_at) / #{period_offset.to_i}) * #{period_offset.to_i}")
  .count
confirmed_counts.transform_keys! { |ts| Time.at(ts) }

# 3. 遍历时间区间生成最终结果数组,无数据的区间补0
graph_data_created = []
graph_data_confirmed = []
temp_period_start = period_start
while temp_period_start < period_end
  graph_data_created << created_counts.fetch(temp_period_start, 0)
  graph_data_confirmed << confirmed_counts.fetch(temp_period_start, 0)
  temp_period_start += period_offset
end

2. 新增必要索引加速查询

没有对应索引的情况下每次查询都会走全表扫描,需新增以下索引,可把单查询速度提升10~100倍:

# 数据库迁移代码
class AddChartQueryIndexes < ActiveRecord::Migration[6.1]
  def change
    add_index :recs, :created_at
    add_index :recs, :confirmed_at
    add_index :recs, :rec_parent_id # 优化joins关联速度,未加过该索引时需要加
    add_index :rec_parents, :start_flag
  end
end

3. 高阶优化(可选,适合百万级以上数据量)

给recs表新增冗余布尔字段is_confirmed,在rec保存或关联的rec_parent更新时自动同步该字段的值,即可去掉confirmed scope中的joins关联,进一步降低查询开销。


内容的提问来源于stack exchange,提问作者hrldrm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 19:30:03