Eliminar datos de una base de datos compartible

Un artículo sobre cómo resolver el problema de optimizar el proceso de eliminación de archivos de un sistema fragmentado. Se trata de un proyecto para compartir y trabajar con archivos. El sistema fue una startup hace unos 8 años, luego se disparó con éxito y se vendió varias veces. El proyecto tiene 4 desarrolladores que están con el proyecto desde el principio, lo cual es muy valioso. La documentación, tradicionalmente, no tenía tiempo para escribir o no es muy relevante.

¿Por qué leerías esto y por qué escribí todo esto? Me gustaría hablar sobre un rastrillo que cuidadosamente se encuentra dentro del sistema y se golpea para que las estrellas se salgan de los ojos.

Quiero agradecer a Hanna_Hlushakova por trabajar juntos, poner fin al proyecto y ayudar a preparar el artículo. Básicamente, encontrará descripciones del problema y el algoritmo para resolverlo, que utilizamos, no hay ejemplos de código, estructuras de datos u otras cosas necesarias. No sé si mi experiencia te ayudará a evitar un rastrillo en casa, pero espero que obtengas algo útil. Quizás este artículo sea una pérdida absolutamente irrevocable de tiempo valioso.



Sobre el proyecto


El proyecto es uno de los líderes en la plaza Gartner, tiene empresas clientes con más de 300 mil empleados en los EE. UU. Y Europa, y varios miles de millones de archivos para mantenimiento.

Las siguientes tecnologías se utilizan en el proyecto: Microsoft, servidores C # .net, base de datos MS SQL, 14 servidores activos + 14 en modo de reflejo de datos.

El volumen de bases de datos es de hasta 4 Tb, la carga constante en horario comercial es de aproximadamente 400 mil solicitudes por minuto.

Hay mucha lógica de negocios en la base de datos:
450 mesas
1000 procedimientos almacenados
80,000 líneas de código SQL
Tradicionalmente, o no tenían tiempo para escribir la documentación, o no es relevante.

Sobre la tarea


La tarea es rehacer la eliminación de archivos del almacenamiento que ya han sido eliminados por los clientes, y el período de almacenamiento de los archivos eliminados ha expirado, en caso de que quieran restaurarlos. En la versión actual, de acuerdo con los cálculos de la propia empresa, los archivos que se eliminaron hace 1 año se almacenaron en los servidores, aunque de acuerdo con las condiciones comerciales, deberían haberse almacenado solo 1 mes. Dado que algunos de los archivos se almacenan en S3, la compañía pagó por los datos adicionales, y los clientes que usaron el almacenamiento local se preguntaban por qué los archivos ocupaban más espacio del que deberían.

Bases de datos shardovany para el cliente de la empresa.

¿Cómo funcionó la eliminación antes?





En un servidor global con información sobre todos los archivos del sistema, se formaron rangos de 15 mil identificadores de archivos.

Luego, de acuerdo con el cronograma, se lanzó una encuesta de servidor para una variedad de identificadores de archivos.
Los límites del rango se transmitieron a cada fragmento.

Fragmento en respuesta envió archivos encontrados desde el rango.

El servidor principal agregó los archivos que faltan a la tabla de cola para su eliminación en su base de datos.
Luego, desde la tabla de colas, el servicio de eliminación de archivos físicos del almacenamiento recibió un grupo de identificadores para su eliminación, después de lo cual envió una confirmación de que iba a eliminar los archivos y se inició una verificación para todos los fragmentos si estos archivos se usaron allí.
Con un aumento en la cantidad de archivos, este enfoque comenzó a funcionar muy lentamente, ya que había varios miles de millones de archivos y la cantidad de rangos aumentó significativamente. Los archivos eliminados aún permanecen menos del 5% en comparación con el número total, respectivamente, es muy ineficiente clasificar miles de millones de archivos para encontrar varios millones de archivos eliminados.

Por ejemplo, generalmente después de que un usuario elimina un archivo, debe almacenarse durante 1 mes, en caso de que deba restaurarse. Después de este período, el programa debe eliminar el archivo del repositorio. Con el número actual de archivos, el número de rangos y la velocidad de omisión de rangos, el servidor tardaría 1 año en omitir completamente todos los rangos.
Está claro que el lugar no fue liberado, y esto causó la insatisfacción de los usuarios, ya que sus servidores almacenaron muchas veces más archivos de los que deberían haber sido reportados. La propia empresa de servicios pagó un lugar adicional en S3, que fue una pérdida directa para él.

Solo en S3 cuando comenzó el trabajo, se almacenaron 2 petabytes de archivos eliminados, y esto solo está en la nube. Además, había clientes cuyos archivos estaban almacenados en sus servidores dedicados, que tenían el mismo problema: el espacio del servidor estaba ocupado por archivos borrados por los usuarios pero no borrados del servidor.

¿Qué decidiste hacer?


Decidimos rastrear los eventos de eliminación:

  • el cliente eliminó el archivo y luego expiró.
  • el usuario eliminó el archivo inmediatamente sin posibilidad de recuperación.

Al eliminar un archivo de una base de datos fragmentada, decidimos utilizar un enfoque optimista y eliminar una de las comprobaciones de uso. Sabíamos que el 99% de los archivos se usan solo dentro de un fragmento. Decidimos agregar inmediatamente el archivo a la cola de eliminación, y no verificamos el resto de los fragmentos para el uso de este archivo, ya que la verificación se realizará nuevamente luego de la confirmación del servicio de eliminación del repositorio.

Además, dejaron el TRABAJO actual que verificó los archivos eliminados por rangos para agregar archivos que se eliminaron antes del lanzamiento de la nueva versión.

Todo lo que se eliminó en el fragmento se recopila en una tabla y luego se transfiere a un único servidor, con información sobre todos los archivos.

En este servidor, se envía a la tabla de eliminación de colas.

En la tabla de eliminación antes de la eliminación, se verifica que el archivo no se use en todos los fragmentos. Esta parte de la verificación estaba aquí antes del cambio de código, y decidieron no tocarlo.

¿Qué necesitabas cambiar en el código?


En cada uno de los fragmentos, se agregó una tabla en la que se debe escribir el identificador del archivo eliminado.

Encontramos todos los procedimientos para eliminar archivos de la base de datos, solo hubo 2. Después de que el usuario eliminó el archivo, el archivo aún permanece en la base de datos durante algún tiempo.

En el procedimiento para eliminar un archivo de la base de datos, agregamos una entrada a esta tabla local con archivos eliminados.

En el servidor global con archivos, hicieron un TRABAJO que descarga una lista de archivos de bases de datos de fragmentos. Simplemente llamando a un procedimiento desde una base de datos shardable, hace un DELETE dentro y muestra una lista de archivos en OUTPUT. En MS SQL Server, la extracción de un servidor remoto es mucho más rápida que la inserción en un servidor remoto. Todo esto se hace en bloques.
Estos archivos se agregan a la tabla de eliminación de colas en el servidor global.
Se agregó una tabla con un identificador de fragmento a la tabla de cola para saber de dónde vino el evento de eliminación.

¿Cómo lo han probado todos?


Hay 3 ambientes:

Dev es un entorno de desarrollo. El código se toma de la rama de desarrollo del gith. Es posible implementar una versión diferente del código en IIS y hacer varias versiones de orquestación. Se conectará al entorno de desarrollo desde el cliente dentro de vpn. Hasta hace poco, el inconveniente era solo con las bases de datos, ya que todos los cambios en la base de datos pueden interrumpir el trabajo de otras partes del sistema. Luego las bases se hicieron locales. El código que ya se está ejecutando se puede verter en servidores de desarrollo con bases de datos para no arruinar el trabajo de todos. En un entorno de desarrollo, hay 3 fragmentos, en lugar de 12, que están en la producción, pero esto suele ser suficiente para probar la interacción.

Puesta en escena: el entorno es el mismo con el producto de acuerdo con la versión del código (casi el mismo, ya que es raro, pero los administradores realizan cambios directamente en el producto). Una copia del código de la rama maestra. A veces, algunas diferencias con el código del producto se detectan en la base de datos, pero en general son idénticas. También hay 3 fragmentos en la puesta en escena, así como en la doncella. No hay carga en la puesta en escena, así como en el desarrollo. Aquí puede ejecutar pruebas de integración completamente, ya que el código coincide con el producto. Todas las pruebas deben pasar, este es un requisito previo antes de ir a la implementación.

Perf lab, donde las pruebas se realizan bajo carga. La carga se crea usando jmeter, 10 veces menos que en el producto, y solo hay un fragmento, lo que a veces crea inconvenientes. Se toman datos de ventas, luego se anonimizan y se usan en el laboratorio de perf. Todos los servidores de la misma configuración que en prod.

La carga es 10 veces menor, porque se supone que esta es una carga aproximada que llega al producto por 1 fragmento. La desventaja es que la base global está muy infrautilizada, a diferencia de las ventas. Y, si los cambios se refieren principalmente a la base de datos global con archivos, entonces puede confiar en los resultados de la prueba solo aproximadamente; en el producto, esto puede no funcionar así. Aunque idealmente el laboratorio de rendimiento no coincide con la carga con el producto, la capacidad de realizar pruebas bajo la carga ya ayuda a detectar muchos errores antes de implementarlo en el producto.

También hay un servidor de respaldo donde puede ver los datos de la venta para detectar algunos casos. En general, la compañía opera bajo una licencia que prohíbe a los desarrolladores dar acceso a los datos de ventas y el acceso del equipo de administración y soporte (Operaciones) al desarrollo, por lo que debe solicitar la ayuda de los administradores de la base de datos. Los datos de ventas hacen que las pruebas sean muy fáciles, porque algunos casos surgen solo de datos de producción y es muy útil estudiar los datos en un sistema real para comprender cómo funciona el sistema para el usuario.

Durante las pruebas en el laboratorio de rendimiento, resultó que la carga de eliminar archivos del almacenamiento no se realizó de la palabra en absoluto. Al implementar las pruebas de carga, elegimos solicitudes más populares del software del cliente, algunas de las cuales no se incluyeron en la limpieza del almacenamiento. Como se trata de una base de datos, resultó realizar una prueba simplificada para todos los objetos modificados con la llamada de los procedimientos modificados en diferentes datos de forma manual. (sobre las opciones que conocía).
Además, en las pruebas de integración y rendimiento, el énfasis principal se puso en el tipo más popular de almacenamiento de archivos.

Una característica adicional del laboratorio de rendimiento, que no se descubrió de inmediato, es la falta de coincidencia de la cantidad de datos en algunas tablas sobre el producto y el rendimiento. En el sentido de que todos los JOB de ventas funcionan en el rendimiento, que generan datos, pero no siempre hay algo que procese los datos generados en la tabla. Y, por ejemplo, la cola de tabla antes mencionada para la eliminación en el rendimiento es mucho mayor que en el producto: 20 millones de registros en el rendimiento y 200 mil registros en pro

Proceso de implementación


El proceso de implementación es bastante estándar. No hubo cambios en el código de la aplicación para esta tarea, todos los cambios son solo en la base de datos. Un DBA siempre se incluye en la base de datos; este proceso no está automatizado. Se preparan 2 versiones de scripts: para aplicar cambios y para revertir los cambios, y se escribe una instrucción para DBA. Siempre se realizan 2 versiones de los scripts, y necesariamente se prueban para la reversión y la reversión de los cambios. Y estas mismas secuencias de comandos se utilizan para aplicar cambios a la puesta en escena y al laboratorio de rendimiento antes de iniciar la integración y las pruebas de carga.

¿Qué pasó después del despliegue?


En las primeras 5 horas después de la implementación, se produjo 1 millón de eventos en los que el software del cliente recibió un error al intentar descargar el archivo. El evento "archivo dañado". Significa que el cliente está intentando descargar el archivo, pero el archivo no se encontró en el repositorio. Por lo general, estos eventos no son del todo, o se miden en 1 - 2 mil por día.

Debo decir de inmediato que me llevó al menos 1 semana encontrar la causa del fallo en un equipo de 3 y, a veces, 5 personas (incluido yo).

Recopilamos la lista completa de archivos para los que se produjo el evento "Archivo dañado".

A pesar del hecho de que hubo más de 1 millón de eventos y todos fueron de diferentes usuarios, diferentes compañías, solo había 250 archivos únicos en esta lista.

El DBA en el servidor de respaldo fue generado para investigar los respaldos de la base de datos en el momento en que llegaron los eventos. Hay bastantes tablas en las bases del proyecto con todo tipo de registros que ayudaron en el análisis. Incluso al eliminar información de la base de datos, se agrega un registro a lo que se eliminó y por qué evento. En el producto, dichos registros se almacenan durante 1 semana y luego se combinan en el servidor de archivo.

Y también lo son las tablas con registros que ayudaron mucho a analizar lo que sucedió:
Se mantiene un registro completo con los eventos del cliente en cada fragmento

En el servidor global:

  • Registro de solicitudes para descargar todos los archivos por parte de los usuarios
  • Registrar la carga de archivos al sistema por parte de los usuarios
  • Registro de eventos de FileCorrupt
  • Archivo de registro para cancelar la eliminación del almacenamiento
  • Registro de archivos borrados de la base de datos.

Además, un ELK con registros de aplicaciones estaba disponible.

Logramos repetir el error en el entorno de desarrollo, lo que confirmó la exactitud de la suposición. Al principio, nadie se tomó en serio esta hipótesis, ya que era muy difícil creer que tantos factores coincidieran al mismo tiempo y que llegaran tantos usuarios en este momento en particular.

¿Qué salió mal?


Resultó que el sistema tenía alrededor de 250 (en comparación, miles de millones de archivos en el sistema) super duper mega archivos populares. 250 si!

Estos archivos aún eran muy antiguos. En el momento en que estos archivos se subieron al sistema, se utilizó otro sistema para generar la clave del archivo para el almacenamiento.

Resultó que para este tipo de clave, el método de eliminación física del almacenamiento físico se comporta de manera diferente a otros archivos.

En la clase con eliminación, hay un bloque de código con una condición específica para archivos con la clave anterior. El sistema, en el momento de la eliminación, antes de comprobar que el archivo no está en fragmentos, mueve este archivo antiguo a otra ubicación. Bueno, eso no funcionó.

Y resultó que en el momento en que se movió el archivo (y recordaré que es muy popular), si uno de los usuarios intenta otorgarle un nuevo derecho de usuario, el software del cliente va al almacenamiento de este archivo, pero el archivo no está en el lugar correcto. Como se mueve, eso no funciona. Y el software del cliente envía un mensaje de que el archivo está roto. En la base de datos, está marcado como roto. Y toda la información se elimina de la base de datos (bueno, casi toda).

Mientras tanto, nuestra rutina de comprobación de fragmentos descubre que se está utilizando el archivo. Y envía una respuesta que necesita devolverlo. Pero toda la información ya se ha eliminado de la base de datos y es imposible devolverla.

Divertido eh?

Es decir, cuando se eliminó el archivo, el usuario estaba en ese período de tiempo cuando se movió el archivo, se verificaron los fragmentos, y fue en este momento que el usuario envió una solicitud de descarga.

Aquí está: alta carga en acción, cuando coinciden los partidos más increíbles.



Tras recuperarnos de la sorpresa y revertir todo, nos aseguramos de que los archivos de los usuarios estén vivos, ya que fueron restaurados de los discos de otros clientes.

Naturalmente, todo estuvo bien en las pruebas, porque durante la prueba los archivos más nuevos se eliminaron con un nuevo tipo de clave que se utilizó en los últimos 5 años. Estos archivos no se transfieren a otra ubicación de almacenamiento durante el tiempo de eliminación.

Nuestro optimismo disminuyó y decidimos no seguir el camino más optimista.

Retrospectiva


Decidimos que necesitamos agregar pruebas para diferentes tipos de almacenamientos
Agregue una carga al laboratorio de rendimiento que utiliza llamadas cuando se elimina del almacenamiento
Cerrar condiciones de carrera famosas
Agregue monitoreo (aunque estaría en los planes, pero no encaja en el alcance original)

Sobre el monitoreo


Decidieron hacer un monitoreo de inmediato, pero luego se desvaneció en el fondo, ya que era necesario implementarlo más rápido.

Para el monitoreo, el proyecto utilizó Zabbix, ELK, Grafana, NewRelic, SQL Sentry y una versión de prueba de AppDynamics.

De esto, en pef lab estaba NewRelic y SQL Sentry.

Ya medimos todas las métricas del sistema y, por lo tanto, queríamos medir las métricas comerciales. Tenía experiencia organizando ese monitoreo a través de Zabbix; decidimos hacer lo mismo.

El esquema es muy simple en la base de datos para crear una tabla en la que recopilar las métricas necesarias por TRABAJO y un procedimiento que cargará las métricas recopiladas en Zabbix.

Métricas:

  • El número de archivos en la cola para su eliminación a nivel mundial
  • Número de archivos en cola por servidor
  • El número de archivos enviados al programa de desinstalación desde el repositorio
  • Cantidad de archivos borrados
  • Número de eventos de FileCorrupt
  • El número de archivos a eliminar en cada fragmento

El monitoreo se implementó y se implementó en el producto por separado, antes de que comenzaran a implementar una nueva implementación de eliminación.

Nueva solución


En general, decidimos que era mejor comer en exceso que no dormir, e hicimos un nuevo plan.

  1. compruebe el mismo fragmento en el que ya nadie usa el archivo con seguridad y transfiera solo los archivos no utilizados al servidor;
  2. al transferir al servidor, recopile todos los archivos de la tabla y verifique que los archivos no se usen en fragmentos antes de colocarlos en la tabla de cola de espera;
  3. cuando use un archivo y lo busque en el sistema, marque la cola de espera en la tabla como un archivo que requiere verificación;
  4. devolver solo archivos para los que no hubo búsquedas;
  5. archivos que se buscaron, vuelva a verificar si hay fragmentos;
  6. en general, elimine la marca de verificación en el procedimiento que elimina el archivo, ya que debería funcionar rápidamente, y el archivo usado no debería alcanzarlo en principio;
  7. tenga en cuenta en el procedimiento que todo lo eliminado por el archivo batido que está en proceso de eliminación, y no elimine información sobre él.

El punto 6 durante el despliegue incluyó la eliminación del cheque en varias etapas. Primero, dejaron el cheque, luego, una semana después, el cheque en los archivos de los empleados de la compañía se apagó, después de 2 semanas, el cheque se apagó por completo.

¿Qué necesitabas cambiar en el código?


Nuevamente, todos los cambios se aplican solo a la base de datos.

La escala de los cambios fue la mayor en el servidor global:
Agregue una tabla intermedia en la que agregar todos los archivos descargados de los fragmentos.
Haga un TRABAJO que verifique los archivos en la tabla intermedia que no se usan en fragmentos.

En la cola de archivos eliminados, agregue un campo con la fecha en que se accedió al archivo por última vez y agregue un índice.

Encuentre todos los procedimientos con acceso al archivo: resultó 5 procedimientos. Agregue un bloque que cambie la fecha del último uso en la tabla de la cola. La fecha cambia cada vez, independientemente de si se completó o no.

Agregue el programa de eliminación a los procedimientos de emisión de archivos para que muestre solo los archivos con una fecha de uso vacía.

Agregue un TRABAJO que recopile todos los archivos con la fecha de uso y las comprobaciones (con un retraso de 10 minutos, lo cual es necesario para que el software del cliente pueda agregar el archivo al fragmento, generalmente demora hasta 2 minutos, pero decidió hacerlo de manera segura) use el archivo en todos los fragmentos. Una vez completada la verificación, la fecha de uso se restablece si no se encuentra el archivo; de lo contrario, el archivo se elimina de la cola. Si la fecha de uso ha cambiado durante el proceso de verificación, los datos no cambian ya que se supone que mientras la verificación estaba en progreso, el archivo podría cargarse en el fragmento, en el que la verificación ya se había completado y se requería un nuevo ciclo de verificación para el archivo.

En fragmentos:

En el procedimiento que elimina archivos de la tabla con archivos eliminados, tuvo que agregar una verificación de que el archivo no se utilizó. El procedimiento ha perdido su simplicidad y belleza no mucho: en ELIMINAR con salida simplemente agregaron NO EXISTE.

Agregamos un TRABAJO, que en el fondo golpeó desde los archivos de la tabla utilizados en el servidor.

Pruebas


Se agregaron escenarios sobre el uso de todas las opciones de almacenamiento a las pruebas de integración.
También escribieron nuevos casos para probar la nueva funcionalidad de eliminación de archivos.

Perf Lab agregó carga al servidor global. Además, agregamos una carga correspondiente a la eliminación de archivos del almacenamiento.

Implementar


Se prepararon secuencias de comandos para la aplicación y la reversión de cambios para la base de datos. DBA lanzó scripts y resultó que durante las pruebas de carga no prestaron atención a los bloqueos en las tablas de colas para eliminar archivos. Como resultado, no arreglamos el índice, que no era el más óptimo.

Debido a esto, era necesario deshabilitar las comprobaciones de TRABAJO por rangos y analizar y agregar identificadores de archivos eliminados manualmente, de acuerdo con los archivos que se eliminaron en el sistema antes de la implementación del nuevo código.

Resultados


Como resultado, como resultado de la implementación, los nuevos archivos eliminados se eliminan del almacenamiento dentro de las 24 horas.
Los archivos eliminados antes del lanzamiento del nuevo sistema se crearon en la copia de seguridad y se agregaron a la cola para producción.

Como resultado, se eliminó el exceso de datos en S3 en la cantidad de 2 petabytes. Lo mismo sucedió con los archivos en servidores de clientes dedicados, y ahora su lugar en el servidor coincide con el lugar que se muestra en sus clientes.

El índice curvo en la tabla de la cola todavía vive en prod, la tarea de cambiar el índice en la cartera de pedidos, pero se pospuso ligeramente debido a tareas más prioritarias.

Source: https://habr.com/ru/post/es427895/


All Articles