\n\n# Django helper\nfrom django.templatetags.static import static\n\n# static('js/app.js?v=1.2.3')\n```\n\n**Условные запросы:**\n\n```python\nimport requests\n\n# Получить ETag\nresponse = requests.head('https://api.example.com/data')\netag = response.headers.get('ETag')\n\n# Отправить условный запрос\nheaders = {'If-None-Match': etag}\nresponse = requests.get('https://api.example.com/data', headers=headers)\n\nif response.status_code == 304: # Not Modified\n print(\"Кеш всё ещё актуален\")\nelse:\n print(\"Получены новые данные\")\n```\n\n### Ключевые моменты\n\n- **HTTP заголовки** — управлять кешем браузера и CDN\n- **Cache busting** — добавить параметр к URL (t=timestamp или v=version)\n- **Отключить кеш явно** — `Cache-Control: no-cache, no-store, must-revalidate`\n- **ETag/If-None-Match** — валидировать кеш без загрузки полного ресурса\n- **Redis FLUSHDB** — очистить весь кеш приложения\n- **Функцион декораторы** — контролировать кеш на уровне Python\n- **skip_cache параметр** — давать опцию обойти кеш при необходимости\n- **CDN контроль** — понимать кеширование на разных уровнях\n\nПравильное управление кешем — баланс между производительностью и актуальностью данных.","dateCreated":"2026-03-22T20:32:24.471392","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как обойти кеширование?

2.0 Middle🔥 111 комментариев
#Python Core#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как обойти кеширование

Кеширование — критический компонент современных приложений, но иногда нужно обойти кеш для получения свежих данных, отладки или специальных операций. Разберёмся, какие техники использовать в разных контекстах.

1. Проблема и контексты кеширования

Кеш существует на нескольких уровнях:

Браузер (HTTP кеш) → CDN → Сервер (Redis/Memcached) → БД

Нужно понимать, на каком уровне находится кеш, чтобы правильно его обойти.

2. Обход HTTP кеша браузера

На стороне клиента (JavaScript):

// Способ 1: Cache busting через параметр
fetch('/api/data?t=' + Date.now())
  .then(r => r.json())
  .then(data => console.log(data));

// Способ 2: Header для отключения кеша
fetch('/api/data', {
  headers: {
    'Cache-Control': 'no-cache',
    'Pragma': 'no-cache',
  }
})

// Способ 3: Версионирование URL
fetch('/api/data?v=2.1.0')

// Способ 4: POST вместо GET (POST обычно не кешируется)
fetch('/api/data', { method: 'POST' })

На стороне сервера (Python/Flask/Django):

from flask import make_response
from datetime import datetime

@app.route('/api/data')
def get_data():
    response = make_response({'data': 'fresh data'})
    
    # Способ 1: Явно отключить кеш
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    
    # Способ 2: Добавить Etag для валидации
    response.headers['ETag'] = f'"{hash(str(response.data))}'  # Уникальный идентификатор
    
    # Способ 3: Last-Modified для условного запроса
    response.headers['Last-Modified'] = datetime.now().strftime('%a, %d %b %Y %H:%M:%S GMT')
    
    return response

3. Обход кеша на уровне приложения (Redis/Memcached)

Отключить кеш для конкретного запроса:

from functools import wraps
import redis

cache = redis.Redis(host='localhost', port=6379, db=0)

def cached(timeout=300):
    """Декоратор для кеширования"""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # Проверить, нужно ли пропустить кеш
            skip_cache = kwargs.pop('skip_cache', False)
            
            cache_key = f"{f.__name__}:{args}:{kwargs}"
            
            if not skip_cache:
                cached_value = cache.get(cache_key)
                if cached_value:
                    return json.loads(cached_value)
            
            # Вычислить значение
            result = f(*args, **kwargs)
            
            # Сохранить в кеш
            if not skip_cache:
                cache.setex(cache_key, timeout, json.dumps(result))
            
            return result
        return decorated_function
    return decorator

@cached(timeout=300)
def get_user_data(user_id):
    return {'id': user_id, 'name': 'John'}

# Использование
data = get_user_data(123)  # Из кеша (или вычислить и кешировать)
fresh_data = get_user_data(123, skip_cache=True)  # Всегда свежие данные

Очистить весь кеш:

import redis

cache = redis.Redis(host='localhost', port=6379, db=0)

# Очистить конкретный ключ
cache.delete('user_data:123')

# Очистить по паттерну
for key in cache.scan_iter('user_data:*'):
    cache.delete(key)

# Очистить всю базу
cache.flushdb()

# Очистить всё (опасно!)
# cache.flushall()

4. Обход кеша в Django ORM

Отключить QuerySet кеш:

from django.db import connection
from django.views.decorators.cache import cache_page
from django.core.cache import cache

# Способ 1: Явно отключить кеш вьюхи
@cache_page(0)  # Не кешировать
def user_list(request):
    users = User.objects.all()
    return render(request, 'users.html', {'users': users})

# Способ 2: Очистить конкретный ключ
def update_user(request, user_id):
    user = User.objects.get(id=user_id)
    user.name = request.POST['name']
    user.save()
    
    # Очистить кеш
    cache.delete(f'user:{user_id}')
    
    return redirect('user_detail', user_id=user_id)

# Способ 3: Использовать select_related для избежания N+1 проблемы
# вместо кеширования
users = User.objects.select_related('profile').all()

# Способ 4: Принудительная переvalуация QuerySet
users = list(User.objects.all())  # Список вместо QuerySet
users = User.objects.all()[:10]  # Только первые 10

Отключить кеш на уровне модели:

class User(models.Model):
    name = models.CharField(max_length=100)
    
    # Отключить кеш при сохранении
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)
        
        # Очистить связанный кеш
        from django.core.cache import cache
        cache.delete_many([
            'all_users',
            f'user:{self.id}',
            f'user:email:{self.email}',
        ])

5. Обход CDN кеша

На стороне клиента:

import requests

# Способ 1: Cache busting в URL
response = requests.get('https://example.com/image.jpg?t=123456')

# Способ 2: Header для отключения кеша
headers = {
    'Cache-Control': 'no-cache',
    'Pragma': 'no-cache',
    'Expires': '0',
}
response = requests.get('https://example.com/api/data', headers=headers)

# Способ 3: ETag валидация
if_none_match = requests.head('https://example.com/api').headers.get('ETag')
headers = {'If-None-Match': if_none_match}
response = requests.get('https://example.com/api', headers=headers)

В FastAPI:

from fastapi import FastAPI, Response
from datetime import datetime, timedelta

app = FastAPI()

@app.get('/api/data')
async def get_data(skip_cache: bool = False):
    if skip_cache:
        # Отключить кеш на CDN и браузере
        headers = {
            'Cache-Control': 'no-cache, no-store, must-revalidate',
            'Pragma': 'no-cache',
            'Expires': '0',
        }
    else:
        # Кешировать на 1 час
        headers = {
            'Cache-Control': 'public, max-age=3600',
            'ETag': f'"{hash(str(response))}'  # Кешировать по ETag
        }
    
    return Response(
        content={'data': 'value'},
        headers=headers,
        media_type='application/json'
    )

6. Кеш на уровне Python (functools.lru_cache)

from functools import lru_cache
import time

@lru_cache(maxsize=128)
def expensive_calculation(n):
    time.sleep(2)  # Имитация дорогой операции
    return n ** 2

# Использование
print(expensive_calculation(5))  # 2 секунды
print(expensive_calculation(5))  # Мгновенно (из кеша)

# Очистить кеш
expensive_calculation.cache_clear()
print(expensive_calculation(5))  # 2 секунды (кеш очищен)

# Информация о кеше
print(expensive_calculation.cache_info())
# CacheInfo(hits=1, misses=1, maxsize=128, currsize=1)

7. Обход кеша в запросах к внешним API

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

# Способ 1: VCR для тестирования (запись/воспроизведение HTTP)
import vcr

@vcr.use_cassette('tests/fixtures/api_response.yaml')
def test_api_call():
    response = requests.get('https://api.example.com/data')
    return response.json()

# Способ 2: Session с контролем кеша
class NoCacheSession(requests.Session):
    def request(self, method, url, **kwargs):
        kwargs['headers'] = kwargs.get('headers', {})
        kwargs['headers']['Cache-Control'] = 'no-cache'
        return super().request(method, url, **kwargs)

session = NoCacheSession()
response = session.get('https://api.example.com/data')  # Без кеша

8. Вспомогательные техники

Версионирование ресурсов:

# Файлы с версией в имени
# static/js/app.v1.2.3.js
# Когда обновляешь: static/js/app.v1.2.4.js

# В HTML:
<script src="/static/js/app.v1.2.3.js"></script>

# Django helper
from django.templatetags.static import static

# static('js/app.js?v=1.2.3')

Условные запросы:

import requests

# Получить ETag
response = requests.head('https://api.example.com/data')
etag = response.headers.get('ETag')

# Отправить условный запрос
headers = {'If-None-Match': etag}
response = requests.get('https://api.example.com/data', headers=headers)

if response.status_code == 304:  # Not Modified
    print("Кеш всё ещё актуален")
else:
    print("Получены новые данные")

Ключевые моменты

  • HTTP заголовки — управлять кешем браузера и CDN
  • Cache busting — добавить параметр к URL (t=timestamp или v=version)
  • Отключить кеш явноCache-Control: no-cache, no-store, must-revalidate
  • ETag/If-None-Match — валидировать кеш без загрузки полного ресурса
  • Redis FLUSHDB — очистить весь кеш приложения
  • Функцион декораторы — контролировать кеш на уровне Python
  • skip_cache параметр — давать опцию обойти кеш при необходимости
  • CDN контроль — понимать кеширование на разных уровнях

Правильное управление кешем — баланс между производительностью и актуальностью данных.