Сериализация Javascript и производительность с V8 и PostgreSQL
Я экспериментировал с PostgreSQL и PL / V8, которые встраивают движок V8 JavaScript в PostgreSQL. Используя это, я могу запрашивать данные JSON внутри базы данных, что довольно здорово.
Основной подход заключается в следующем:
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%';
Используя V8, я могу анализировать данные JSON в JS, затем возвращать поле и использовать его как регулярное выражение запроса pg.
НО
На больших наборах данных производительность может быть проблемой, так как для каждой строки мне нужно анализировать данные. Парсер работает быстро, но это определенно самая медленная часть процесса, и это должно происходить каждый раз.
То, что я пытаюсь выяснить (чтобы, наконец, добраться до реального вопроса), есть ли способ кэширования или предварительной обработки JSON ... даже сохранение двоичного представления JSON в таблице, которая может использоваться V8 автоматически, как объект JS может быть победой. Я рассмотрел использование альтернативного формата, такого как пакет сообщений или protobuf, но я не думаю, что они обязательно будут такими же быстрыми, как и собственный анализатор JSON в любом случае.
ДУМАЛ
У PG есть двоичные объекты и двоичные типы, поэтому данные могут храниться в двоичном виде, тогда нам просто нужен способ перенести это в V8.
5 ответов
Postgres поддерживает индексы для произвольных вызовов функций. Следующий индекс должен сделать свое дело:
CREATE INDEX json_idx ON things (json_string(field,'name'));
Короткая версия, кажется, что с новым Pg json
поддержка, пока нет способа хранить JSON напрямую в любой форме, кроме сериализованного текста JSON. (Вероятно, это изменится в 9.4)
Кажется, вы хотите сохранить предварительно проанализированную форму, которая представляет собой сериализованное представление того, как v8 представляет json, в памяти, и это в настоящее время не поддерживается. Даже не ясно, что v8 предлагает какую-либо двоичную сериализацию / десериализацию структур json. Если это не происходит изначально, необходимо добавить код в Pg, чтобы получить такое представление и превратить его обратно в структуры данных vson json.
Это также не обязательно будет быстрее:
Если
json
был сохранен в специальной двоичной форме v8, запросы, которые возвращали обычное представление json клиентам, должны были бы отформатировать его каждый раз, когда он возвращался, что влечет за собой затраты ЦП.Бинарная сериализованная версия json - это не то же самое, что хранение структур данных v8 json непосредственно в памяти. Вы не можете записать структуру данных, которая включает в себя какой-либо граф указателей непосредственно на диск, она должна быть сериализована. Эта сериализация и десериализация имеет свою стоимость, и она может даже не быть намного быстрее, чем анализ текстового представления json. Это во многом зависит от того, как v8 представляет объекты JavaScript в памяти.
Бинарное сериализованное представление может легко быть больше, так как большинство json - это текст и маленькие числа, где вы не получаете никакой компактности от двоичного представления. Поскольку размер хранилища напрямую влияет на скорость сканирования таблиц, выборки значений из TOAST, время распаковки, необходимое для значений TOASTed, размеры индексов и т. Д., Вы можете легко получить доступ к более медленным запросам и таблицам большего размера.
Мне было бы интересно узнать, возможна ли оптимизация, подобная той, которую вы описываете, и окажется ли она вообще оптимизацией.
Чтобы получить преимущества, которые вы хотите получить при сканировании таблиц, я думаю, что вам действительно нужен формат, который можно просмотреть, не анализируя его и не превратив его в то, что, вероятно, представляет собой malloc() граф объектов javascript. Вы хотите иметь возможность дать выражение пути для поля и извлечь его непосредственно из сериализованной формы, где оно было прочитано в буфер чтения Pg или в shared_buffers. Это был бы действительно интересный дизайн-проект, но я бы удивился, если бы что-то подобное существовало в v8.
Что вам действительно нужно сделать, так это исследовать, как существующие объектные базы данных на основе json выполняют быстрый поиск произвольных путей json и каковы их представления на диске, а затем сообщают о pgsql-хакерах. Может быть, есть чему поучиться у людей, которые уже решили это - предполагая, конечно, что у них есть.
В то же время я хотел бы сосредоточиться на том, что делают другие ответы здесь: обойти медленную точку и найти другие способы сделать то, что вам нужно. Вы также можете попытаться оптимизировать анализатор json, но в зависимости от того, используется ли v8 или другой, который уже может дойти до точки снижения отдачи.
Я предполагаю, что это одна из областей, где есть компромисс между скоростью и гибким представлением данных.
У меня нет никакого опыта с этим, но это заинтересовало меня, поэтому я немного почитал.
Только JSON
Как насчет чего-то вроде следующего (непроверенного, кстати)? Это не решает ваш вопрос о сохранении двоичного представления JSON, это попытка проанализировать весь JSON сразу для всех проверяемых строк, в надежде, что это приведет к более высокой производительности за счет сокращения обработки накладные расходы делают это индивидуально для каждого ряда. Если это удастся, я думаю, что это может привести к увеличению потребления памяти.
CREATE TYPE...set_of_records()
Материал взят из примера в вики, где упоминается, что "Вы также можете возвращать записи с массивом JSON". Я думаю, это действительно означает "массив объектов".
Это id
значение из записи БД, встроенной в JSON?
Версия № 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%'
Версия № 2
Это позволяет Postgres объединять / объединять некоторые значения для сокращения обработки, выполняемой в 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
Как бы вы сравнили производительность?: дублируйте данные JSON в столбец hstore во время записи (или, если производительность каким-то образом может быть достаточно хорошей, преобразуйте JSON в hstore в выбранное время) и используйте hstore в своем WHERE
Например:
SELECT id, data FROM things WHERE hstore_data -> name LIKE 'Z%'
Я слышал о hstore отсюда: http://lwn.net/Articles/497069/
В статье упоминаются некоторые другие интересные вещи:
PL / v8 позволяет вам... создавать индексы выражений для определенных элементов JSON и сохранять их, предоставляя вам сохраненные поисковые индексы, аналогичные "представлениям" CouchDB.
Это не уточняет это, и я не знаю, о чем идет речь.
Есть комментарий, приписываемый как "jberkus", который говорит:
Мы обсуждали также наличие двоичного типа JSON, но без протокола для передачи двоичных значений (BSON вовсе не является стандартом и имеет некоторые серьезные глюки), в этом не было смысла.
Если вы заинтересованы в работе над двоичной поддержкой JSON для PostgreSQL, мы бы хотели, чтобы вы помогли...
Возможно, вместо того, чтобы возложить на фазу поиска ответственность за анализ данных, лучшим подходом может стать создание нового типа данных, который мог бы предварительно распространять данные json при вводе?
http://www.postgresql.org/docs/9.2/static/sql-createtype.html
Я не знаю, будет ли это полезно здесь, но я наткнулся на это: http://code.google.com/p/pg-to-json-serializer/. Упоминает функциональность для:
парсинг строк JSON и заполнение записей / массивов postgreSQL из него
Я не знаю, принесет ли это какое-либо преимущество в производительности по сравнению с тем, что вы делали до сих пор, и я даже не совсем понимаю их примеры.
Просто подумал, что стоит упомянуть.