2010-01-07 8 views
16

Una línea de fondo: soy el desarrollador de Redis, a NoSQL database. Una de las nuevas funciones que estoy implementando es la memoria virtual, porque Redis toma todos los datos en la memoria. Gracias a VM Redis es capaz de transferir objetos raramente usados ​​de la memoria al disco, hay varias razones por las que esto funciona mucho mejor que dejar que el sistema operativo haga el trabajo por nosotros intercambiando (los objetos redis están construidos con muchos objetos pequeños asignados en no contiguos lugares, cuando Redis los serializa en disco, toman 10 veces menos espacio en comparación con las páginas de memoria donde viven, y así sucesivamente).C programa atascado en espera ininterrumpida durante la ejecución de E/S de disco en Mac OS X Snow Leopard

Ahora tengo una implementación alfa que funciona perfectamente en Linux, pero no tan bien en Mac OS X Snow Leopard. De vez en cuando, mientras Redis intenta mover una página de la memoria al disco, el proceso de redis ingresa en el estado de espera ininterrumpida durante minutos. No pude depurar esto, pero esto sucede en una llamada al fseeko() o al fwrite(). Después de unos minutos, la llamada finalmente vuelve y redis continúa funcionando sin problemas: no se bloquea.

La cantidad de datos transferidos es muy pequeño, algo así como 256 bytes. Por lo tanto, no debería tratarse de una gran cantidad de E/S realizadas.

Pero hay un detalle interesante sobre el archivo de intercambio que es el objetivo de la operación de escritura. Es un archivo grande (26 Gigabytes) creado abriendo un archivo con fopen() y luego ampliado con ftruncate(). Finalmente, el archivo es unlink() ed para que Redis continúe tomando una referencia al mismo, pero estamos seguros de que cuando el proceso Redis salga, el sistema operativo realmente liberará el archivo de intercambio.

Ok, eso es todo, pero estoy aquí para más detalles. Y por cierto, incluso puedes encontrar el código real en Redis git, pero no es trivial entenderlo en cinco minutos dado que es un sistema bastante complejo.

Muchas gracias por cualquier ayuda.

+0

Más información: ahora, al intentar con un archivo de intercambio más pequeño (256 MB), el error desapareció, incluso si los datos están escritos exactamente en las mismas ubicaciones y en el mismo número de páginas. Teniendo esto en cuenta y las otras suposiciones en las respuestas, parece mucho que lo que sucede es que el sistema operativo después de algunas escrituras parece tratar de asignar físicamente el gran archivo en el sistema de archivos, y esto lleva minutos dado el tamaño. Puedo "escribir" algunos bytes aleatorios al inicio para forzar la asignación física lo antes posible, al menos como una opción. Muchas gracias. Será necesario actualizar aquí. – antirez

Respuesta

11

Como lo entiendo, HFS + tiene muy poco apoyo para los archivos dispersos. Por lo tanto, es posible que su escritura active una expansión de archivo que esté inicializando/materializando una gran fracción del archivo.

Por ejemplo, sé que mapear un nuevo archivo vacío grande y luego escribir en algunas ubicaciones aleatorias produce un archivo muy grande en el disco con HFS +. Es bastante molesto ya que mmap y los archivos dispersos son una forma extremadamente conveniente de trabajar con datos, y prácticamente cualquier otra plataforma/sistema de archivos maneja esto con gracia.

¿El archivo de intercambio está escrito de forma lineal? ¿Quiere decir que reemplazamos un bloque existente o escribimos un nuevo bloque al final e incrementamos un puntero de espacio libre?Si es así, quizás hacer llamadas ftruncate más frecuentes para expandir el archivo resultaría en pausas más cortas.

Como nota aparte, me llama la atención por qué Redis VM no usa mmap y luego simplemente mueve bloques en un intento de concentrar los bloques calientes en las páginas calientes.

+0

Hola Jason. Sí, esta fue mi idea también: que por alguna razón después de ftruncate() y después de algunas escrituras, en algún momento la implementación de HFS + cree que es hora de materializar una gran parte del archivo. Las páginas se asignan de forma incremental. Utilizo un algoritmo similar al del kernel de Linux. Intento asignar incrementos hasta un número determinado de páginas, que volver al inicio del archivo de vez en cuando buscando bloques contiguos libres. Ftruncates incrementales() son una buena idea AFAIK. Pensé en ello, pero evito decir "falta de espacio" al inicio en el disco completo necesario. – antirez

+0

Me pregunto, ¿ftruncate() realmente reserva el espacio de archivos incluso en sistemas que admiten archivos dispersos? También: He oído que Apple ha comenzado a trabajar en un nuevo sistema de archivos, no derivado de HFS. Hasta que lo hagan, OSX nunca será utilizable para servidores, y será molesto para los desarrolladores que se instalan en linux/solaris/etc. –

+0

Después de intentar con un archivo más pequeño, el error desapareció. Así que creo que su respuesta es correcta, después de ftruntar las primeras escrituras probablemente estén materializando el archivo. Dado que todo el mundo ejecuta Redis en Linux para la producción, este no es un gran problema, pero es mejor saber :) Gracias – antirez

0

¿Ha desactivado el almacenamiento en caché de archivos para su archivo? es decir fcntl (fd, F_GLOBAL_NOCACHE, 1)

+0

No, es una buena idea que la memoria caché del sistema operativo pueda almacenar en caché el archivo cuando hay memoria libre en el sistema. En realidad, uno de los usos legítimos es intercambiar ciclos de CPU por memoria, ya que los datos almacenados en VM son mucho más pequeños pero de acceso más lento. Así que en teoría debería ser un archivo normal, pero si crees que este podría ser el problema, puedo intentarlo en realidad. Informaré mis hallazgos. Gracias por la respuesta. – antirez

-1

Como dijo Linus una vez en la lista de correo de Git:

"Soy consciente de que la gente OS X tienen dificultades para aceptar, pero OS X sistemas de ficheros son generalmente total y basura total, incluso más que Windows ".

+1

Entretenido, pero no es una respuesta útil. – sbooth

0

¿Ha intentado depurar con DTrace and o Instruments (frente experimental Dtrace de Apple)?

Exploring Leopard with DTrace

Debugging Chrome on OS X

+0

Intenté dtruss para ver las llamadas, sin demasiado éxito, sin pistas sobre por qué tarda tanto.Probablemente es algo de bloqueo que el sistema operativo está haciendo como materializar parte del archivo en el disco después de la ftruncate? Probaré más y gracias por los enlaces y la respuesta. – antirez

1

antirez, no estoy seguro de que seré de mucha ayuda ya que mi experiencia con Apple se limita al Apple ][, pero lo intentaré.

Lo primero es una pregunta. Pensaría que, para la memoria virtual, la velocidad de operación sería una medida más importante que el espacio en disco (especialmente para un DB NoSQL donde la velocidad es el punto, de lo contrario estarías usando SQL, ¿no?). Pero, si su archivo de intercambio es 26G, tal vez no :-)

Algunas cosas que debe probar (si es posible).

  1. Trate de aislar realmente el problema al buscar o escribir. Me cuesta mucho creer que una búsqueda podría tomar tanto tiempo, ya que, en el peor de los casos, debería ser un cambio de puntero del buffer. Aún así, no escribí OSX, así que no puedo estar seguro.
  2. Intenta ajustar el tamaño del archivo de intercambio para ver si eso es lo que está causando el problema.
  3. ¿Alguna vez amplió dinámicamente el archivo de intercambio (en lugar de la preasignación)? Si lo haces, eso puede ser lo que está causando el problema.
  4. ¿Siempre escribe lo más bajo que pueda en el archivo? Puede ser que la creación de un archivo 26G no llene realmente los datos, pero si lo crea y escribe en el último byte, el sistema operativo puede tener que poner a cero los bytes antes de eso (posponiendo la inicialización, si corresponde).
  5. ¿Qué sucede si solo asigna previamente el archivo completo (escribe en cada byte) y no lo desenlaza? En otras palabras, deja el archivo allí entre las ejecuciones de tu programa (creándolo si no existe ya, por supuesto). Luego, en su código de inicio para Redis, simplemente inicialice el archivo (punteros y demás). Esto puede eliminar cualquier problema como los del punto 4 anterior.
  6. Pregunte en los diferentes sitios de BSD también. No estoy seguro de cuánto cambió Apple bajo las sábanas, pero OSX es solo BSD en el nivel más bajo (patos Pax para cubrir).
  7. También considere pedir en los sitios de Apple (si aún no lo ha hecho).

Bueno, esa es mi pequeña contribución, espero que ayude. Buena suerte con tu proyecto.

+0

Hola, ¡tu comentario es genial! Muchas gracias por eso. Sobre el tamaño, de hecho, todo es cuestión de velocidad, pero hay muchos conjuntos de datos en los que solo se utiliza activamente el 5% de todo el conjunto de datos, por lo que a veces puede ser útil un gran archivo de intercambio. En Redis, el usuario puede configurar tanto el tamaño del archivo de intercambio (el tamaño de la página y el número de páginas en realidad) como la cantidad de RAM que Redis puede usar, por lo que es cuestión de ajustar muy bien el sistema para su conjunto de datos. Por cierto: 1) buena idea. 2) de hecho, esto puede confirmar si el tiempo real de asignación de archivos. 3) es difícil recuperarse del espacio pero ... – antirez

+0

(continuar) Lo intentaré con 4 también. 5) el tiempo de inicio puede ser demasiado grande, me gustaría hacerlo de forma incremental, y dado que con Linux funciona, y es la primera plataforma de implementación ... 6 y 7) también son buenas ideas. Maravilloso comentario y ayuda. ¡Gracias! En este momento mi mejor suposición es que Mac OS x trata de asignar el archivo en el disco después de las pocas escrituras, y dado que es un archivo de 26 GB, tarda años. – antirez

+0

antirez, re "5) el tiempo de inicio puede ser demasiado grande": sugerí que hagas esto * una vez * la primera vez que ejecutas el programa y dejas el archivo de intercambio entre ejecuciones. De esta forma, las siguientes ejecuciones no tendrán que crear el archivo. Todavía tendrían que inicializarlo, pero con suerte sería un simple caso escribir algunos valores tipo puntero o conteos cero al comienzo. – paxdiablo

Cuestiones relacionadas