简体   繁体   中英

Querying cached records in Rails seems slow

I'm doing a query based suggestion API in Rails, with suggestions being returned to the user as they type. In order to avoid hitting the database too often, I decided to cache the records.

def cached_values
  Rails.cache.fetch(:values_cached, expires_in: 1.day) do
    Table.verified.select(:value).all
  end
end

cached_values
=>
[#<Table:0x000056406fc70370 id: nil, value: "xxx">,
 #<Table:0x000056406fc77f80 id: nil, value: "xxx">,
 #<Table:0x000056406fc77d00 id: nil, value: "xxx">

 ...

I'm aware it's not a good practice to cache ActiveRecord entries, but the "verified" scope is relatively small (~6k rows) and I want to query it further. So when a call to the API is made, I query the cached values (simplified, the real one is sanitized):

def query_cached(query)
  cached_values.where("value LIKE '%#{query}%'").to_a
end

The issue here is that I have tested both cached and uncached queries, and the later has better performance. Setting Rails.logger.level = 0 , I noticed the cached query still logs a database query:

pry(main)> query_cached("a")
  Table Load (1.2ms)  SELECT "table"."value" FROM "table" WHERE "table"."verified" = TRUE AND (value LIKE '%a%')

My guess is that the cached search is both opening a connection to the database and loading the cached records, taking more time but being effectively useless. Is there any reliable way to check that? If the cache is just slower, maybe it is still worth keeping it and preventing too many connections to the database.

Benchmark for 10000 queries each:

       user     system      total        real
uncached 20.110681   0.369983  20.480664 ( 26.935934)
cached 23.750934   0.753414  24.504348 ( 34.198694)

It is important to note that all doesn't return all records in an array. Instead it returns an ActiveRecord::Relation object. Such a relation represents a database query that might be called later or that can be extended by more conditions like, for example, .where("value LIKE '%#{query}%'") . If all already returned an array of records then you would not be able to add the additional condition with .where("value LIKE '%#{query}%'") to it because where doesn't exist on arrays.

Because you only cached the Relation that represents a database query that can be run by calling a method that needs the actual records (like each , to_a , first ) but hasn't run yet, the caching is useless in this case.

Additionally, I would argue that caching is not useful in the context of this example at all because you would need to cache different values for each different user input. That means if the user searched for foo then you can cache that result but if another user then searches for bar you would still need to run another query to the database. Only if two users search for the same string the cache might be useful.

In your example, a full-text index in the database might be the better choice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM