proyecto del billón de tablas - pgday ecuador 2013
DESCRIPTION
¿Qué es una base de datos "grande"? ¿Cuántas tablas puede contener una base de datos PostgreSQL? ¿Hay un límite? ¿Es posible crear 10^9, un billón, de tablas en una base de datos? Charla impartida en PgDay Ecuador 2013, donde se habló de bases de datos grandes, alto rendimiento, 150.000 inserciones por segundo en un único nodo y, por supuesto... si en NOSYS llegamos a crear 1.000.000.000 tablas en una base de datos y cuántos $$$$ nos costó ejecutar el experimento en Amazon AWS.TRANSCRIPT
El Proyecto del Billón de Tablas
Álvaro Hernández Tortosa <[email protected]>
Acerca de mí
● Álvaro Hernández Tortosa <[email protected]>● Fundador y Director Técnico en NOSYS● ¿Qué hacemos en NOSYS?
✔ Formación, consultoría y desarrollo de software con PostgreSQL (y Java)✔ Partners de EnterpriseDB✔ Formación avanzada en Java con Javaspecialists.eu: Java Master Course y Java Concurrency Course✔ Partners de Amazon AWS. Formación y consultoría en AWS
● Twitter: @ahachete● LinkedIn: http://es.linkedin.com/in/alvarohernandeztortosa/
¿Qué es una base de datos “grande”?
● Bases de datos de un sólo nodo: hasta TBs o decenas de Tbs. Miles de millones a billones de registros.
● Bases de datos multi nodo, virtualmente ilimitadas. Casos conocidos de centenares de TBs e incluso Pbs.
● Esta charla no es acerca de Big Data. Es sólo de Big Data
● De hecho, aquí realmente hablamos de Big MetaData(y el peor ratio metadata/data de la historia)
Tipos de bases de datos (por número de tablas)
Base de datos # Tablas
SLST Schema-Less-Like, Single-Table 1
EDNECRM Extremely De-Normalized Enterprise CRM 2
S Small 20
M Medium 80
L Large 200
XL Extra Large 1,000
ORMGW ORMs Gone Wild 5,000
MT Multi-Tenancy 50,000
MMT Massive Multi-Tenancy 1,000,000
BTP Billion Tables Project 1,000,000,000
Tipos de bases de datos (II)
SLST ENNECRM S M L XL ORMGW MT MMT BTP0
10
20
30
40
50
60
70
80
90
100
Number of tables by database type
10
log
_1
0 (
# ta
ble
s)
Límites teóricos de PostgreSQL
Característica Límite
# columnas / tabla 250-1600 (dependiendo del tipo de las columnas)
Max tamaño / columna 1GB
Max tamaño fila 1.6 TB
Max # filas / tabla ilimitado
Max tamaño / tabla 32 TB
Max # tablas / bbdd ilimitado
Max tamaño / bbdd ilimitado
Dónde empezó todo...
● En 2002, un mail a [email protected]:
“I'm guessing that the maximum number of tables is related to how much can be stored in the pg_ tables […]. So, based on that, the maximum number of rows is unlimited and the maximum size for a table is 64 TB. So realistically, you would need an enormous number (trillions) of tables to exceed that limit”
Simon Cawleyhttp://www.postgresql.org/message-id/53386E0C47E7D41194BB0002B325C997747F2B@NTEX60
Dónde empezó todo... (II)
http://it.toolbox.com/blogs/database-soup/one-billion-tables-or-bust-46270
21 de Mayo de 2011
¿Por qué hacer 1BT?
Ofc
ialm
ente
...
En rea
lidad
...
● Demostrar que PostgreSQL no? tiene límite en el # tablas
● Someter a Postgres a stress de forma no habitual
● Probar un nuevo servidor antes de producción
● Para ganar a Josh Berkus, creando tablas más rápido que él
● “Yo la tengo más grande que tú” (la bbdd)
● Porque podemos
Redefiniendo “tps”
Wikipedia (http://en.wikipedia.org/wiki/Transactions_per_second):
“Transactions Per Second refers to the number of atomic actions performed by certain entity per second”
En lo sucesivo, en esta presentación, querrá decir:
“tablas por segundo”
Primeros intentos (2011)
● Josh Berkus(http://it.toolbox.com/blogs/database-soup/one-billion-tables-or-bust-46270): 3M tablas, 83 tps. PostgreSQL murió (sin disco). Serial + text
● Jan Urbanski(http://it.toolbox.com/blogs/database-soup/one-billion-tables-part-2-46349): 4.6M tablas, 1K tps. PostgreSQL murió (inodes). Int + text
● $SELF(http://it.toolbox.com/blogs/database-soup/one-billion-tables-part-2-46349): 10M tablas, 2K2 tps. Finalziación correcta. 1 columna int100M tablas, 1K5 tps. Finalziación correcta. 1 columna int
100M tablas. ¿Cómo lograrlo?
● Necesitamos RAM:Out of memory: kill process 4143 (postgres) score 235387 or a childKilled process 4146 (postgres)
● Un sistema de ficheros que pueda manejar muchos ficheros: reiserfs
● Estrategia de creación de tablas:➔ No usar un fichero CSV o .sql pre-generado➔ No usar un driver a través de TCP/IP➔ Solución: enviar comandos SQL vía la entrada
estándar, con psql sobre sockets UNIX
100M tables. How to get there? (II)
Configurar postgresql.conf:
fsync = ofsynchronous_commit = offull_page_writes = ofwal_bufers = 256MBautovacuum = ofmax_locks_per_transaction = 10000shared_bufers = 16384MBcheckpoint_segments = 128
100M tables. How to get there? (III)
Primer servidor probado:
● Intel Core 2 CPU● 4GB RAM● 3X 1TB SATA 7K2 rpm, RAID 0● Reiserfs● Ubuntu 10.04● PostgreSQL 9.0
100M tablas. El código
100M tablas. Los resultados
510
1520
2530
3540
4550
5560
6570
7580
8590
95100
0
500
1000
1500
2000
2500
3000
100M tables
Intel Core 2, 4GB RAM, 3TB reiser
time (min)
speed (tps)
M tables
Uso de disco: 257GB
El camino a 1B tablas. Los peores enemigos
● Autovacuum(¿pero no estaba autovacuum = of ?)
autovacuum_freeze_max_age = 2000000000# maximum XID age before forced vacuum
● updatedb(¿quién activa esto por defecto en las distros??????)
El camino a 1B tablas. El almacenamiento
● Separar el directorio base del directorio de tablas
● Crear un tablespace (o más) en una partición reiserfs (llamada /data)
● El mejor rendimiento se obtuvo con base en XFS (/bigdata). El patrón es añadir, como una bbdd “normal”
● Los registros de WAL van a RAM (tmpfs con swap para evitar desbordamientos, montado en /xlog)
El camino a 1B tablas. El almacenamiento (II)
El camino a 1B tablas. Tablespaces
● Excepto reiserfs, otros sistemas de ficheros degradan rápidamente con el número de ficheros
● Incluso reiser pierde rendimiento con varios millones
● Solución: crear tantos tablespaces como sea necesario (incluso dentro del mismo sistema de ficheros)
● Para la prueba de 1B, usamos 1000 tablespaces para máximo rendimiento
El camino a 1B tablas. Una pizza más grande
● 2X Intel(R) Xeon(R) CPU E5-2650 @ 2.00GHz(16 cores, 32 threads)
● 48GB RAM
● Versión moderna de SO y Postgres:➔ Debian wheezy (kernel 3.2.41)➔ PostgreSQL 9.2.4
● Apenas unos segundos para hacer “make -j16” postgresql
El camino a 1B tablas.. Concurrencia
● La creación de tablas no limita por disco. El rendimiento medio fue de < 5MB/s en la prueba de 100M tablas
● Hay dos límites principales:➔ La velocidad de la CPU (los backends alcanzan el 100% si se ejecutan aisladamente)➔ La contención (locking)
● Para mejorar el rendimiento, lanzamos varios procesos en segundo plano
● 16 procesos fue la mejor combinación
El camino a 1B tablas. Concurrencia (II)
● Con múltiples procesos, no podemos permitir que cada proceso loguee sus propios datos (por la dificultad de agregar los datos)
● Ejecutamos otro proceso sólo para loguear los datos:➔ El logger tiene el PID de cada trabajador➔ Cuando el logger quiere loguear, envía una señal SIGUSR1 a los trabajadores➔ El logger espera la información en un fifo identificado por el PID del trabajador➔ El trabajador escribe el número de tablas que ha generado hasta la fecha
El camino a 1B tablas. El código
● El trabajador es un script en python:➔ Divide en interaciones el número de tablas asignado➔ En cada iteración, crea un proceso psql al que le manda el comando CREATE TABLE … TABLESPACE … a través de la entrada estándar➔ Cuando recibe la señal USR1, escribe el número de tablas actual al fifo➔ Sale cuando recibe (del logger) la señal TERM➔ Las iteraciones (I/O) se ejecutan en un thread
● El logger es un shell script que loguea cuando recibe USR1● Main es también un shell script. Lanza todos los procesos y manda al logger que loguee cada 10s (le manda USR1)
El camino a 1B tablas. El código (II)
btp-main.sh
btp-process.py
btp-logger.sh
1B tablas. ¿Funcionó?
$ time ./btp-main.sh 1000000000 16 50000 1000real 2022m19.961suser 240m7.044ssys 165m25.336s(esto es: 33h 42m 20s)
● Media: 8242tps
btp=# SELECT txid_current(); txid_current -------------- 1000001685
1B tablas. ¿Funcionó? (II)
$ echo -e '\\timing on\nSELECT count(*) FROM pg_class' |psql btp count------------ 1000000288Time: 9221642.102 ms
$ df -h /data /bigdata /var/tmpFilesystem Size Used Avail Use% Mounted on
/dev/mapper/vgMain-data 500G 97G 404G 20% /data
/dev/etherd/e15.0 5.5T 2.6T 3.0T 46% /bigdata
tmpfs 90G 4.1G 86G 5% /var/tmp
1B tablas. ¿Funcionó? (III)
btp=# SELECT relname, heap_blks_read, heap_blks_hit, idx_blks_read, idx_blks_hit FROM pg_statio_all_tables WHERE relname IN ('pg_tablespace', 'pg_database', 'pg_shdepend');
relname | heap_blks_read | heap_blks_hit | idx_blks_read | idx_blks_hit
---------------+----------------+---------------+---------------+--------------
pg_tablespace | 35 | 6226009368 | 13 | 6794
pg_database | 3 | 63015 | 12 | 105017
pg_shdepend | 1 | 1000001001 | 5 | 1001537778
btp=# INSERT INTO _3ade68b1 VALUES (2), (3);
Time: 20.673 ms
btp=# SELECT * FROM _3ade68b1 LIMIT 1;
[...]
Time: 0.207 ms
1B tablas. ¿Cuánto tiempo requiere un “\dt”?
$ time ./postgresql-9.2.4/bin/psql btp -c "\dt" > tables
∞ERROR: canceling statement due to user request
real 2993m51.710s
user 0m0.000s
sys 0m0.000s
Terminado vía pg_cancel_backend()
1B tablas. Rendimiento
2080
200260
300320
340380
440460
540600
620640
680860
1000
0
2000
4000
6000
8000
10000
12000
1B tables. Performance
Tables per second
tps
M tables
Pico: 10Ktps
1B tablas. Rendimiento (II)
Carga media de los backends: 57%Carga media del sistema: 11.7
2080
200260
300320
340380
440460
540600
620640
680860
1000
05000
1000015000200002500030000350004000045000
1B tables
Memory usage
mem free (MB)
buffers (MB)
Cached (MB)
M tables
Restaurando la durabilidad de la bbdd
● Detener el servidor y llevar pg_xlog a disco de nuevo
● Configurar postgresql.conf:
fsync = onsynchronous_commit = onfull_page_writes = onautovacuum = of
● Reiniciar el servidor. Y disfrutar :)
El camino a... 2B de tablas
● Una pizza aún mayor 2x Xeon quad-core de última generación a 3.3 Ghz➔ Más rápido que 16-core 2.0Ghz (8K5tps @100Mt):
✔ Con 16 procesos: 11K9 tps @100Mt✔ Con 8 procesos: 12K3 tps @100Mt
$ time ./btp-main.sh 1000000000 8 50000 1000real 1400m6.680suser 165m32.565ssys 119m46.493s(esto es: 23h 20m 07s)Media: 11903tps
El camino a... 2B de tablas (II)
● Necesidades de almacenamiento:➔ Base: ~6TB➔ Tablespaces: ~ 300GB
● El problema de autovacuum (otra vez):➔ autovacuum_freeze_max_age como máximo es
2E9➔ Autovacuum se lanzará. Si los matamos, postgres
los relanza➔ Antes hacíamos una tabla por transacción➔ Si agrupamos varias tablas creadas por
transacción, el rendimiento baja (curioso)
¿Qué tablas de catálogo escribe CREATE TABLE?
● Para una tabla de 1 columna, sin índices:➔ 1 entrada en pg_class➔ 2 entradas en pg_type (el tipo de la tabla y el tipo del array de tabla)➔ 3 entradas en pg_depend (2 para los tipos, 1 para el schema al que pertenece la tabla)➔ 7 entradas en pg_attribute (xmin, xmax, cmin, cmax, ctid, tableoid y la propia columna)
➔ Así que crear 11K9 tablas realmente quiere decir:➔ Hacer 154K inserciones por segundo➔ Más 11K9 fcheros creados por segundo(1MB/s de metainformación)
Entonces, ¿hay algún límite en el número de tablas?
● Tanto pg_class como pg_type tienen columnas oid
● Los oids son enteros de 32 bits sin signo
● Para cada tabla creada, se insertan 2 registros en pg_type
● Hay 330 tipos por defecto en 9.2
● Por lo tanto, hay un límite en el número máximo de tablas: (2^32 – 330) / 2 = 2_147_483_483
(2_147_483_481 en 9.3)
Validando empíricamente la teoría
● Objetivo: intentar crear 2_147_483_481 tablas en pg9.3
● Llegados allí, intentar crear una tabla más :)
● Hemos usado Amazon AWS para ello. Una instancia cr1.8xlarge (16 cores, 244GB RAM, 2x120GB SSD)
● Los tablespaces estaban en un RAID0 de los 2 SSDs
● Tablas de catálogo: RAID0 de 6x volúmenes de 1TB IOPSs no garantizados
Validando empíricamente la teoría: los costes
Amazon aportó créditos para la realización de estas pruebas. ¡Muchas gracias, Amazon!
$1,877.23
Validando empíricamente la teoría: las tablas
Validando empíricamente la teoría: las tablas (II)
Validando empíricamente la teoría: el resultado
2,147,475,456 tablas
(a 8025 del límite teórico)
Agradecimientos
● A Josh Berkus (y Selena Deckelmann, Jan Urbanski y Álvaro Herrara) que son los locos inventores de esta idea
● Grandes agradecimientos a José Luis Tallón:➔ Por proveer del hardware inicial y tunearlo
➔ Por co-crear, co-programar, co-organizar y co-disfrutar este proyecto conmigo
● A ASLE, patrocinadores del evento, PostgreSQL Global y especialmente a Jaime Casanova por el esfuerzoen trarme acá :)
El Proyecto del Billón de Tablas
Álvaro Hernández Tortosa <[email protected]>