python - borrar - django rest framework cache
¿Cómo maneja Django múltiples servidores de memcached? (4)
En la documentación de django dice esto:
...
Una excelente característica de Memcached es su capacidad para compartir caché en múltiples servidores. Esto significa que puede ejecutar los demonios de Memcached en varias máquinas, y el programa tratará el grupo de máquinas como un solo caché, sin la necesidad de duplicar los valores de caché en cada máquina. Para aprovechar esta función, incluya todas las direcciones del servidor en UBICACIÓN, ya sea separadas por punto y coma o como una lista.
...
El framework de caché de Django - Memcached
¿Cómo funciona esto exactamente? He leído algunas respuestas en este sitio que sugieren que esto se logra mediante la fragmentación en los servidores en función de los hashes de las claves.
Pregunta de múltiples servidores de memcached
¿Cómo funciona realmente MemCacheStore con múltiples servidores?
Está bien, pero necesito una respuesta mucho más específica y detallada que esa. Uso de django con pylibmc o python-memcached ¿cómo se realiza realmente este fragmento? ¿Importa el orden de las direcciones IP en la configuración? ¿Qué sucede si dos servidores web diferentes que ejecutan la misma aplicación django tienen dos archivos de configuración diferentes con las direcciones IP de los servidores memcached en un orden diferente? ¿Resultará eso en que cada máquina use una estrategia de fragmentación diferente que cause claves duplicadas y otras ineficiencias?
¿Qué pasa si una máquina en particular aparece dos veces en la lista? Por ejemplo, ¿qué pasaría si hiciera algo como esto donde 127.0.0.1 es en realidad la misma máquina que 172.19.26.240?
CACHES = {
''default'': {
''BACKEND'': ''django.core.cache.backends.memcached.MemcachedCache'',
''LOCATION'': [
''127.0.0.1:11211'',
''172.19.26.240:11211'',
''172.19.26.242:11211'',
]
}
}
¿Qué pasa si uno de los servidores de memcached tiene más capacidad que los otros? Si la máquina uno tiene 64 MB de memoria y la máquina 2 tiene 128 MB, ¿el algoritmo de fragmentación lo tendrá en cuenta y le dará a la máquina 2 una mayor proporción de las claves?
También he leído que si se pierde un servidor memcached, entonces esas claves se pierden. Eso es obvio cuando se trata de fragmentación. Lo que es más importante es lo que sucederá si un servidor memcached deja de funcionar y dejo su dirección IP en el archivo de configuración. ¿Django / memcached simplemente no obtendrá las claves que se habrían compartido en ese servidor fallido, o se dará cuenta de que el servidor ha fallado y creará una nueva estrategia de fragmentación? Si hay una nueva estrategia de fragmentación, ¿toma inteligentemente las claves que originalmente estaban destinadas al servidor fallido y las divide entre los servidores restantes, o presenta una nueva estrategia como si el primer servidor no existiera? resulta en duplicación de claves?
Intenté leer el código fuente de python-memcached, y no pude resolverlo del todo. Planeo intentar leer el código de libmemcached y pylibmc, pero pensé que preguntar sería más fácil si alguien ya lo supiera.
Es el cliente memcached real que hace el sharding. Django solo pasa la configuración de settings.CACHES
al cliente.
El orden de los servidores no importa *, pero (al menos para python-memcached) puede especificar un ''peso'' para cada uno de los servidores:
CACHES = {
''default'': {
''BACKEND'': ''django.core.cache.backends.memcached.MemcachedCache'',
''LOCATION'': [
(''cache1.example.org:11211'', 1),
(''cache2.example.org:11211'', 10),
],
}
Creo que un vistazo rápido a memcache.py
(de python-memcached) y especialmente a memcached.Client._get_server
debería responder el resto de sus preguntas:
def _get_server(self, key):
if isinstance(key, tuple):
serverhash, key = key
else:
serverhash = serverHashFunction(key)
for i in range(Client._SERVER_RETRIES):
server = self.buckets[serverhash % len(self.buckets)]
if server.connect():
#print "(using server %s)" % server,
return server, key
serverhash = serverHashFunction(str(serverhash) + str(i))
return None, None
Espero que los otros clientes de memcached se implementen de una manera similar.
Aclaración por @Apreche: El orden de los servidores importa en un caso. Si tiene varios servidores web y desea que todos pongan las mismas claves en los mismos servidores de memcached, debe configurarlos con la misma lista de servidores en el mismo orden y con los mismos pesos
Pensé en agregar esta respuesta dos años después de que se hiciera la pregunta, ya que ocupa un lugar muy alto en la búsqueda y porque encontramos una situación en la que django estaba hablando solo con uno de los servidores memcached.
Con un sitio ejecutándose en django 1.4.3, python-memcached 1.51 hablando con cuatro instancias de memcached, encontramos que la base de datos estaba siendo consultada con mayor frecuencia de lo esperado. Al investigar más a fondo, encontramos que cache.get()
devolvía None
para las claves que se sabía que estaban presentes en al menos una de las instancias de memcached. Cuando se inició memcached con la opción -vv, ¡se demostró que la pregunta se hizo solo a un servidor!
Después de haber tirado mucho cabello, cambiamos el backend a django.core.cache.backends.memcached.PyLibMCCache
(pylibmc) y el problema desapareció.
Probé parte de esto y encontré algunas cosas interesantes con django 1.1 y python-memcached 1.44.
En django usando 2 servidores memcache
cache.set(''a'', 1, 1000)
cache.get(''a'') # returned 1
Busqué en qué servidor de memcache ''a'' estaba destinado a usar otras 2 configuraciones de django, cada una apuntando a uno de los servidores de memcache. Simulé una interrupción de la conectividad al instalar un firewall entre la instancia original de django y el servidor memcache en el que se almacenaba ''a''.
cache.get(''a'') # paused for a few seconds and then returned None
cache.set(''a'', 2, 1000)
cache.get(''a'') # returned 2 right away
La biblioteca del cliente de memcache actualiza su estrategia de fragmentación si un servidor falla.
Entonces quité el firewall.
cache.get(''a'') # returned 2 for a bit until it detected the server back up then returned 1!
Puede leer datos obsoletos cuando un servidor de memcache se cae y vuelve. Memcache no hace nada inteligente para tratar de evitar esto.
Esto realmente puede desordenar las cosas si está utilizando una estrategia de almacenamiento en caché que pone las cosas en memcache durante mucho tiempo y depende de la invalidación de la memoria caché para manejar las actualizaciones. Se puede escribir un valor antiguo en el servidor de caché "normal" para esa clave y, si pierde la conectividad y se realiza una invalidación durante esa ventana, cuando el servidor vuelva a estar accesible, leerá datos obsoletos que no debería poder a.
Una nota más: he estado leyendo sobre algunas bibliotecas de caché de objetos / consultas y creo que johnny-cache debería ser inmune a este problema. No invalida explícitamente las entradas; en cambio, cambia la clave en la que se almacena en caché una consulta cuando cambia una tabla. Así que nunca leería accidentalmente los valores antiguos.
Edit: creo que mi nota sobre johnny-cache funciona bien es una mierda. http://jmoiron.net/blog/is-johnny-cache-for-you/ dice "hay lecturas adicionales de caché en cada solicitud para cargar las generaciones actuales". Si las generaciones se almacenan en el propio caché, el escenario anterior puede hacer que se lea una generación obsoleta.
Si lo ideal es utilizar dos memcache distintos, la implementación predeterminada de django permite este comportamiento.
Primero querrás actualizar tu configuración.py:
CACHES = {
''default'': {
''BACKEND'': ''django.core.cache.backends.memcached.MemcachedCache'',
''LOCATION'': ''127.0.0.1:11211'',
},
''rusty'': {
''BACKEND'': ''django.core.cache.backends.memcached.MemcachedCache'',
''LOCATION'': ''127.0.0.1:11222'',
}
}
Dentro de su código django, el método predeterminado para acceder a memcache no ha cambiado. Ahora puede utilizar la otra interfaz de caché de la siguiente manera:
from django.core.cache import cache, caches
cache.set("activity", ''great stuff'', 15 ) # Default cache
caches["rusty"].set("activity", "A great time}", 32) # New rusty cache interface
La documentación de Django tiene un excelente artículo sobre este tema: https://docs.djangoproject.com/en/dev/topics/cache/