Skip to main content

Django Cache Backends

Introduction​

Caching is a crucial technique for improving the performance of Django applications by storing the results of expensive operations and serving them quickly on subsequent requests. Django provides a robust caching framework with support for multiple cache backends, each with its own strengths and use cases.

In this tutorial, we'll explore the different cache backends available in Django, how to configure them, and when to use each one. By the end, you'll have a solid understanding of how to implement caching effectively in your Django projects.

Understanding Cache Backends​

A cache backend is essentially a storage system where Django stores cached data. Django supports several cache backends out of the box, and you can also use third-party backends or create custom ones. The choice of backend depends on your specific requirements, infrastructure, and performance needs.

Available Cache Backends in Django​

Django offers several built-in cache backends:

  1. Memory Cache
  2. File-based Cache
  3. Database Cache
  4. Memcached
  5. Dummy Cache
  6. Local Memory Cache

Let's explore each of these in detail.

1. Memory Cache​

The memory cache backend stores cached data in your application's memory. It's the fastest option but is cleared when the server restarts.

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}

The LOCATION parameter is used to identify a specific cache instance. This is useful when you want to have multiple separate caches.

When to use​

The memory cache is excellent for development environments and testing, but it's not recommended for production unless you have a single-server setup. Since the cache is local to each server process, it won't be shared across multiple servers or processes.

2. File-based Cache​

The file-based cache stores each cache value in a separate file in a specified directory on the filesystem.

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}

Make sure the directory specified in LOCATION exists and is writable by the server.

When to use​

File-based caching is suitable for development and production environments where you don't have access to more advanced caching systems. It's also useful for caching large items, as the filesystem is usually larger than memory.

3. Database Cache​

The database cache stores cached data in a database table. Django will automatically create the necessary table the first time you run python manage.py createcachetable.

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}

After adding this configuration, you must create the cache table:

bash
python manage.py createcachetable

When to use​

The database cache is useful when you already have a database set up and don't want to set up another service. However, it's generally slower than memory-based caching options, as it requires database access for each cache operation.

4. Memcached​

Memcached is a high-performance, distributed memory caching system. Django supports it out of the box and it's often the best choice for production environments.

Installation​

First, you'll need to install Memcached itself and a Python Memcached client. For the client, you can use either python-memcached or pylibmc:

bash
pip install python-memcached
# OR
pip install pylibmc

Configuration​

python
# Using python-memcached
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}

# OR using multiple Memcached servers
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}

When to use​

Memcached is an excellent choice for production environments, especially those with multiple web servers. It's fast, reliable, and specifically designed for caching. Use Memcached when:

  • You have multiple web servers
  • You need to share cache data across servers
  • You require high performance

5. Dummy Cache​

The dummy cache doesn't actually cache anything; it just implements the cache interface without doing anything.

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}

When to use​

The dummy cache is useful during development when you want to disable caching temporarily without changing your code. It's also useful in testing environments where you want to ensure that tests don't rely on cached data.

6. Local Memory Cache​

This is Django's default cache backend when you don't specify one. It's similar to the memory cache but works on a per-process basis.

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}

When to use​

Local memory cache is useful for development and small single-server deployments, but it doesn't share data between different processes.

Third-Party Cache Backends​

Besides the built-in backends, there are several third-party backends available for Django:

Redis Cache​

Redis is a popular in-memory data store that can be used as a cache backend. It's similar to Memcached but offers more features like data persistence and complex data types.

Installation​

bash
pip install django-redis

Configuration​

python
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}

When to use​

Redis is a great choice when you need more features than Memcached offers, such as:

  • Data persistence (Redis can save data to disk)
  • Support for complex data types (lists, sets, sorted sets, etc.)
  • Pub/sub messaging capabilities
  • Built-in support for data expiration

Common Cache Settings​

All cache backends support these common settings:

TIMEOUT​

The default timeout (in seconds) for cached items. A value of None means the cache never expires.

python
CACHES = {
'default': {
'BACKEND': '...',
'TIMEOUT': 600, # 10 minutes
}
}

KEY_PREFIX​

A string that will be automatically included in all cache keys:

python
CACHES = {
'default': {
'BACKEND': '...',
'KEY_PREFIX': 'mysite',
}
}

VERSION​

The default version number for cache keys:

python
CACHES = {
'default': {
'BACKEND': '...',
'VERSION': 1,
}
}

KEY_FUNCTION​

A dotted path to a function that defines how to compose cache keys:

python
def make_key(key, key_prefix, version):
return f'{key_prefix}:{version}:{key}'

CACHES = {
'default': {
'BACKEND': '...',
'KEY_FUNCTION': 'path.to.make_key',
}
}

Setting Up Multiple Cache Backends​

Django allows you to configure multiple cache backends at once:

python
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
'TIMEOUT': 300,
},
'filesystem': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 3600,
}
}

To use a specific cache backend:

python
from django.core.cache import caches

default_cache = caches['default']
filesystem_cache = caches['filesystem']

# Use specific cache
default_cache.set('my_key', 'my_value', timeout=60)
value = filesystem_cache.get('another_key')

Practical Example: Setting Up a Custom Cache Backend Chain​

Let's look at a practical example where we use multiple cache backends in a chain, starting with the fastest and falling back to slower ones:

python
# cache.py
from django.core.cache.backends.base import BaseCache
from django.core.cache import caches

class CacheChainBackend(BaseCache):
def __init__(self, params):
super().__init__(params)
self.cache_backends = params.get('BACKENDS', ['default'])

def get(self, key, default=None, version=None):
# Try to get from each cache in order
for backend_name in self.cache_backends:
backend = caches[backend_name]
value = backend.get(key, None, version)
if value is not None:
# Found in this cache, populate the higher caches
self._populate_higher_caches(key, value, version, backend_name)
return value
return default

def set(self, key, value, timeout=None, version=None):
# Set in all caches
for backend_name in self.cache_backends:
backend = caches[backend_name]
backend.set(key, value, timeout, version)

def _populate_higher_caches(self, key, value, version, found_in):
# Populate higher (faster) caches with the value found in a lower cache
found_index = self.cache_backends.index(found_in)
for i in range(found_index):
backend = caches[self.cache_backends[i]]
backend.set(key, value, self.default_timeout, version)

Then in your settings:

python
CACHES = {
'memory': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
},
'redis': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
},
'filesystem': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
},
'default': {
'BACKEND': 'path.to.cache.CacheChainBackend',
'BACKENDS': ['memory', 'redis', 'filesystem'],
}
}

This setup will first check the memory cache, then Redis, then the filesystem cache. If it finds the value in a slower cache, it will populate the faster caches with that value for future requests.

Best Practices for Choosing and Using Cache Backends​

  1. Match the backend to your needs: For development, local memory cache is often sufficient. For production with multiple servers, use Memcached or Redis.

  2. Consider persistence needs: If you need cached data to survive restarts, consider Redis or a file-based cache.

  3. Think about cache size: Memory-based caches are limited by available RAM. For large cache sizes, consider file-based or database caches.

  4. Monitor cache usage: Keep track of your cache hit rate and adjust your strategy accordingly.

  5. Set appropriate timeouts: Cache items should expire based on how frequently the underlying data changes.

  6. Be careful with per-user caching: When caching per-user data, ensure keys include user identifiers and consider privacy implications.

  7. Plan for cache failures: Your application should still work (albeit slower) if the cache system fails.

Summary​

Django's cache framework is flexible and supports various backends to suit different needs:

  • Memory-based caches (Memory Cache, Memcached, Redis) offer the best performance but are limited by available RAM.
  • File-based caches provide persistence at the cost of slower access times.
  • Database caches use your existing database infrastructure but add extra load to your database.
  • Dummy caches are useful for development when you want to disable caching.

The choice of backend depends on your specific requirements, infrastructure, and performance needs. By understanding the characteristics of each backend, you can make an informed decision that balances performance, reliability, and ease of maintenance.

Additional Resources​

  1. Django's Official Caching Documentation
  2. Memcached Official Website
  3. Redis Official Website
  4. django-redis Documentation

Exercises​

  1. Set up a file-based cache in a Django project and implement caching for a view that fetches data from an external API.

  2. Configure a Memcached backend and compare its performance with the file-based cache.

  3. Create a custom middleware that caches pages for anonymous users using the cache backend of your choice.

  4. Implement a cache backend chain like the example above, but log each cache hit and miss to see how data moves between caches.

  5. Research and implement one third-party cache backend not mentioned in this tutorial, such as MongoDB or DynamoDB.



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)