Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 10 de enero de 2012
Hasta donde yo sé, ZFS es el único sistema de ficheros de uso mínimamente popular que permite combinar el uso de discos duros normales con memorias FLASH (SSD = Solid-State Disk) de forma transparente y combinando lo mejor de ambos mundos.
Lo discos duros son razonablemente rápidos para acceso "streaming" (acceso a datos contiguos, no acceso aleatorio) y tienen una gran capacidad a un coste reducido.
Aunque tecnológicamente los SSD puedan proporcionar un ancho de banda arbitrariamente alto (simplemente juntando más chips en el paquete y accediéndolos en paralelo) y que habitualmente su consumo energético sea inferior a un disco duro equivalente, su principal ventaja es que carecen de "seek time". Es decir, que a la hora de acceder al SSD, da lo mismo que los datos estén contiguos a que estén repartidos de forma aleatoria.
Un disco duro es un dispositivo mecánico sujeto a fatiga y a inercias físicas. Por mucha ingeniería que metamos, son límites físicos que pueden mejorarse pero no romperse.
Eventualmente, el coste por gigabyte de los SSD bajará por debajo del de los discos duros, momento en que éstos quedarán obsoletos. Mientras tanto casi todos los sistemas te obligan a elegir: usar disco duro o usar SSD. Muchos servidores incorporan ambos dispositivos, y el administrador debe particionarlos y decidir qué aloja en cada uno. Por ejemplo, la base de datos en el SSD, porque la velocidad de acceso aleatorio es importante, y lo que se usa poco o cuya velocidad de acceso es menos crítica, en el disco duro, que es más barato y tiene mucha más capacidad.
En todo caso, es una decisión explícita del administrador de sistemas. Si la base de datos crece hasta superar la capacidad de memoria del SSD, no podemos sino comprar otra más grande, ya que alojarla en el disco duro no nos proporciona el rendimiento que necesitamos.
En ZFS no es así. En ZFS podemos unir un "pool" de discos duros (puede ser un disco o docenas) a un número cualquiera (uno o docenas) de SSD's. Y no asignamos cada recurso de forma estática para un "dataset", sino que pertenecen a todo el "pool" y su uso es automático y transparente.
Básicamente las SSD se utilizarán como sistema de caché transparente. Veamos los dos casos: lectura y escritura.
ZFS utiliza un sistema de caché en RAM llamado ARC (Adaptive Replacement Cache). Este algoritmo es bastante más avanzado y complejo que el LRU, por ejemplo, pero se comporta mejor que éste ante situaciones patológicas típicas, como que copiar ficheros grandes de un sitio a otro desaloje de la caché objetos de uso frecuente, que tendrán que leerse de nuevo del disco duro enseguida.
Pero por muy bueno que sea el algoritmo, y aunque que el sistema operativo utilice los 24 gigabytes del servidor como caché, lo cierto es que si estamos manejando datos de forma activa con un tamaño superior a nuestra capacidad de RAM al final tendremos que "tirar" datos de la memoria que, tarde o temprano, habrá que volver a leer del disco duro.
Pero podemos definir un dispositivo del "pool" ZFS como "L2ARC". Es decir, "ARC de nivel 2". La idea es que los objetos que ya no caben en la RAM (el ARC), en vez de eliminarlos de memoria y recargarlos de disco de nuevo cuando sean necesarios, se envían al dispositivo (o dispositivos) L2ARC. Si la lectura del L2ARC es más rápida que acceder al disco original, tenemos una ganancia de rendimiento.
Normalmente el dispositivo L2ARC será una SSD, pero no es necesario. Por ejemplo, si los datos originales residen en un dispositivo iSCSI remoto, usar un disco duro local rápido como L2ARC puede ser una ganancia sustancial en ancho de banda y en velocidad. ZFS usará como L2ARC el dispositivo o dispositivos que le indiquemos.
Algunas características de L2ARC en las versiones actuales de ZFS:
Este detalle hace también que asignar un dispositivo "grande" como L2ARC puede ser un desperdicio que no aporta ningún beneficio. Por ejemplo, si creamos un L2ARC en un disco de un terabyte, tardaríamos mucho (posiblemente, nunca) en llenarlo. No compensa. Por no olvidar el consumo de memoria RAM que supondría. Por otro lado, contendría datos de uso tan infrecuente que ir al disco duro "real" a por ellos no afecta al rendimiento.
La regla habitual es que 100GB de L2ARC ocupan 1-2GB de RAM real. Pero la cifra exacta depende del tamaño de bloque efectivo de ZFS.
Debido a esto y al punto anterior, no es inteligente usar "mirrors" como L2ARC. En general, sobre todo con el tamaño actual de las SSD, es preferible utilizar dos SSD como "stripping", duplicando la capacidad y el ancho de banda del L2ARC.
Cuando se guarda un dato en el L2ARC, se le calcula una suma de control que se mantienen en RAM, de forma que podemos verificar su integridad cuando se lee, aunque el dispositivo no nos indique ningún error.
Solaris 10 y derivados proporcionan estadísticas interesantes y detalladas sobre ARC y L2ARC. Por ejemplo, en un servidor sin L2ARC puedo ver lo siguiente:
[root@XXX /]# kstat zfs::arcstats module: zfs instance: 0 name: arcstats class: misc c 10212435301 c_max 24678334464 c_min 3084791808 crtime 77.32556706 data_size 8770825216 deleted 19154548 demand_data_hits 4415206368 demand_data_misses 5057241 demand_metadata_hits 9148538352 demand_metadata_misses 3213951 evict_l2_cached 0 evict_l2_eligible 739645837312 evict_l2_ineligible 42101163520 evict_skip 2159701 hash_chain_max 15 hash_chains 305112 hash_collisions 326041988 hash_elements 1026344 hash_elements_max 1587374 hdr_size 202097704 hits 13596352582 l2_abort_lowmem 0 l2_cksum_bad 0 l2_evict_lock_retry 0 l2_evict_reading 0 l2_feeds 0 l2_free_on_write 0 l2_hdr_size 0 l2_hits 0 l2_io_error 0 l2_misses 0 l2_read_bytes 0 l2_rw_clash 0 l2_size 0 l2_write_bytes 0 l2_writes_done 0 l2_writes_error 0 l2_writes_hdr_miss 0 l2_writes_sent 0 memory_throttle_count 0 mfu_ghost_hits 4091225 mfu_hits 13314858371 misses 14457327 mru_ghost_hits 3114547 mru_hits 249049566 mutex_miss 40351 other_size 1236206768 p 9193395515 prefetch_data_hits 5728937 prefetch_data_misses 5098302 prefetch_metadata_hits 26878925 prefetch_metadata_misses 1087833 recycle_miss 857241 size 10209129688 snaptime 8599864.1085979
La máquina tiene un "uptime" de tres meses. No tiene configurado L2ARC (aún), pero vemos que Solaris hubiera movido al L2ARC unos 700 gigabytes, de haber podido. Eso no significa que necesitemos 700GB para el L2ARC, por dos motivos:
Acelerar las lecturas supone un aumento de rendimiento inmediato porque casi ninguna aplicación puede realizar ningún tipo de progreso hasta que recibe el dato leído. A la hora de escribir, en cambio, lo importante es disponer de ancho de banda suficiente al disco para nuestra aplicación. Pero la latencia de escritura no importa en la mayoría de los casos. De hecho cualquier sistema operativo moderno decente acumulará unos segundos de actividad en RAM y luego los volcará en una ráfaga al disco duro, sin que las aplicaciones sean conscientes de estos detalles. Esta acumulación permite muchas optimizaciones, como evitar toda actividad de disco para ficheros creados, modificados, leídos y borrados en rápida sucesión (por ejemplo, ficheros de intercambio entre programas), o colapsar muchas escrituras cortas en una larga y eficiente escritura larga. O planificar con detalle la asignación de posiciones de los datos en el disco duro. Y todo automático y transparente a las aplicaciones, que dan la orden de grabación al sistema operativo y se olvidan.
Pero hay un caso donde la velocidad de escritura es importante, crucial: las escrituras síncronas.
Las escrituras "cacheadas" en memoria, las normales, son escrituras asíncronas. Los programas piden que se grabe un dato y el sistema operativo lo hará "cuando le parezca". Un tiempo normal de "cacheo" es, por ejemplo, 30 segundos. El problema es que si se va la luz, el sistema se cuelga, el ordenador empieza a echar humo, etc., podemos perder esos 30 segundos de grabaciones pendientes. Es decir, podemos estar escribiendo nuestra tesis doctoral, grabarla y tirar del cable del ordenador a los diez segundos... y haber perdido el fichero que grabamos "hace" diez segundos.
La mayor parte del tiempo no importa mucho. Un servidor se reinicia una vez al mes (hay que parchearlo :) de forma controlada, y permitiendo que las escrituras sean asíncronas la mejora de velocidad es apabullante.
Pero existen casos donde debemos garantizar que los datos se han grabado en el disco duro de forma fehaciente. Por ejemplo, cuando llega un mensaje de correo electrónico a nuestro servidor, no debería darle el OK al remitente HASTA haberse asegurado de que el mensaje se ha grabado realmente en el disco duro. No podemos permitir que el ordenador se cuelgue y se pierda un correo electrónico. Eso es intolerable. O no queremos que nuestro balance bancario quede inconsistente. Otro ejemplo donde las escrituras síncronas son importantes es el caso de un servidor NFS.
En el caso del procesador de texto, las copias de seguridad periódicas que hace el programa automáticamente pueden ser grabaciones asíncronas (y que mantenga un histórico, porque la última puede estar incompleta), pero estaría bien que cuando el usuario pulsa sobre "save", el fichero se "grabe de verdad". Ahora.
En el mundo de las bases de datos se habla de ACID. La "D" significa "Durabilidad". Es decir, que una vez que la base de datos se hace cargo de un dato, este dato no se va a perder por "chorraditas" menores que un incendio en el datacenter. O una bomba atómica. Pero, desde luego, no porque alguien tropiece con el enchufe.
El problema es, nuevamente, que los discos duros son dispositivos mecánicos sujetos a leyes físicas. Son lentos, comparados con la electrónica, sobre todo cuando empezamos a mover el cabezal del disco duro de un lado para otro. ZFS utiliza ZIL (ZFS Intention Log) para completar las grabaciones síncronas "rápido", a poder ser sin mover el cabezal del disco duro (gracias a que ZFS es un sistema CoW, "Copy on Write"). Comparado con otros sistemas de ficheros, ZFS es muy rápido en escrituras síncronas, siendo muy fácil obtener 3-4 veces más transacciones por segundo que otros sistemas de ficheros modernos, si los datos a escribir son pocos. 120 transacciones por segundo en un disco "normal" de 7200 RPM es rutina.
Pero un SSD "normal" me permite hacer más de 5000 escrituras síncronas por segundo. Eso son casi dos órdenes de magnitud por encima. Y esto es con la tecnología actual, que no está limitada por restricciones mecánicas como mover el cabezal a la otra punta del disco duro, o esperar a que el disco rote hasta que el sector que nos interesa pase por debajo del cabezal.
ZFS permite poner el ZIL en el dispositivo separado. Puede ser una SSD o un disco duro pequeño pero rápido (digamos, un disco de 72GB de 15.000 RPM). Las escrituras asíncronas, que son la mayoría, se guardan en el disco duro como siempre, tras un pequeño tiempo en memoria. Las escrituras síncronas se guardan en el ZIL, en un dispositivo rápido, pero permanecen en memoria como siempre y serán volcados al disco duro de forma normal tras ese período de cacheo temporal en RAM. De hecho el ZIL es prácticamente escrituras en exclusiva. Solo se lee al reiniciar el sistema, para comprobar si hay transacciones pendientes de grabar en el disco duro. El resto del tiempo solo se escribe.
Algunos detalles:
[root@XXX z-dtrace]# ./zilstat.ksh -t -p datos txg waiting for txg commit... TIME txg N-Bytes N-Bytes/s N-Max-Rate B-Bytes B-Bytes/s B-Max-Rate ops <=4kB 4-32kB >=32kB 2012 Jan 5 19:30:48 619068 476512 17648 299024 1347584 49910 524288 11 0 0 11 2012 Jan 5 19:31:18 619069 445616 14853 119152 4775936 159197 598016 41 1 0 40 2012 Jan 5 19:31:48 619070 2907760 96925 2860440 3575808 119193 3014656 28 0 0 28 2012 Jan 5 19:32:18 619071 666240 22208 409688 2527232 84241 1122304 26 1 0 25 2012 Jan 5 19:32:48 619072 278248 9274 142248 1212416 40413 430080 15 0 0 15 2012 Jan 5 19:33:18 619073 803400 26780 363296 3067904 102263 524288 33 1 0 32 2012 Jan 5 19:33:48 619074 82280 2742 45136 466944 15564 262144 5 0 0 5 2012 Jan 5 19:34:18 619075 1139568 37985 306712 3584000 119466 917504 47 1 0 46 2012 Jan 5 19:34:48 619076 49656 1655 49656 503808 16793 503808 6 0 0 6 2012 Jan 5 19:35:18 619077 4127464 137582 2807424 6348800 211626 2883584 62 1 2 59 2012 Jan 5 19:35:48 619078 181048 6034 80864 634880 21162 262144 7 0 0 7 2012 Jan 5 19:36:18 619079 786864 26228 544536 3895296 129843 1122304 35 1 0 34 2012 Jan 5 19:36:48 619080 850440 28348 715624 2002944 66764 917504 16 0 0 16 2012 Jan 5 19:37:18 619081 1198856 39961 404856 5578752 185958 823296 50 1 0 49 2012 Jan 5 19:37:48 619082 305360 10178 117160 2134016 71133 786432 17 0 0 17 2012 Jan 5 19:38:18 619083 816240 27208 294320 4993024 166434 1179648 45 1 1 43 2012 Jan 5 19:38:48 619084 2915080 97169 2820680 3432448 114414 2883584 28 0 2 26 2012 Jan 5 19:39:18 619085 625152 20838 302808 2576384 85879 786432 39 1 6 32 2012 Jan 5 19:39:48 619086 198328 6610 48400 2396160 79872 671744 32 0 8 24 2012 Jan 5 19:40:18 619087 1120288 37342 775032 2473984 82466 1048576 37 1 11 25 2012 Jan 5 19:40:48 619088 194720 6490 135560 692224 23074 262144 8 0 3 5 2012 Jan 5 19:41:18 619089 58749672 1958322 58147816 69685248 2322841 65966080 540 1 1 538 2012 Jan 5 19:41:48 619090 340008 11333 67136 2785280 92842 536576 33 0 9 24 2012 Jan 5 19:42:18 619091 3623800 120793 2612088 6569984 218999 2752512 68 1 2 65 2012 Jan 5 19:42:48 619092 1039200 34640 903920 1347584 44919 1179648 11 0 0 11 2012 Jan 5 19:43:18 619093 672104 22403 358896 2379776 79325 524288 31 1 2 28 2012 Jan 5 19:43:48 619094 47096 1569 47096 167936 5597 167936 2 0 0 2 ^C
Vemos que ZFS genera una transaccion ZFS nueva cada 30 segundos (en algunos casos el tiempo puede ser menor, como cuando se hace un "snapshot" ZFS), y las escrituras síncronas durante cada período se canalizan al ZIL, que es lo que estamos midiendo con ese script DTrace. Como no tengo configurado aún un ZIL en la SSD, el ZIL (en realidad uno por cada "dataset") se guarda en los discos duros del propio ZPOOL.
En este caso la transacción más "cargada" generó unos 70MB en 30 segundos, aunque el tráfico medio sea muy inferior. Y 540 transacciones. La flash puede absorber fácilmente 5.000 transacciones por segundo, mientras que el disco duro, con suerte, llega a 120 transacciones por segundo (y eso si usas ZFS, que es muy eficiente al respecto).
Digamos que nuestro tráfico medio son 70MB por 30 segundos. ¿Qué tamaño necesitamos para el ZIL?. Pues del orden de 2-3 veces más que el tráfico medio (estamos recopilando una transacción nueva mientras estamos grabando la anterior). En este caso iríamos sobradísimos con 200MB.
Como puede verse, el tamaño del ZIL puede ser pequeño, pero la diferencia de rendimiento es brutal.
En mi servidor tengo dos SSD's de 40GB. Los particiono de la siguiente forma, como explico en otro documento: 8 GB como partición de recuperación (con OpenIndiana), 2 GB como ZIL y 27GB para el L2ARC.
Lo primero que hago es comparar el tráfico medio sostenido en lecturas aleatorias, en el disco duro y en el SSD. Usaré el siguiente programa en Python:
#!/usr/bin/env python import sys, threading, Queue, time, random timeout = 10 done = False if len(sys.argv) != 4+1 : print >>sys.stderr, "%s device maxsize num_threads blocksize" %sys.argv[0] sys.exit(1) device, maxsize, num_threads, blocksize = sys.argv[1:] maxsize = int(maxsize)*1024*1024*1024 num_threads = int(num_threads) blocksize = int(blocksize) q = Queue.Queue() def worker() : f = open(device, "rb") rnd = random.Random() num_blocks = maxsize//blocksize n = 0 while not done : f.seek(blocksize*rnd.randrange(0, num_blocks)) a = f.read(blocksize) n += 1 q.put(n) t = [] for i in xrange(num_threads) : i = threading.Thread(target = worker) i.setDaemon(True) i.start() t.append(i) time.sleep(timeout) done = True transfered = 0 for i in t : i.join() transfered += q.get() bps = transfered/(timeout+0.0) print "%.1f bloques/s, %.1f bytes/s" %(bps, blocksize*bps)
Para comprobar el disco duro, quito una unidad del "mirror", para que el tráfico "normal" no afecte a la medida. Ejecuto el programa sobre el disco duro "offline":
[root@XXX datos]# zpool offline -t datos c4t3d0s0 [root@XXX datos]# zpool status pool: datos state: DEGRADED status: One or more devices has been taken offline by the administrator. Sufficient replicas exist for the pool to continue functioning in a degraded state. action: Online the device using 'zpool online' or replace the device with 'zpool replace'. scan: scrub repaired 0 in 8h7m with 0 errors on Sun Jan 1 02:34:18 2012 config: NAME STATE READ WRITE CKSUM datos DEGRADED 0 0 0 mirror-0 DEGRADED 0 0 0 c4t2d0s0 ONLINE 0 0 0 c4t3d0s0 OFFLINE 0 0 0 errors: No known data errors
Una vez que terminamos con el disco duro, restauro el "mirror", que ya me estaba poniendo nervioso:
[root@XXX datos]# zpool online datos c4t3d0s0 [root@XXX datos]# zpool status pool: datos state: ONLINE scan: resilvered 258M in 0h1m with 0 errors on Thu Jan 5 21:26:19 2012 config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 c4t2d0s0 ONLINE 0 0 0 c4t3d0s0 ONLINE 0 0 0 errors: No known data errors
Obsérverse como ZFS realiza una sincronización incremental de los discos en espejo, muy eficiente.
512 bytes | 1 Kbyte | 2 Kbytes | 4 Kbytes | 8 Kbytes | 16 Kbytes | 32 Kbytes | 64 Kbytes | 128 Kbytes | 256 Kbytes | 512 Kbytes | 1 Mbytes | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
Disco duro | 32/62 | 65/127 | 128/258 | 261/516 | 526/1035 | 1001/1946 | 1986/3185 | 3867/4862 | 7209/- | 13029/- | 22073/- | 32506/- |
Unidad SSD | 3907/14853 | 7800/29733 | 15606/59474 | 30818/119419 | 61095/239162 | 67066/238969 | 67777/239016 | 76454/239069 | 89129/239298 | 98382/239206 | 102813/239337 | 104858/240543 |
La tabla muestra dos valores por casilla: la velocidad de transferencia con un único hilo, y la velocidad de transferencia óptima lanzando varios hilos para proporcionar varias peticiones al sistema operativo y al disco duro, para que pueda procesarlas y reordenarlas como les parezca. No indico el número de hilos para llegar a ese valor óptimo, pero suele rondar los 32 hilos en paralelo. Si indico "-" significa que lanzar más de un hilo es contraproducente, porque las peticiones se hacen competencia entre sí. Es el caso de las transferencias largas, donde si hay dos peticiones concurrentes, el disco duro mueve el cabezal entre ellas, en vez de realizar una larga transferencia sin movimiento mecánico.
En el caso de la memoria SSD la penalización de las transferencias cortas es muy evidente también, aunque se recupera muy rápido. Asimismo, lanzando unos pocos hilos es muy fácil llegar al límite de transferencia de 240MB/s. Otro detalle interesante es que muchas peticiones concurrentes no afectan demasiado al rendimiento, lo que es consistente con el hecho de que las SSD no tengan "seek time".
El disco duro tiene una velocidad de transferencia que varía por la posición de la cabeza (en la parte exterior del disco, la transferencia es más rápida). Además, para obtener la velocidad máxima de transferencia es necesario que la cabeza no se mueva, así que la ráfaga a transferir debe ser muy larga. En cuanto hay competencia por el acceso a disco, el rendimiento cae. Con un tamaño de bloque de 128KB en ZFS, en el peor de los casos estaríamos utilizando apenas el 9% del ancho de banda del disco duro.
Quiero señalar, no obstante, que estas cifras representan el peor de los casos para el disco duro, y que no son representativas del rendimiento típico. En la práctica suele existir bastante localidad de referencia y el rendimiento es mucho mejor. Estas cifras representan el caso pesimista de tráfico aleatorio de disco, que solo se cumpliría en la práctica en casos extremos, como una base de datos inmensa (cientos de gigabytes) con un acceso aleatorio a la misma. De hecho en condiciones normales se llega fácilmente a los 100-120MB/s por disco. 240MB/s con dos discos.
Activemos ahora L2ARC. Como he comentado en la sección correspondiente, no utilizo redundancia porque no aporta nada y es preferible incrementar su capacidad y ancho de banda:
[root@XXX datos]# zpool add datos cache c4t0d0s2 [root@XXX datos]# zpool add datos cache c4t1d0s2 [root@XXX datos]# pool: datos state: ONLINE scan: resilvered 258M in 0h1m with 0 errors on Thu Jan 5 21:26:19 2012 config: NAME STATE READ WRITE CKSUM datos ONLINE 0 0 0 mirror-0 ONLINE 0 0 0 c4t2d0s0 ONLINE 0 0 0 c4t3d0s0 ONLINE 0 0 0 cache c4t0d0s2 ONLINE 0 0 0 c4t1d0s2 ONLINE 0 0 0 errors: No known data errors [root@XXX /]# kstat zfs::arcstats module: zfs instance: 0 name: arcstats class: misc c 9981439403 c_max 24678334464 c_min 3084791808 crtime 77.32556706 data_size 8532319744 deleted 19217837 demand_data_hits 4442792691 demand_data_misses 5064197 demand_metadata_hits 9178662256 demand_metadata_misses 3217339 evict_l2_cached 723968 evict_l2_eligible 741436190720 evict_l2_ineligible 42276615680 evict_skip 2159701 hash_chain_max 15 hash_chains 305196 hash_collisions 327132917 hash_elements 1025709 hash_elements_max 1587374 hdr_size 201643584 hits 13654205277 l2_abort_lowmem 0 l2_cksum_bad 0 l2_evict_lock_retry 0 l2_evict_reading 0 l2_feeds 388 l2_free_on_write 0 l2_hdr_size 0 l2_hits 0 l2_io_error 0 l2_misses 289 l2_read_bytes 0 l2_rw_clash 0 l2_size 54472192 l2_write_bytes 54947328 l2_writes_done 30 l2_writes_error 0 l2_writes_hdr_miss 0 l2_writes_sent 30 memory_throttle_count 0 mfu_ghost_hits 4095269 mfu_hits 13371779494 misses 14487113 mru_ghost_hits 3116107 mru_hits 249839090 mutex_miss 40442 other_size 1229508512 p 9103037846 prefetch_data_hits 5758589 prefetch_data_misses 5117053 prefetch_metadata_hits 26991741 prefetch_metadata_misses 1088524 recycle_miss 859124 size 9963471840 snaptime 8630741.47083925
Vemos que Solaris detecta el L2ARC y que está empezando a usarlo, poco a poco, a medida que debe desalojar elementos del ARC en RAM para hacer sitio a datos nuevos. Ahora hay que darle tiempo para que se vaya llenando.
Para ocuparnos del ZIL, primero vemos la velocidad de escritura síncrona actual. El programa en Python que utilizaré es el siguiente:
Este programa nos indica el número de transacciones por segundo y bytes por segundo que nos permite realizar el ZIL. El juego con "urandom" es para evitar suspicacias debido a la compresión transparente que realiza ZFS, otra de sus muchas ventajas. Se observa que para tamaños pequeños de escritura, de menos de 64Kbytes, las escrituras son muy rápidas, en los límites físicos de una única escritura en el disco duro. Para tamaños de escritura altos, la estrategia del ZIL cambia y el rendimiento desciende bastante, aunque sigue siendo bastante superior a otros sistemas operativos.import os, time for i in xrange(21) : l = 2**i m = 2<<20 d = open("/dev/urandom").read(m) assert len(d) == m f = open("zzz", "w") t = time.time() fd = f.fileno() p = 0 count = 0 while time.time()-t < 10 : count += 1 if p >= m : p = 0 f.write(d[p:p+l]) f.flush() os.fsync(fd) p += l t = time.time()-t print "%d bytes: %.1f bytes/s, %.1f transacciones por segundo" \ %(l, count*l/t, count/t)
Con mi configuración actual, el programa me ofrece la siguiente salida:
1 bytes: 114.4 bytes/s, 114.4 transacciones por segundo 2 bytes: 196.6 bytes/s, 98.3 transacciones por segundo 4 bytes: 473.8 bytes/s, 118.4 transacciones por segundo 8 bytes: 953.1 bytes/s, 119.1 transacciones por segundo 16 bytes: 1579.0 bytes/s, 98.7 transacciones por segundo 32 bytes: 3813.3 bytes/s, 119.2 transacciones por segundo 64 bytes: 7467.6 bytes/s, 116.7 transacciones por segundo 128 bytes: 12806.7 bytes/s, 100.1 transacciones por segundo 256 bytes: 29325.7 bytes/s, 114.6 transacciones por segundo 512 bytes: 61007.7 bytes/s, 119.2 transacciones por segundo 1024 bytes: 107706.5 bytes/s, 105.2 transacciones por segundo 2048 bytes: 243773.0 bytes/s, 119.0 transacciones por segundo 4096 bytes: 478810.5 bytes/s, 116.9 transacciones por segundo 8192 bytes: 799350.4 bytes/s, 97.6 transacciones por segundo 16384 bytes: 1854843.4 bytes/s, 113.2 transacciones por segundo 32768 bytes: 3710380.4 bytes/s, 113.2 transacciones por segundo 65536 bytes: 2916061.9 bytes/s, 44.5 transacciones por segundo 131072 bytes: 6803879.4 bytes/s, 51.9 transacciones por segundo 262144 bytes: 9944467.1 bytes/s, 37.9 transacciones por segundo 524288 bytes: 16451763.6 bytes/s, 31.4 transacciones por segundo 1048576 bytes: 27946452.0 bytes/s, 26.7 transacciones por segundo
Por comparación, esto es lo que veo en un ordenador moderno con Ubuntu 10.04 y sistema de ficheros EXT4:
1 bytes: 20.1 bytes/s, 20.1 transacciones por segundo 2 bytes: 40.6 bytes/s, 20.3 transacciones por segundo 4 bytes: 81.1 bytes/s, 20.3 transacciones por segundo 8 bytes: 155.5 bytes/s, 19.4 transacciones por segundo 16 bytes: 289.3 bytes/s, 18.1 transacciones por segundo 32 bytes: 639.3 bytes/s, 20.0 transacciones por segundo 64 bytes: 1207.0 bytes/s, 18.9 transacciones por segundo 128 bytes: 2372.5 bytes/s, 18.5 transacciones por segundo 256 bytes: 4921.7 bytes/s, 19.2 transacciones por segundo 512 bytes: 10682.4 bytes/s, 20.9 transacciones por segundo 1024 bytes: 19443.0 bytes/s, 19.0 transacciones por segundo 2048 bytes: 40720.4 bytes/s, 19.9 transacciones por segundo 4096 bytes: 78898.7 bytes/s, 19.3 transacciones por segundo 8192 bytes: 154574.1 bytes/s, 18.9 transacciones por segundo 16384 bytes: 282820.3 bytes/s, 17.3 transacciones por segundo 32768 bytes: 570933.1 bytes/s, 17.4 transacciones por segundo 65536 bytes: 1266590.2 bytes/s, 19.3 transacciones por segundo 131072 bytes: 2300879.1 bytes/s, 17.6 transacciones por segundo 262144 bytes: 4195885.0 bytes/s, 16.0 transacciones por segundo 524288 bytes: 6912373.8 bytes/s, 13.2 transacciones por segundo 1048576 bytes: 12640044.4 bytes/s, 12.1 transacciones por segundo
Además de que ZFS duplica el rendimiento en el peor de los casos, el movimiento de cabezal se ve también reducido (por el diseño del ZIL y por la arquitectura CoW de ZFS), así que en caso de competencia por el acceso a disco duro la ventaja del ZFS será incluso mayor.
Voy ahora a activar el ZIL en las SSD. Por lo ya explicado en su sección, lo haré como "mirror":
[root@XXX datos]# zpool add datos log mirror c4t0d0s1 c4t1d0s1 cannot add to 'datos': root pool can not have multiple vdevs or separate logs [root@XXX datos]# zpool upgrade This system is currently running ZFS pool version 29. All pools are formatted using this version.
Vaya, esto sí que es completamente inesperado. No puedo activar un ZIL separado en el "zpool" de arranque del sistema operativo. Tiene su lógica, porque mientras arranca el sistema operativo los recursos de los que disponemos son escasos y frágiles, y este soporte supone complejidad adicional. Pero no deja de ser decepcionante. Mi gozo en un pozo... de momento.
En aras de la demostración voy a crear un "zpool" nuevo en un ZVOLUME del "zpool" actual. A ver si funciona:
[root@XXX datos]# zfs create -V 10G datos/prueba [root@XXX datos]# zpool create datos2 /dev/zvol/dsk/datos/prueba [root@XXX datos]# mv rendimiento_ZIL.py /datos2 [root@XXX datos]# cd /datos2 [root@XXX datos2]# zpool status datos2 pool: datos2 state: ONLINE scan: none requested config: NAME STATE READ WRITE CKSUM datos2 ONLINE 0 0 0 /dev/zvol/dsk/datos/prueba ONLINE 0 0 0 errors: No known data errors [root@XXX datos2]# python rendimiento_ZIL.py 1 bytes: 111.7 bytes/s, 111.7 transacciones por segundo 2 bytes: 202.5 bytes/s, 101.2 transacciones por segundo 4 bytes: 459.4 bytes/s, 114.9 transacciones por segundo 8 bytes: 910.7 bytes/s, 113.8 transacciones por segundo 16 bytes: 1547.2 bytes/s, 96.7 transacciones por segundo 32 bytes: 3647.4 bytes/s, 114.0 transacciones por segundo 64 bytes: 7339.9 bytes/s, 114.7 transacciones por segundo 128 bytes: 12616.5 bytes/s, 98.6 transacciones por segundo 256 bytes: 28840.0 bytes/s, 112.7 transacciones por segundo 512 bytes: 60024.9 bytes/s, 117.2 transacciones por segundo 1024 bytes: 103763.8 bytes/s, 101.3 transacciones por segundo 2048 bytes: 230377.5 bytes/s, 112.5 transacciones por segundo 4096 bytes: 475515.8 bytes/s, 116.1 transacciones por segundo 8192 bytes: 750557.2 bytes/s, 91.6 transacciones por segundo 16384 bytes: 1655206.8 bytes/s, 101.0 transacciones por segundo 32768 bytes: 3387881.2 bytes/s, 103.4 transacciones por segundo 65536 bytes: 1716648.9 bytes/s, 26.2 transacciones por segundo 131072 bytes: 5804253.9 bytes/s, 44.3 transacciones por segundo 262144 bytes: 7798288.7 bytes/s, 29.7 transacciones por segundo 524288 bytes: 4254491.2 bytes/s, 8.1 transacciones por segundo 1048576 bytes: 12577264.9 bytes/s, 12.0 transacciones por segundo [root@XXX /]# zpool status datos2 pool: datos2 state: ONLINE scan: resilvered 0 in 0h0m with 0 errors on Fri Jan 6 03:53:59 2012 config: NAME STATE READ WRITE CKSUM datos2 ONLINE 0 0 0 /dev/zvol/dsk/datos/prueba ONLINE 0 0 0 logs c4t0d0s1 ONLINE 0 0 0 errors: No known data errors [root@XXX datos2]# python rendimiento_ZIL.py 1 bytes: 5135.9 bytes/s, 5135.9 transacciones por segundo 2 bytes: 10797.5 bytes/s, 5398.7 transacciones por segundo 4 bytes: 21472.7 bytes/s, 5368.2 transacciones por segundo 8 bytes: 43205.3 bytes/s, 5400.7 transacciones por segundo 16 bytes: 87860.8 bytes/s, 5491.3 transacciones por segundo 32 bytes: 168635.6 bytes/s, 5269.9 transacciones por segundo 64 bytes: 342234.8 bytes/s, 5347.4 transacciones por segundo 128 bytes: 693206.5 bytes/s, 5415.7 transacciones por segundo 256 bytes: 1346381.5 bytes/s, 5259.3 transacciones por segundo 512 bytes: 2737607.1 bytes/s, 5346.9 transacciones por segundo 1024 bytes: 5483587.9 bytes/s, 5355.1 transacciones por segundo 2048 bytes: 10894478.8 bytes/s, 5319.6 transacciones por segundo 4096 bytes: 10068762.0 bytes/s, 2458.2 transacciones por segundo 8192 bytes: 28521978.3 bytes/s, 3481.7 transacciones por segundo 16384 bytes: 2235868.9 bytes/s, 136.5 transacciones por segundo 32768 bytes: 34653658.7 bytes/s, 1057.5 transacciones por segundo 65536 bytes: 4002900.2 bytes/s, 61.1 transacciones por segundo 131072 bytes: 12414994.3 bytes/s, 94.7 transacciones por segundo 262144 bytes: 33842062.6 bytes/s, 129.1 transacciones por segundo 524288 bytes: 12736360.5 bytes/s, 24.3 transacciones por segundo 1048576 bytes: 11944777.9 bytes/s, 11.4 transacciones por segundo [root@XXX /]# zpool attach datos2 c4t0d0s1 c4t1d0s1 [root@XXX /]# zpool status datos2 pool: datos2 state: ONLINE scan: resilvered 0 in 0h0m with 0 errors on Fri Jan 6 04:10:11 2012 config: NAME STATE READ WRITE CKSUM datos2 ONLINE 0 0 0 /dev/zvol/dsk/datos/prueba ONLINE 0 0 0 logs mirror-1 ONLINE 0 0 0 c4t0d0s1 ONLINE 0 0 0 c4t1d0s1 ONLINE 0 0 0 errors: No known data errors [root@XXX datos2]# python rendimiento_ZIL.py 1 bytes: 4510.6 bytes/s, 4510.6 transacciones por segundo 2 bytes: 8916.0 bytes/s, 4458.0 transacciones por segundo 4 bytes: 18884.8 bytes/s, 4721.2 transacciones por segundo 8 bytes: 37616.0 bytes/s, 4702.0 transacciones por segundo 16 bytes: 73822.1 bytes/s, 4613.9 transacciones por segundo 32 bytes: 140470.9 bytes/s, 4389.7 transacciones por segundo 64 bytes: 284437.2 bytes/s, 4444.3 transacciones por segundo 128 bytes: 601880.1 bytes/s, 4702.2 transacciones por segundo 256 bytes: 1108514.7 bytes/s, 4330.1 transacciones por segundo 512 bytes: 2371158.3 bytes/s, 4631.2 transacciones por segundo 1024 bytes: 4550019.3 bytes/s, 4443.4 transacciones por segundo 2048 bytes: 9164521.8 bytes/s, 4474.9 transacciones por segundo 4096 bytes: 13783347.7 bytes/s, 3365.1 transacciones por segundo 8192 bytes: 25303757.2 bytes/s, 3088.8 transacciones por segundo 16384 bytes: 5225476.4 bytes/s, 318.9 transacciones por segundo 32768 bytes: 34207540.9 bytes/s, 1043.9 transacciones por segundo 65536 bytes: 4882781.9 bytes/s, 74.5 transacciones por segundo 131072 bytes: 36812441.0 bytes/s, 280.9 transacciones por segundo 262144 bytes: 4338026.7 bytes/s, 16.5 transacciones por segundo 524288 bytes: 20352716.7 bytes/s, 38.8 transacciones por segundo 1048576 bytes: 17280271.9 bytes/s, 16.5 transacciones por segundo
Advertimos varias cosas:
Ahora toca pensar en separar el ZPOOL en dos: "sistema" y "datos", para poder beneficiarme al 100% de esta tecnología...
Problema solucionado: Solaris 10 y ZFS: Separación de un ZPOOL unificado en producción, en ZPOOLs de"sistema" y "datos".
La adición del ZIL en "mirror" en simple:
Veamos el rendimiento. Recordemos que la máquina está en producción con bastante actividad, así que el resultado es errático pero representativo:[root@XXX /]# zpool add datos log mirror c4t0d0s1 c4t1d0s1
1 bytes: 4329.8 bytes/s, 4329.8 transacciones por segundo 2 bytes: 8959.7 bytes/s, 4479.9 transacciones por segundo 4 bytes: 16496.5 bytes/s, 4124.1 transacciones por segundo 8 bytes: 33868.1 bytes/s, 4233.5 transacciones por segundo 16 bytes: 63228.8 bytes/s, 3951.8 transacciones por segundo 32 bytes: 126826.2 bytes/s, 3963.3 transacciones por segundo 64 bytes: 245802.5 bytes/s, 3840.7 transacciones por segundo 128 bytes: 509778.8 bytes/s, 3982.6 transacciones por segundo 256 bytes: 1089404.4 bytes/s, 4255.5 transacciones por segundo 512 bytes: 1870344.2 bytes/s, 3653.0 transacciones por segundo 1024 bytes: 3845308.7 bytes/s, 3755.2 transacciones por segundo 2048 bytes: 8500152.5 bytes/s, 4150.5 transacciones por segundo 4096 bytes: 12163273.7 bytes/s, 2969.5 transacciones por segundo 8192 bytes: 19263422.3 bytes/s, 2351.5 transacciones por segundo 16384 bytes: 24046256.0 bytes/s, 1467.7 transacciones por segundo 32768 bytes: 20473232.0 bytes/s, 624.8 transacciones por segundo 65536 bytes: 11248985.6 bytes/s, 171.6 transacciones por segundo 131072 bytes: 17431207.5 bytes/s, 133.0 transacciones por segundo 262144 bytes: 21972560.3 bytes/s, 83.8 transacciones por segundo 524288 bytes: 25369483.4 bytes/s, 48.4 transacciones por segundo 1048576 bytes: 25659356.7 bytes/s, 24.5 transacciones por segundo
Más información:
ERRATA: El L2ARC no se llena con lo que desborda del ARC de forma síncrona, sino que hay un hilo "feeder" que se ejecuta de vez en cuando y revisa los extremos de las colas ARC, es decir los objetos que posiblemente se tiren pronto, y los copia en el L2ARC. Por tanto, no todo lo que sale del ARC acaba en el L2ARC, y bajo extrema presión en el ARC, el L2ARC no se ve saturado. Controlando la frecuencia del "feeder", controlamos todo. Los detalles son interesantes.
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS