Skip to main content

Mastering Django Part 1: How to Use Memcache and DatabaseCache for Speed

ยท 8 min read
Sudip Parajuli
Full Stack Django Developer | Data Science | IoT and Robotics

Introductionโ€‹

If you are absolute beginner to this concept of caching then it is okay that caching sounds super complex and fancy. Things are complex inside the caching i am not disagreeing with that sentiment either. But let me simplify some things for you why you need caching and how you can implement some common type of caches instantly to learn concepts.

Representative Image

Fibonacci and Technique of Memoizationโ€‹

We were all in the data structures and algorithms class and our professor Er. Pukar Karki sir told us to calculate Fibonacci of arbitrary long number by using recursion and we were all doing our work like writing in a way as most of us will do on the first time.

def fibonacci_without_memo(n):
if n <= 1:
return n
return fibonacci_without_memo(n - 1) + fibonacci_without_memo(n - 2)

This was my code, yeah it's not the exact one i wrote there but these is somewhat accurate representation of my code. When i try to calculate the Fibonacci of a large number then it starts to take time. Then the professor told us that "Have you heard of the word Memoization?" and we all are like what??? Then he wrote the exact code but with help of some dictionary.

def fibonacci_with_memo(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci_with_memo(n - 1, memo) + fibonacci_with_memo(n - 2, memo)
return memo[n]

and then he showed us the code and run it it run too much faster because it's saving the repeated operations in the Fibonacci sequence.

                   fibonacci(5)
/ \
fibonacci(4) fibonacci(3)
/ \ / \
fibonacci(3) fibonacci(2) fibonacci(2) fibonacci(1)
/ \ / \ / \
fibonacci(2) fibonacci(1) fibonacci(1) fibonacci(0)
/ \
fibonacci(1) fibonacci(0)
...

Since the recursion works by calling the function with itself until the certain condition is fulfilled which is called as the base condition. It was my first encounter with the optimization in the code that seems too less but too much powerful if used correctly.

What is Caching?โ€‹

Caching is a technique used in computing to temporarily store copies of frequently accessed data in a high-speed storage layer. This allows future requests for the same data to be served much faster than retrieving it from its original source, such as a database or a remote server.

Typical Scenario before using caching

Caching isn't necessary for every request; it's most useful for data that doesn't change often or for frequently accessed data that benefits from temporary storage for faster retrieval. For data that is updated frequently, caching can still be helpful if the cache is updated alongside the data, allowing it to act as a temporary, semi-persistent storage layer.

General Flow of Caching in Database

Types Of Cacheโ€‹

So, you may be wondering what are the types of caches then and when to use them. hold on.. we will be going to that soon. Let's build some proper concept first. Most of us are always in hurry in the implementation but we don't want to listen the theory behind that. It's okay if you need to deliver something super fast but it's always a good thing to read proper documentation or instructions or theory before jumping into implementation. There are many types of the caches but i would like to narrow down that to mainly 3 types.

1. In-Memory Cacheโ€‹

โ€ข Stores frequently accessed data directly in RAM, allowing for extremely fast retrieval times. โ€ข Examples: Redis, Memcached. โ€ข Use Cases: Ideal for high-speed access needs, like session data, user profiles, and other frequently accessed data in web applications. In-memory caches are commonly used to reduce database load by caching query results or computed values.

2. Persistent Cacheโ€‹

โ€ข Stores data on disk for larger capacity, allowing caching of more data than memory-only solutions at a slightly slower access speed. โ€ข Examples: Browser cache, operating system disk cache, CDN cache. โ€ข Use Cases: Suitable for storing larger datasets that don't fit into memory, such as images, videos, or downloaded files. Persistent caching is essential for improving response times in applications with large static assets or content delivery across regions.

3. Distributed Cacheโ€‹

โ€ข Shared cache used across multiple servers, enabling data consistency across distributed systems. โ€ข Examples: Redis in a clustered mode, AWS ElastiCache, Hazelcast. โ€ข Use Cases: Perfect for scalable, distributed applications where multiple servers or services need access to shared data. Used extensively in microservices architectures and large-scale web applications to ensure consistency and minimize backend load.

What is Caching in Django?โ€‹

Django provides a caching framework that allows you to store and retrieve data quickly to enhance the performance of your application. When used correctly, caching can significantly reduce the load on your database and speed up response times. Django supports multiple caching backends, including Memcache and DatabaseCache.

1. Memcache in Djangoโ€‹

Memcache is a distributed memory caching system that stores data in RAM. It's extremely fast and is used for caching data that is frequently accessed. Memcache is suitable for scenarios where data changes infrequently, and you want to retrieve it quickly.

Setting Up Memcache in Djangoโ€‹

To use Memcache in Django, you'll need to install python-memcached or pylibmc. Here's how to set up Memcache for Django:

  1. Install using pip:
pip install python-memcached
  1. Add these to settings.py:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211', # Memcache server location
}
}
  1. Using Memcache in Views:

Use cache.set('key', value, timeout) to store data in the cache. you can use the following code to get that after that:

from django.core.cache import cache

def my_view(request):
data = cache.get('some_key')
if not data:
data = expensive_database_query()
cache.set('some_key', data, timeout=60*15) # Cache data for 15 minutes
return render(request, 'my_template.html', {'data': data})

2. DatabaseCache in Djangoโ€‹

Django also provides a DatabaseCache backend that stores cached data in your database. This is useful when you don't have a dedicated caching server like Memcache or Redis and want to persist cached data in your database.

Setting Up DatabaseCache in Djangoโ€‹

  1. Configure DatabaseCache in settings.py:

In your Django settings, you can configure the DatabaseCache as follows:

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'django_cache_table', # Name of the cache table
}
}
  1. Create the Cache Table:
python manage.py createcachetable
  1. Using DatabaseCache in Views:

Setting cache is same as memcache:

from django.core.cache import cache

def my_view(request):
data = cache.get('some_key')
if not data:
data = expensive_database_query()
cache.set('some_key', data, timeout=60*60*24) # Cache data for 1 day
return render(request, 'my_template.html', {'data': data})

Advanced Caching Strategiesโ€‹

Cache Invalidationโ€‹

One of the most challenging aspects of caching is knowing when to invalidate (clear) the cache. Here are some strategies:

from django.core.cache import cache
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from myapp.models import MyModel

@receiver(post_save, sender=MyModel)
@receiver(post_delete, sender=MyModel)
def invalidate_cache(sender, **kwargs):
cache.delete('expensive_query_result')

Cache Decoratorsโ€‹

Django provides convenient decorators for caching:

from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator

@cache_page(60 * 15) # Cache for 15 minutes
def my_view(request):
return render(request, 'my_template.html')

# For class-based views
@method_decorator(cache_page(60 * 15), name='dispatch')
class MyView(View):
def get(self, request):
return render(request, 'my_template.html')

Template Fragment Cachingโ€‹

You can also cache specific parts of templates:

{% load cache %}
{% cache 500 sidebar %}
<!-- expensive template code -->
{% endcache %}

Best Practicesโ€‹

  1. Choose the Right Cache Backend: Use Memcache/Redis for high-performance scenarios, DatabaseCache for simpler setups.

  2. Set Appropriate Timeouts: Don't cache data indefinitely. Set reasonable expiration times.

  3. Cache at the Right Level: Consider caching at different levels - database queries, template fragments, or entire pages.

  4. Monitor Cache Performance: Use tools to monitor cache hit rates and performance.

  5. Handle Cache Misses Gracefully: Always have fallback logic when cache data is not available.

Conclusionโ€‹

Caching is an essential technique for improving the performance of web applications. With Django's built-in caching framework, implementing caching using Memcache or DatabaseCache is straightforward. Memcache offers high speed and scalability, while DatabaseCache provides persistence and simplicity for smaller setups.

By incorporating these caching strategies into your Django applications, you can significantly reduce database load, improve response times, and enhance the overall user experience. Make sure to choose the appropriate caching strategy based on your application's needs.

Remember that caching is not a silver bullet - it's important to profile your application to identify bottlenecks and implement caching strategically where it will have the most impact.