COUNT(*) operations in relational databases can get slow for large sets of data. There are already existing ways to deal with improving counting performance and techniques to avoid doing it altogether.
This gem adds caching support to the
ActiveRecord::Relation class to allow repeated count calls to be cached. Counts are cached based on the query that is to be run and cleared when records are saved or destroyed.
Cached counts works well with tables that are large and have many more selects than inserts. You will see some benefits even in tables that have regular inserts, but the gains will not be as great.
Add it to your gemfile in your Rails 3.2+ project
Cached counts uses your Rails.cache setup, for a good memcached store, look at dalli
You can use the size, length or count method on any active record model.
User.count # => # Cached count value User.size # => # Cached count value User.length # => # Cached count value
You can use scopes as well
User.where(:admin => true).count # Cached count value for this specific query
You can clear the cache at any time for a model:
You can also use the non cached count on the class or scopes.
User.count_without_caching # => Runs a database lookup User.where(:admin => true).count_without_caching # => Runs a database lookup
Usage with Rails
If you're using rails you can optionally disable the ovveriding of the
lenght methods by setting the
count_with_caching configuration value.
You can do so in your application config like so:
# config/application.rb module MyApp class Application < Rails::Application #... config.cache_counts_by_default = false #... end end
Disabling the cache by default allows you to use
MyModel.count_with_caching to return the cached count value.
Clearing the cache
The counts cache is cleared for a model after save and after destroy. If you are updating the database manually without these methods then you will need to clear the cache yourself.
User.update_all(:name => 'joe') User.clear_count_cache
1.9.3p194 :020 > User.count (0.2ms) SELECT COUNT(*) FROM "users" => 130 1.9.3p194 :021 > User.count => 130 1.9.3p194 :022 > User.last.destroy # ... => #<User id: 131 ...> 1.9.3p194 :023 > User.count (0.4ms) SELECT COUNT(*) FROM "users" => 129 1.9.3p194 :024 > User.count => 129 1.9.3p194 :036 > User.where(:admin => true).count (0.5ms) SELECT COUNT(*) FROM "users" WHERE "users"."admin" = 't' => 72 1.9.3p194 :037 > User.where(:admin => true).count => 72 1.9.3p194 :038 >
- Better testing