Cómo eliminar rápidamente datos de Redis en lotes sin afectar el rendimiento
1. El procesamiento de Redis está estrechamente relacionado con la red. Si la red se interrumpe, es probable que se agote el tiempo de espera de Redis. Si esto sucede, primero debe verificar la información del ancho de banda de la red de la máquina Redis para determinar si hay una falla.
2. Redis coloca todos los datos en la memoria. Cuando la memoria física es insuficiente, el sistema operativo Linux utilizará la memoria de intercambio, lo que provocará el intercambio de memoria. En este momento, si hay un comando de llamada de redis, se producirá un tiempo de espera de redis.
int rdbSaveBackground(char *filename) { pid_t childpid; inicio largo y largo; if ( server.rdb_child_pid ! = -1) return REDIS_ERR; serverserver.dirty_before_bgsave = server.dirty; .lastbgsave_try = time(NULL); start = ustime(); if ((childpid = fork()) == 0) { int retval /* Child */ if (server.ipfd gt; 0) close(server.ipfd; ); si (servidor.sofd gt; 0) cerrar(servidor.sofd); retval = = 0if (servidor.sofd gt; 0) cerrar(servidor.sofd = retval( nombre de archivo); ) { size_t private_dirty = zmalloc_get_private_dirty(); if (private_dirty) { redisLog(REDIS_NOTICE, "RDB: zu MB de memoria utilizada por copia en escritura", private_dirty/(1024*1024) } } exitFromChild((1024*); 1024)).} exitFromChild((retval == REDIS_OK) ?rdb_save_time_start = time(NULL); server.rdb_child_pid = childpid; updateDictResizePolicy(); return REDIS_OK; } return REDIS _OK; /* no alcanzado */ }
Programa 1
También hay algunas circunstancias especiales que causarán que se produzca el intercambio. Cuando usamos rdb para la persistencia del clúster de redis, puede ocurrir memoria física insuficiente (aof la persistencia solo admite continuamente clústeres de redis. cambie la operación a. anexar, lo que es poco probable que resulte en un intercambio).
Cuando se utiliza la persistencia de rdb, como se muestra en el Programa 1, el proceso principal bifurcará un proceso secundario para volcar todos los datos en redis, y el proceso principal seguirá sirviendo al cliente. En este momento, el proceso principal y el proceso secundario comparten la misma área de memoria y el kernel de Linux utiliza copia en escritura para garantizar la seguridad de los datos. En este modo, si un cliente emite una solicitud de escritura, el kernel asigna la página a una página nueva, la marca como escritura y escribe la solicitud de escritura en esa página. Por lo tanto, bajo la persistencia de rdb, si hay otras solicitudes, redis usará más memoria y será más probable que se produzca el intercambio. Por lo tanto, use la persistencia de rdb lo menos posible en escenarios que se puedan restaurar rápidamente. Por supuesto, también puedes elegir aof, pero aof también tiene sus propios defectos. Además, también puede utilizar la estructura maestro-esclavo para separar la lectura y la escritura después de 2.6, de modo que no habrá escenarios de lectura y escritura del proceso del servidor 3. Comando de proceso único de Redis. Redis admite conexiones udp y tcp. El cliente redis envía información que contiene comandos de redis al servidor redis. Después de recibir la información, el servidor redis analiza los comandos y realiza las operaciones correspondientes. Después de recibir la información, el servidor redis analizará el comando y realizará las operaciones correspondientes. El proceso de procesamiento en serie del comando por redis es el siguiente. Primero, el servidor necesita establecer una conexión, como se muestra en el Programa 2. Después de crear el socket, vincularlo y monitorearlo, se devuelve el descriptor de archivo:
server.ipfd = anetTcpServer(server.neterr, server .port, server.bindaddr);
Programa 2
Para un servicio como redis que necesita manejar miles de conexiones (hasta 655350), necesita usar multiplexación para manejar múltiples conexiones. Aquí redis proporciona epoll, select y kqueue para la implementación, y epoll (ae.c) se usa de forma predeterminada. Después de obtener el descriptor de archivo fd devuelto por la función de escucha, redis agregará fd y su función de procesamiento aceptarTcpHandler a la lista vinculada controlada por eventos. De hecho, cuando se agrega a la cola de eventos, el controlador de eventos del Programa 4 agrega el descriptor de archivo fd asociado con el socket al evento de escucha de epoll.
if (server.ipfd gt; 0 & aeCreateFileEvent(server.el, server.ipfd, AE_READABLE, AcceptTcpHandler, NULL) == AE_ERR) redisPanic( "Error irrecuperable al crear el evento del archivo server.ipfd .
"); int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData){ if (fd gt; = eventLoop-gt; setize) { errno = ERANGE; return AE_ERR; } aeFileEvent *fe = amp;eventLoop-gt;eventos[fd]; if (aeApiAddEvent(eventLoop, fd, máscara) == -1) return AE_ERR; fe-gt;mask |= máscara; = proc; if (máscara & AE_ WRITABLE) fe-gt; wfileProc = proc; clientData clientData = clientData; }
Programa 3
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) { aeApiState *state = eventLoop-gt; struct epoll_event ee; Ha sido monitoreado por un evento, debemos realizar la operación MOD *; de lo contrario, debemos realizar la operación ADD.
*/ int op = eventLoop-gt; event[fd].mask = eventLoop-gt; event[fd].events[fd].mask == AE_NONE ? gt; events[fd].mask; /* Fusionar eventos antiguos */ if (máscara amp; AE_READABLE) ee.events |= EPOLLIN; if (máscara amp; AE_READABLE) ee.events |= EPOLLIN; ) ee.events |= EPOLLIN; if (máscara & AE_READABLE) ee.events |= EPOLLINEEPOLLIN; if (máscara & AE_WRITABLE) ee.events |= EPOLLOUT = 0; .data.fd = fd; if (epoll_ctl(state-gt. epfd, op, fd, amp; ee) == -1) return -1;
Programas: EE_WRITABLE = EPOLLOUT; .
Programa 4
Después de la sesión inicial de todos los controladores de eventos, como se muestra en el Programa 5, el proceso principal realiza los siguientes pasos de acuerdo con numevents = aeApiPoll(eventLoop, tvp) y su correspondiente El controlador obtiene el descriptor de archivo listo para io y maneja el fd. El proceso general es aceptar()-gt; createclient()-gt; readQueryFromClient(). Entre ellos, readQueryFromClient() lee el comando redis del mensaje -gt; ProcessInputBuffer() -gt call() finaliza el comando.
void aeMain(aeEventLoop *eventLoop) { eventLoop-gt; stop = 0; while (!eventLoop-gt; stop) { if (eventLoop-gt; beforesleep! = NULL) eventLoop-gt( eventLoop); aeProcessEvents(eventLoop, AE_ALL_EVENTS); int aeProcessEvents(aeEventLoop *eventLoop, int banderas) { --------------------------- ---- numevents = aeApiPoll(eventLoop, tvp); for (j = 0; j lt; numevents; j ) { aeFileEvent *fe = amp; eventLoop-gt; ; int máscara = eventLoop-gt; disparado[j].mask; int fd = eventLoop-gt; disparado[j].fd; .Código: Tal vez un evento * manejado eliminó un elemento que se activó y todavía no lo hemos * manejado, por lo que queremos verificar si el evento aún es válido. */ if (fe-gt; máscara y máscara; ... máscara y máscara; AE_READABLE) { rfired = 1; rfileProc(eventLoop, fd, fe-gt; clientData, máscara }); if (fe-gt; máscara amp; máscara amp; AE_ WRITABLE) { if (!rfired || fe-gt; wfileProc ! = fe-gt; rfileProc) wfileProc(eventLoop, fd, fe-gt; clientData , máscara); } procesado; }}
Programa 5
Como se puede ver en el código anterior, redis utiliza un controlador de eventos combinado con epoll para implementar comandos serializados. Por lo tanto, algunos comandos lentos (como sort, hgetall, union, mget) alargarán el tiempo de procesamiento de un solo comando y fácilmente provocarán que se agote el tiempo de espera de los comandos siguientes. Por lo tanto, primero debemos tratar de evitar el uso de comandos lentos desde una perspectiva empresarial, como cambiar el formato hash a kv para el autoanálisis. En segundo lugar, debemos aumentar la cantidad de instancias de redis. Cuantas menos llamadas a cada servidor de redis, mejor. . Necesitamos evitar órdenes lentas desde el lado empresarial.