我在ruby 2.6.3 上使用轨道4.2.11.1
我使用rails的请求非常慢,所以我对代码进行了基准测试,找到了罪魁祸首。大部分减速发生在数据库调用中,我从数据库中的表中选择一行。我已经尝试了几个不同版本的相同想法
使用此版本
Rails.logger.info Benchmark.measure{
result = Record.find_by_sql(['SELECT column FROM table WHERE condition']).first.column
}
rails的输出显示sql需要54.5ms,但基准测试输出0.043427 0.006294 0.049721(1.795859(,总请求需要1.81秒。当我直接在postgres终端中运行上面的sql时,它需要42ms。
显然问题不在于我的sql速度慢。42毫秒并不明显。但是1.79秒的速度太慢了,造成了糟糕的用户体验。
我读了一些书,得出的结论是,速度减慢是由rails的对象创建引起的(这看起来很奇怪,但显然可能非常慢(,所以我尝试使用pull来最大限度地减少创建的对象数量:
Rails.logger.info Benchmark.measure{
result = Record.where(condition).pluck(column).first
}
现在rails说sql耗时29.3ms,基准测试给出0.017989 0.006119 0.024108(0.713973(整个请求耗时0.731秒。这是一个巨大的改进,但0.7秒的速度仍然很慢,仍然会破坏我的应用程序的可用性。
我做错了什么?如此简单的事情竟然会出现如此巨大的放缓,这在我看来简直是疯了。如果rails就是这样工作的,我无法想象有人会把它用于严肃的应用程序!
find_by_sql
对数据库执行自定义SQL查询并返回所有结果。
这意味着数据库中的所有记录都将被返回并实例化。只有这样,您才能通过对结果调用first
来从该数组中选择第一个。
当您在ActiveRecord::Relation上调用first
时,它将为您的查询添加一个限制,并只选择该限制,这就是您想要的行为。
这意味着你应该自己限制查询:
result = Record.find_by_sql(['SELECT column FROM table WHERE condition LIMIT 1']).first.column
我确信你的请求会很快,因为ruby不需要实例化所有的结果行。
正如我上面提到的,如果你只想要第一个,不确定为什么要要求所有匹配。
如果我这样做:
Rails.logger.info Benchmark.measure{
result = User.where(email: 'foo@bar.com').pluck(:email).first
}
(9.6ms) SELECT "users"."email" FROM "users" WHERE "users"."email" = $1 [["email", "foo@bar.com"]]
#<Benchmark::Tms:0x00007fc2ce4b7998 @label="", @real=0.6364280000561848, @cstime=0.00364, @cutime=0.000661, @stime=0.1469640000000001, @utime=0.1646029999999996, @total=0.3158679999999997>
Rails.logger.info Benchmark.measure{
result = User.where(email: 'foo@bar.com').limit(1).pluck(:email)
}
(1.8ms) SELECT "users"."email" FROM "users" WHERE "users"."email" = $1 LIMIT $2 [["email", "foo@bar.com.com"], ["LIMIT", 1]]
#<Benchmark::Tms:0x00007fc2ce4cd838 @label="", @real=0.004004000045824796, @cstime=0.0, @cutime=0.0, @stime=0.0005539999999997214, @utime=0.0013550000000002171, @total=0.0019089999999999385>
Rails也提供现金服务。如果再次运行查询,第二次应该会更快。你的where
状况有多复杂?这可能是其中的一部分。