Выбор отдельных объектов в большой таблице Google App Engine - PullRequest
1 голос
/ 14 февраля 2011

Мне было интересно, может ли кто-нибудь помочь мне с этой проблемой.

У нас есть идея, которую мы хотели бы реализовать, и в настоящее время мы не можем сделать это эффективно.

Я анонимизировал данные как можно лучше, но структура та же.

У нас есть две сущности, Car и CarJourney. Каждый автомобиль имеет 0 ко многим CarJourney's. Каждое автомобильное путешествие имеет (среди прочих свойств) дату, связанную с ним - дату начала поездки.

Я хотел бы запросить время по автомобильным поездкам. У меня будет два раза, дата начала и дата окончания, где дата начала <= endDate, и я хочу получить последнее начатое путешествие за этот период. </p>

Итак, если бы я имел в виду конкретную машину, скажем, машину 123, я бы написал запрос, который ограничивается значениями Car.key и Car.startDate, где Car.key == 123 и Journey.startDate> = startDate и Journey.startDate <= endDate с упорядочением по Journey.startDate по убыванию и пределом 1. </p>

например. Автомобиль А имеет 3 поездки, совершенные 1-го, 2-го и 3-го числа месяца. Дата начала запроса - 1-й, а дата окончания запроса - 2-й. Результатом этого запроса будет одно автомобильное путешествие, второе.

Как только возвращается результат этого запроса, выполняется очень небольшая обработка, чтобы вернуть результат пользователю.

Это легко.

Но вместо 1 Автомобиля мне нужен список автомобилей, в котором содержится N ключей от автомобилей.

Итак, я хочу выполнить вышеуказанный запрос N раз, один раз для каждой машины. И я хочу новейшее путешествие для каждой машины.

Поскольку временной диапазон является гибким (и, следовательно, не может быть известен заранее), мы не можем реализовать флаг «isMostRecent», потому что, хотя он может быть самым последним на данный момент, он может быть не самым последним для указанные параметры даты.

Мы также должны убедиться, что это возвращает быстро (текущие запросы находятся на отметке 3-5 секунд для небольшого набора данных), поскольку это возвращается непосредственно к пользователю. Это означает, что мы не можем использовать очереди задач, и поскольку указанные даты являются произвольными, мы не можем реализовать массовую индексацию полей «isWithinDate».

Мы попытались использовать асинхронный запрос, но поскольку объем обработки незначителен, узким местом по-прежнему являются запросы к хранилищу данных (поскольку асинхронный API-интерфейс по-прежнему отправляет запросы синхронно, он просто не блокируется).

В идеале, мы бы реализовали это как выбор для автомобильных поездок, заказанных startDate, где Car.key отличается, но мы не можем осуществить это в GAE.

Существует множество небольших оптимизаций, которые мы можем сделать (например, некоторые MemCaching для повторных запросов), но ни одна не оказала существенного влияния на время нашего запроса. И MemCaching может помочь только максимум 1-2 минуты (из-за неизбежного продвижения вперед!)

Любые идеи приветствуются и высоко ценятся.

Спасибо, Ed

Ответы [ 7 ]

1 голос
/ 15 февраля 2011

Похоже, лучший вариант - выполнить множество запросов самостоятельно.Вы говорите, что пробовали асинхронные запросы, но узкое место отправляло запрос.Это кажется очень странным - у вас должно быть много запросов одновременно, что существенно сокращает время ожидания.Как вы это определили?

0 голосов
/ 25 февраля 2011

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

0 голосов
/ 15 февраля 2011

Я столкнулся с такой же проблемой некоторое время назад.Я попробовал некоторые решения (в сортировке и фильтрации памяти, кодировании вещей в ключи и т. Д., И я сравнил их для циклов задержки и процессора, используя некоторые тестовые данные около 100 тыс. Объектов). Другой подход, который я использовал, - это кодирование даты в виде целого числа (день с начала эпохи или день с начала года, то же самое относится к часу дня или месяца в зависимости от того, сколько деталей вам нужно в выходных данных) и сохраняя их в свойстве.Таким образом, вы превращаете свой фильтр запроса даты в фильтр только на равенство, которому даже не требуется указывать индекс), а затем вы можете сортировать или фильтровать другие свойства.Сравнительный анализ последнего решения, которое я обнаружил, заключается в том, что когда отфильтрованный результирующий набор представляет собой небольшую долю нефильтрованного исходного набора, он на 1+ порядка быстрее и эффективнее.Худший случай, когда уменьшение набора результатов из-за фильтрации задержки и использования процессора было сопоставимо с предыдущими решениями)

Надеюсь, это поможет, или я что-то пропустил?

Счастливое кодирование-:)

0 голосов
/ 14 февраля 2011

Денормализация должна решить вашу проблему - наличие в вашем автомобиле ссылочного свойства last_journey, поэтому каждый раз, когда вы начинаете путешествие, вы также обновляете сущность Car - таким образом вы сможете запрашивать все автомобили и совершать их последние поездки на результат. Стоит отметить, что при доступе к last_journey в хранилище данных будет выдан новый метод get (), поэтому, если вы перечисляете много машин, вы можете создать список со всеми ключами last_journey и затем извлекать все сразу в db.get ().

Масштабируемые, сложные приложения в App Engine определенно необходимо посмотреть (к сожалению, в этом видео ужасный звук)

0 голосов
/ 14 февраля 2011

Вы также можете использовать один запрос и самостоятельно фильтровать различные автомобили.Введите select CarJouney startDate >= startDate and startDate <= endDate order by startData и выполняйте итерацию (+ фильтр на вашей стороне) этого запроса, пока не найдете достаточно данных для показа.

0 голосов
/ 14 февраля 2011

Прежде всего, я бы порекомендовал использовать objectify .JDO / JPA на appengine просто вводит людей в заблуждение, что хранилище данных appengine - это просто база данных SQL, которая, как вы поняли, далека от истины.

Если я правильно понимаю, у вас есть автомобиль, содержащий списокCarJourneys?

Свойства списка в appengine ограничены 5000 записями, и каждый раз, когда вы обращаетесь к ним / изменяете их, они должны быть сериализованы / десериализованы целиком.Так что если вы планируете иметь много CarJourney на автомобиль, то это будет медленно.Кроме того, поскольку appengine создает запись индекса для каждого значения в коллекции, это может привести к взрывным индексам .

Вместо этого просто создайте свойство Car внутри CarJourney, которое указывает на Car, совершивший путешествие.: отношения один-к-одному от CarJourney к Car.Тип может быть Key или просто string / long, содержащий идентификатор автомобиля.При запросе просто добавьте фильтр для свойства Car.

Я предлагаю посмотреть видео Бретта Слаткина: Масштабируемые, сложные приложения в App Engine .

0 голосов
/ 14 февраля 2011

Как предположил JB nizet, мне интересно, может ли ответ быть чем-то вроде одного запроса, возможно, с временной таблицей или анонимной промежуточной таблицей (я не знаю, что поддерживает Google с этой целью), используя группу (таким образом исключая дополнительную передачу данных и необходимость обработки Java).Я думаю о чем-то вроде

CREATE TEMPORARY TABLE temp1 AS
SELECT * FROM car_journey
WHERE start_date > ? AND
end_date < ?

SELECT car_id, journey_id
FROM temp1 t1, (
  SELECT car_id, MIN(start_date)
  FROM temp1
  GROUP BY car_id 
) t2
WHERE t1.car_id = t2.car_id AND
t1.start_date = t2.start_date

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...