Comments 33
Но что случится, если нам потребуется вывести сразу 10'000 записей?
И вот тут сразу возникает вопрос: а зачем?
Ситуации разные бывают. Экспорт данных или передача данных на обработку в другой сервис, например.
Такие вещи надо делать стримингом.
Одно другому не мешает вроде как. Сам по себе стримминг не ускорит генерацию JSON.
Сам по себе стриминг сильно меняет требования к производительности, потому что у вас затраты «размазываются».
Здесь «производительность» измеряется во времени, поэтому как затраты не «размазывай» — времени меньше не потратится. Есть задача — отдать 10 тысяч объектов и сделать это максимально быстро. Чем поможет стриминг?
Тем, что у вас будет не «сначала забрали все из БД, потом упаковали в json», а «забрали запись из БД, упаковали ее в JSON», при этом первая запись пакуется в json в то время, пока вторая забирается из БД. А в реальности у вас еще есть время передачи по каналу, и тут тоже: если вы выплюнули первую запись сразу, а не когда все сконвертировалось, то в реальности в можете достигнуть результата быстрее.
Такая вот простая параллельность.
Такая вот простая параллельность.
Такое решение тоже имеет право на жизнь, но превносит дополнительную сложность, как минимум. Во-первых, принимающая сторона тоже должна поддерживать стриминг. Во-вторых, это теория, а чтобы узнать что быстрее — нужны цифры из тестов. В-третьих, может стоять задача сделать моментальный снимок данных и тянуть данные по одной записи нельзя.
Ситуация бывают разные, поэтому и решения могут быть разными. Я не утверждаю, что решение в статье, которую я перевел, подходит всем. Но и стриминг не является в данном случае 100% альтернативой, которая будет лучшей заменой для любой подобной задачи, верно?
Ситуация бывают разные, поэтому и решения могут быть разными. Я не утверждаю, что решение в статье, которую я перевел, подходит всем. Но и стриминг не является в данном случае 100% альтернативой, которая будет лучшей заменой для любой подобной задачи, верно?
Во-первых, принимающая сторона тоже должна поддерживать стриминг.
Нет, потому что http его поддерживает.
В-третьих, может стоять задача сделать моментальный снимок данных и тянуть данные по одной записи нельзя.
Вообще-то, с точки зрения «моментальности» ничего не меняется — как вам БД отдавала данные по определенным критериям, так и отдает. Вопрос только в том, как и когда вы начинаете их обрабатывать.
Такое решение тоже имеет право на жизнь, но превносит дополнительную сложность, как минимум.
А решение из поста — не привносит?
Во-вторых, это теория, а чтобы узнать что быстрее — нужны цифры из тестов.
Бинго. Именно поэтому сначала надо спросить «зачем», потом найти ботлнек. Обычно большая часть проблем решается еще на первом шаге.
Нет, потому что http его поддерживает.
HTTP — это протокол, а принимающая сторона — ПО, поэтому протокол может быть и поддерживает, а вот принимающая сторона может и не поддерживать. Иначе откуда взялись костыли вроде Long polling и иже с ним для браузеров?
Вообще-то, с точки зрения «моментальности» ничего не меняется — как вам БД отдавала данные по определенным критериям, так и отдает. Вопрос только в том, как и когда вы начинаете их обрабатывать.
Вообще-то, очень даже меняется. Если данные в базе изменяются, например, 20 раз в секунду, то один запрос и 1000 запросов вернут совсем разные значения.
А решение из поста — не привносит?
Привносит минимум.
HTTP — это протокол, а принимающая сторона — ПО, поэтому протокол может быть и поддерживает, а вот принимающая сторона может и не поддерживать.
Вы не понимаете, как это работает. Я говорю банально о том, что, вместо того, чтобы сконвертировать весь массив в json в памяти (допустим, это 30 секунд), а потом выплюнуть его в http-поток, по которому он будет передаваться еще 30, можно сконвертировать первую запись (0.5с) и сразу ее выплюнуть (0.5с). Если клиент умеет так читать поток, он получит данные сразу (т.е., первые данные — через 1с). Если нет — у него соберется весь массив данных (через 30,5с вместо минуты). Никаких действий со стороны клиента при этом не нужно.
Если данные в базе изменяются, например, 20 раз в секунду, то один запрос и 1000 запросов вернут совсем разные значения.
Кто вам сказал, что будет 1000 запросов? Запрос-то один, просто обрабатывается по частям.
Привносит минимум.
Если что, правильно написанный стриминг приносит не больше.
Ок, т.е. все сводится к тому, что мы делаем все тоже самое, только отдаем данные по кускам? Как это кардинально меняет то, что написано в статье, не понимаю. Не надо делать выборку из базы только с нужными полями? Да нет, вроде как надо. Не надо использовать хэши вместо объектов ActiveRecord? Да нет, вроде не лишняя операция. Не стоит использовать альтернативные дамперы JSON? Возможно, но нужно тестить.
В сухом остатке — вся та же самая подготовка данных, но отдача стримингом. Так? Не совсем понятно зачем мы это так долго обсуждали :)
В сухом остатке — вся та же самая подготовка данных, но отдача стримингом. Так? Не совсем понятно зачем мы это так долго обсуждали :)
Нет, в сухом остатке тот факт, что если сделать выборку и сериализацию новых полей и стриминг, то альтернативные дампера и хэши уже могут быть и не нужными.
Я просто исповедую принцип наименьших изменений.
Я просто исповедую принцип наименьших изменений.
Произошла путаница с терминами.
Попробую объяснить псевдокодом:
Ваш подход:
если «развернуть» методы, то увидим что под капотом происходит примерно следующее:
lair предлагает такой подход:
Этот подход хорош ещё тем, что занимает меньше памяти.
Но! Часто его нельзя реализовать не ломая логику используемого фреймворка.
Попробую объяснить псевдокодом:
Ваш подход:
var query = DB.exec_query(...);
var result = query.get_all();
var output_string = JSON.encode(result);
write_output(output_string);
если «развернуть» методы, то увидим что под капотом происходит примерно следующее:
var query = DB.exec_query(...);
while ( var row = query.get_next() ) {
result.push(row);
}
foreach (res_row IN result ) {
output_string = output_string + JSON.encode(res_row);
}
write_output(output_string);
lair предлагает такой подход:
var query = DB.exec_query(...);
while ( var row = query.get_next() ) {
write_output(
JSON.encode( row );
);
}
Этот подход хорош ещё тем, что занимает меньше памяти.
Но! Часто его нельзя реализовать не ломая логику используемого фреймворка.
>Чем поможет стриминг?
Не уверен, что стримминг поможет, но вот передача пачками — точно.
Не уверен, что стримминг поможет, но вот передача пачками — точно.
Стримингом? В RoR?
RoR действительно никак не позволяет это сделать?
К сожалению, я не настолько хорошо с ним знаком.
К сожалению, я не настолько хорошо с ним знаком.
Были какие-то потуги типа async-rails, но вообще придётся ждать 4.0 или держать по процессу на каждое соединение. По поводу, можно хотя бы по Thread'у, честно говоря, ничего сказать не могу, и в любом случае это адский костыль.
Всё написанное ни в коем случае не относится в общем к Ruby, а только в частности к Rails. В Sinatra такое возможно, и очень просто, уже писал чуть ниже.
Всё написанное ни в коем случае не относится в общем к Ruby, а только в частности к Rails. В Sinatra такое возможно, и очень просто, уже писал чуть ниже.
Ну можно перевернуть задачу — вывести по 10 записей но 10 000 раз. На маленьких задачах тоже есть смысл экономить если у вас много клиентов.
Буквально на днях наткнулся на блог «How I Learned to Stop Using LINQ2SQL and Love nHibernate», перец пытается считать из базы «1.6 million records», жалуется на таймаут и говорит, что Linq2Sql дерьмо и всем надо срочно переходить на NHibernate.
После прочтения этого поста на Хабре и того блога хочется биться головой о стол и кричать — «Люди!!! Человеки!!! На хера?!!! На хера вы это делаете???!!!!» Это ж как зуб пломбировать через жопу…
После прочтения этого поста на Хабре и того блога хочется биться головой о стол и кричать — «Люди!!! Человеки!!! На хера?!!! На хера вы это делаете???!!!!» Это ж как зуб пломбировать через жопу…
а что, если все же надо? ну ок, прям сегодня мне ставили задачу и надо было 113 000 записей выдать, чуть раньше, на прошлой неделе, было 1.13 млн. и?
Была 2 раза ситуация, когда JSON-запросы были медленные и большие. Решение было довольно простое. Перенести запрос на NodeJS… 38 строчек код ускорили отклик и получение данных в 4 раза.
Я обожаю Rails, но бывают узкие места, которые можно очень просто перенести на NodeJS и никто не пострадает.
Я обожаю Rails, но бывают узкие места, которые можно очень просто перенести на NodeJS и никто не пострадает.
А можно вопрос — почему Node.js с его ужасной структурой, а не Sinatra, кроме того написанная на том же Ruby? (=
Вроде как sinatra нативно не поддерживает comet? И соответственно, вопросы производительности и архитектуры. Я бы лучше спросил почему node.js, а не erlang?)
У меня был опыт использования NodeJS, в отличии от Sinatra.
И мне интуитивно кажется, что NodeJS быстрее отдаст запрос, чем любой Ruby-сервер…
И мне интуитивно кажется, что NodeJS быстрее отдаст запрос, чем любой Ruby-сервер…
Sign up to leave a comment.
Молниеносный JSON в Ruby on Rails