Serialización y rendimiento de Javascript con V8 y PostgreSQL.
plv8 (5)
¿Quizás en lugar de responsabilizar a la fase de recuperación del análisis de los datos, crear un nuevo tipo de datos que pueda difundir previamente los datos json en la entrada podría ser un mejor enfoque?
http://www.postgresql.org/docs/9.2/static/sql-createtype.html
He estado experimentando con PostgreSQL y PL / V8 , que integra el motor V8 de JavaScript en PostgreSQL. Usando esto, puedo consultar datos JSON dentro de la base de datos, lo cual es bastante impresionante.
El enfoque básico es el siguiente:
CREATE or REPLACE FUNCTION
json_string(data json, key text) RETURNS TEXT AS $$
var data = JSON.parse(data);
return data[key];
$$ LANGUAGE plv8 IMMUTABLE STRICT;
SELECT id, data FROM things WHERE json_string(data,''name'') LIKE ''Z%'';
Usando, V8 puedo analizar datos JSON en JS, luego devolver un campo y puedo usar esto como una expresión de consulta pg regular.
PERO
En grandes conjuntos de datos, el rendimiento puede ser un problema, ya que para cada fila necesito analizar los datos. El analizador es rápido, pero definitivamente es la parte más lenta del proceso y tiene que suceder todas las veces.
Lo que estoy tratando de resolver (para finalmente llegar a una pregunta real) es si hay una manera de almacenar en caché o preprocesar el JSON ... incluso almacenando una representación binaria del JSON en la tabla que podría ser utilizada por V8 automáticamente como un objeto JS podría ser una victoria. He echado un vistazo al uso de un formato alternativo como messagepack o protobuf, pero no creo que sean necesariamente tan rápidos como el analizador JSON nativo en ningún caso.
PENSAMIENTO
PG tiene blobs y tipos binarios, por lo que los datos podrían almacenarse en binarios, entonces solo necesitamos una forma de convertir esto en V8.
La versión corta parece ser que con el nuevo soporte de json
Pg, hasta ahora no hay manera de almacenar json directamente en ninguna otra forma que no sea el texto serializado de json. (Esto parece probable que cambie en 9.4)
Parece que desea almacenar un formulario pre-analizado que es una representación serializada de cómo v8 representa el json en la memoria, y eso no es compatible actualmente. Ni siquiera está claro que v8 ofrezca algún tipo de serialización / deserialización binaria de estructuras json. Si no lo hace de forma nativa, el código debería agregarse a Pg para producir tal representación y volver a convertirla en estructuras de datos v8 json.
Tampoco sería necesariamente más rápido:
Si
json
se almacenó en un formato binario específico de v8, las consultas que devolvieron la representación json normal a los clientes tendrían que formatearlo cada vez que se devolviera, incurriendo en un costo de CPU.Una versión serializada binaria de json no es lo mismo que almacenar las estructuras de datos v8 json directamente en la memoria. No se puede escribir una estructura de datos que involucre cualquier tipo de gráfico de punteros al disco directamente, tiene que ser serializado. Esta serialización y deserialización tiene un costo, y puede que ni siquiera sea mucho más rápido que analizar la representación del texto json. Depende mucho de cómo v8 represente los objetos JavaScript en la memoria.
La representación serializada binaria podría fácilmente ser más grande, ya que la mayoría de json es texto y números pequeños, donde no se obtiene ninguna compacidad de una representación binaria. Dado que el tamaño de almacenamiento afecta directamente a la velocidad de las exploraciones de tablas, las recuperaciones de valores de TOAST, el tiempo de descompresión requerido para los valores de TOASTed, los tamaños de los índices, etc., podría llegar fácilmente con consultas más lentas y tablas más grandes.
Me interesaría ver si es posible una optimización como la que usted describe, y si resultaría ser una optimización.
Para obtener los beneficios que desea al realizar escaneos de tablas, creo que lo que realmente necesita es un formato que se pueda recorrer sin tener que analizarlo y convertirlo en lo que probablemente sea un gráfico malloc () de objetos javascript. Desea poder dar una expresión de ruta para un campo y extraerla directamente del formulario serializado donde se ha leído en un búfer de lectura Pg o en shared_buffers. Ese sería un proyecto de diseño muy interesante, pero me sorprendería si existiera algo parecido en v8.
Lo que realmente debe hacer es investigar cómo las bases de datos de objetos basadas en json existentes realizan búsquedas rápidas de rutas de json arbitrarias y cuáles son sus representaciones en disco, luego informen a los piratas informáticos pgsql. Tal vez haya algo que aprender de las personas que ya han resuelto esto, suponiendo, por supuesto, que lo hayan hecho.
Mientras tanto, en lo que me gustaría centrarme es en lo que están haciendo las otras respuestas aquí: trabajar alrededor del punto lento y encontrar otras formas de hacer lo que necesita. También puede buscar ayudar a optimizar el analizador json, pero dependiendo de si el v8 o algún otro está en uso, es posible que ya haya pasado el punto de los rendimientos decrecientes.
Supongo que esta es una de las áreas donde hay un equilibrio entre la velocidad y la representación flexible de datos.
No sé si sería útil aquí, pero encontré esto: pg-to-json-serializer . Menciona funcionalidad para:
analizar cadenas JSON y rellenar registros / matrices postgreSQL desde él
No sé si ofrecería algún beneficio de rendimiento sobre lo que has estado haciendo hasta ahora, y realmente no entiendo sus ejemplos.
Solo pensé que valía la pena mencionarlo.
No tengo ninguna experiencia con esto, pero me dio curiosidad, así que hice algunas lecturas.
Solo JSON
¿Qué pasa con algo como lo siguiente (sin probar, por cierto)? No responde a su pregunta sobre el almacenamiento de una representación binaria del JSON, es un intento de analizar todo el JSON a la vez para todas las filas que está verificando, con la esperanza de que produzca un mayor rendimiento al reducir el procesamiento. Gastos generales de hacerlo individualmente para cada fila. Sin embargo, si tiene éxito, estoy pensando que puede resultar en un mayor consumo de memoria.
Las cosas de CREATE TYPE...set_of_records()
se adaptan del ejemplo en la wiki donde se menciona que "También puedes devolver registros con una matriz de JSON". Supongo que realmente significa "una matriz de objetos".
¿El valor de id
del registro de base de datos está incorporado en el JSON?
Versión 1
CREATE TYPE rec AS (id integer, data text, name text);
CREATE FUNCTION set_of_records() RETURNS SETOF rec AS
$$
var records = plv8.execute( "SELECT id, data FROM things" );
var data = [];
// Use for loop instead if better performance
records.forEach( function ( rec, i, arr ) {
data.push( rec.data );
} );
data = "[" + data.join( "," ) + "]";
data = JSON.parse( data );
records.forEach( function ( rec, i, arr ) {
rec.name = data[ i ].name;
} );
return records;
$$
LANGUAGE plv8;
SELECT id, data FROM set_of_records() WHERE name LIKE ''Z%''
Versión 2
Éste consigue que Postgres agregue / concatene algunos valores para reducir el procesamiento realizado en JS.
CREATE TYPE rec AS (id integer, data text, name text);
CREATE FUNCTION set_of_records() RETURNS SETOF rec AS
$$
var cols = plv8.execute(
"SELECT" +
"array_agg( id ORDER BY id ) AS id," +
"string_agg( data, '','' ORDER BY id ) AS data" +
"FROM things"
)[0];
cols.data = JSON.parse( "[" + cols.data + "]" );
var records = cols.id;
// Use for loop if better performance
records.forEach( function ( id, i, arr ) {
arr[ i ] = {
id : id,
data : cols.data[ i ],
name : cols.data[ i ].name
};
} );
return records;
$$
LANGUAGE plv8;
SELECT id, data FROM set_of_records() WHERE name LIKE ''Z%''
hstore
¿Cómo sería el rendimiento de esta comparación ?: duplicar los datos JSON en una columna hstore en el momento de la escritura (o si el rendimiento logró ser lo suficientemente bueno, convierta el JSON en hstore a la hora seleccionada) y use el hstore en su WHERE
, por ejemplo :
SELECT id, data FROM things WHERE hstore_data -> name LIKE ''Z%''
He escuchado sobre hstore desde aquí: http://lwn.net/Articles/497069/
El artículo menciona algunas otras cosas interesantes:
PL / v8 te permite ... crear índices de expresión en elementos JSON específicos y guardarlos, brindándote índices de búsqueda almacenados como las "vistas" de CouchDB.
No da más detalles sobre eso y realmente no sé a qué se refiere.
Hay un comentario atribuido como "jberkus" que dice:
También hablamos de tener un tipo JSON binario, pero sin un protocolo para transmitir valores binarios (BSON no es en absoluto un estándar, y tiene algunas fallas graves), no parecía haber ningún punto.
Si está interesado en trabajar en el soporte JSON binario para PostgreSQL, nos interesaría que lo ayude ...
Postgres soporta índices en llamadas de funciones arbitrarias. El siguiente índice debería hacer el truco:
CREATE INDEX json_idx ON things (json_string(field,''name''));