Подтвердить что ты не робот

Сериализация Javascript и производительность с помощью V8 и PostgreSQL

Я был экспериментировал с PostgreSQL и PL/V8, в который встроен движок JavaScript V8 в 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 может быть победой. Я посмотрел на использование альтернативного формата, такого как messagepack или protobuf, но я не думаю, что они обязательно будут такими же быстрыми, как и собственный JSON-парсер в любом случае.

МЫСЛЬ

PG имеет blobs и двоичные типы, поэтому данные могут быть сохранены в двоичном формате, тогда нам просто нужен способ их сортировки в V8.

4b9b3361

Ответ 1

Postgres поддерживает индексы для произвольных вызовов функций. Следующий индекс должен сделать трюк:

CREATE INDEX json_idx ON things (json_string(field,'name'));

Ответ 2

Короче говоря, с поддержкой Pg new json, до сих пор нет возможности хранить json прямо в любой форме, кроме сериализованного json-текста. (Вероятно, это изменится в 9.4)

Кажется, вы хотите сохранить предварительно обработанную форму, которая представляет собой сериализованное представление о том, как v8 представляет json в памяти и которая в настоящее время не поддерживается. Неясно даже, что v8 предлагает любую двоичную сериализацию/десериализацию json-структур. Если это не так изначально, код должен быть добавлен в Pg для создания такого представления и для его возврата в структуры данных v8 json.

Это также не обязательно будет быстрее:

  • Если json был сохранен в бинарной форме v8, запросы, которые возвращали нормальное json-представление клиентам, должны были бы форматировать их каждый раз, когда они были возвращены, что привело к стоимости процессора.

  • Двоичная сериализованная версия json - это не то же самое, что хранить файлы данных v8 json непосредственно в памяти. Вы не можете написать структуру данных, которая напрямую связана с любым графиком указателей на диск, она должна быть сериализована. Эта сериализация и десериализация имеют затраты, и это может быть даже не намного быстрее, чем анализ текстового представления json. Это зависит от того, как v8 представляет объекты JavaScript в памяти.

  • Двоичное сериализованное представление может быть легко большим, так как большинство json - это текст и небольшие числа, где вы не получаете никакой компактности из двоичного представления. Поскольку размер хранилища напрямую влияет на скорость сканирования таблиц, извлекает значения из TOAST, время декомпрессии, требуемое для значений TOASTed, размеры индекса и т.д., Вы можете легко приземлиться с более медленными запросами и большими таблицами.

Мне было бы интересно узнать, возможна ли оптимизация, как то, что вы описали, и будет ли она вообще оптимизацией.

Чтобы получить преимущества, которые вы хотите использовать при сканировании таблиц, я думаю, что вам действительно нужен формат, который можно пройти без необходимости его синтаксического анализа и превратить его в то, что, вероятно, является графиком malloc() 'j объектов javascript. Вы хотите дать выражение пути для поля и получить его непосредственно из сериализованной формы, где оно было прочитано в буфер чтения Pg или в shared_buffers. Это был бы действительно интересный проект, но я был бы удивлен, если бы что-то вроде этого существовало в версии 8.

Что вам действительно нужно сделать, так это исследование того, как существующие базы данных объектов на основе json быстро выполняют поиск произвольных путей json и каковы их представления на диске, а затем сообщают о pgsql-хакерах. Может быть, есть что-то, чему можно научиться у людей, которые уже решили это, полагая, конечно, что они есть.

В то же время я хотел бы сосредоточиться на том, что здесь делают другие ответы: работайте над медленной точкой и находите другие способы делать то, что вам нужно. Вы также можете изучить оптимизацию json-парсера, но в зависимости от того, используется ли v8 один или какой-либо другой, который уже может быть намного меньше точки убывания.

Я предполагаю, что это одна из областей, где есть компромисс между скоростью и гибким представлением данных.

Ответ 3

возможно, вместо того, чтобы создать фазу поиска, ответственную за разбор данных, создание нового типа данных, которое могло бы предварительно распространять данные json на входе, могло бы быть лучшим подходом?

http://www.postgresql.org/docs/9.2/static/sql-createtype.html

Ответ 4

У меня нет опыта с этим, но мне стало любопытно, поэтому я немного читал.

Только JSON

Как насчет чего-то вроде следующего (непроверенный, BTW)? Он не затрагивает ваш вопрос о сохранении двоичного представления JSON, это попытка разобрать все JSON сразу для всех строк, которые вы проверяете, в надежде, что это даст более высокую производительность за счет сокращения обработки накладные расходы, выполняемые индивидуально для каждой строки. Если это удастся, я думаю, что это может привести к увеличению потребления памяти.

Материал CREATE TYPE...set_of_records() адаптирован из примера в wiki, где упоминается, что "вы также можете возвращать записи с массивом из JSON." Я предполагаю, что это действительно означает "массив объектов".

Значение id из записи DB, встроенной в 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, нам будет интересно получить помощь...

Ответ 5

Я не знаю, было бы полезно здесь, но я натолкнулся на это: pg-to-json-serializer. В нем упоминается функциональность для:

разбор строк JSON и заполнение записей/массивов postgreSQL из него

Я не знаю, принесет ли она какое-либо преимущество в производительности по сравнению с тем, что вы делали до сих пор, и я даже не понимаю их примеров.

Просто подумал, что стоит упомянуть.