开发者

Help me optimize an ActiveRecord object with too many attributes

开发者 https://www.devze.com 2023-04-02 01:15 出处:网络
I\'m working on a app which ties to a legacy database.The primary model is based on a stupidly large 100+ column table.I don\'t know too much about the inner-workings of ActiveRecord but it seems to m

I'm working on a app which ties to a legacy database. The primary model is based on a stupidly large 100+ column table. I don't know too much about the inner-workings of ActiveRecord but it seems to me that any request on this model is slowing down because it's creating objects with 100+ attributes. Let's call this SlowModel.

Rendering pages with this model sometimes take 17 seconds on my dev computer. Straight up mysql queries only take ~ 0.5 - 1 second.

I've managed to speed up one portion of the app by using a MySQL view that selects a subset of fields (20 or so). We'll 开发者_如何学运维call this QuickModel. Using views is OK but isn't the most portable solution.

I will likely continue to try and add this QuickModel into other parts of the site but I was wondering if anyone had other ideas in speeding up the original object. For instance, is there a way to specify in the model what columns activerecord should just ignore and avoid building? Maybe there are specific column types (:text??) that cause bloat in ActiveRecord objects.

Assume that columns have proper indices.


You can specify which columns are returned in the model lookup using the :select option of the ActiveRecord lookup:

SlowModel.all(:select => 'id, col1, col2, col3')

...will load instances of SlowModel with only the specified columns populated.


How about having a completely new QuickModel that sits to its own table... and a QuickModel has_one SlowModel?

You can use SQL to move the most-necessary data into the QuickModel table and only refer to the SlowModel using my_quick_model.slow_model when necessary.

Alternatively, you can add a "select" to the default scope (you can google "rails default scope" for more). By default it'll only fetch the reduced set - but you can ask for all attributes by passing :select => "*" if necessary.


Along the lines of what Winfield is saying, you may want to take a look at using an attribute tracker like SlimScrooge. The tracker attempts to fetch only the data that you're using, which reduces overhead. It attempts to automatically do what Winfield is suggesting.

Example from the Readme:

# 1st request, sql is unchanged but columns accesses are recorded
Brochure Load SlimScrooged 1st time (27.1ms)   SELECT * FROM `brochures` WHERE (expires_at IS NULL)

# 2nd request, only fetch columns that were used the first time
Brochure Load SlimScrooged (4.5ms)   SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.id FROM `brochures` WHERE (expires_at IS NULL)

# 2nd request, later in code we need another column which causes a reload of all remaining columns
Brochure Reload SlimScrooged (0.6ms) `brochures`.name,`brochures`.comment,`brochures`.image_height,`brochures`.id, `brochures`.tel,`brochures`.long_comment,`brochures`.image_name,`brochures`.image_width FROM `brochures` WHERE `brochures`.id IN ('5646','5476','4562','3456','4567','7355')

# 3rd request
Brochure Load SlimScrooged (4.5ms)   SELECT `brochures`.expires_at,`brochures`.operator_id,`brochures`.name, `brochures`.id FROM `brochures` WHERE (expires_at IS NULL)
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号