Я относительно новичок в StackOverflow и не уверен, что это подходящее место, чтобы задать вопрос по дизайну.Сайт подсказывает мне «Вопрос, который вы задаете, кажется субъективным и, вероятно, будет закрыт» .Возможно, это следует спросить на programmers.stackexchange.com .Пожалуйста, дайте мне знать.
В любом случае .. Один из проектов, над которым я работаю, это онлайн-опрос.Это мой первый крупный коммерческий проект на GAE .
Мне нужен ваш совет о том, как собирать статистику и эффективно записывать ее в хранилище данных, не разоряя меня.Начальные требования:
- После того, как пользователь завершит опрос, клиент отправит список пар [ID (int) + PercentHit (double)].В этом списке показано, насколько близкие ответы этого пользователя соответствуют предварительно заданным ответам эталонных ответчиков (которые идентифицируются по идентификаторам).Я называю их «целевыми идентификаторами».
- Создатель опроса хочет видеть агрегированный% для данных идентификаторов за последний час, конкретный период или с начала опроса.
- Некоторые опросы могут иметьтысячи целевых / эталонных ответчиков.
Итак, я создал сущность
public class HitsStatsDO implements Serializable
{
@Id
transient private Long id;
transient private Long version = (long) 0;
transient private Long startDate;
@Parent transient private Key parent; // fake parent which contains target id
@Transient int targetId;
private double avgPercent;
private long hitCount;
}
Но написание HitsStatsDO для каждой цели от каждого пользователя даст много данных.Например, у меня был опрос с 3000 мишенями, на который ответили ~ 4 миллиона человек в течение одной недели, и 300K человек приняли участие в опросе в первый день.Даже если мы предположим, что они отвечали на него равномерно в течение 24 часов, это дало бы нам ~ 1040 записей в секунду.Очевидно, что он достигает лимита одновременной записи в Datastore.
Я решил собрать данные за один час и сохранить их, поэтому в HitsStatsDO
есть avgPercent
и hitCount
.Экземпляры GAE не имеют состояния, поэтому мне пришлось использовать динамический бэкэнд-экземпляр .
Там у меня есть что-то вроде этого:
// Contains stats for one hour
private class Shard
{
ReadWriteLock lock = new ReentrantReadWriteLock();
Map<Integer, HitsStatsDO> map = new HashMap<Integer, HitsStatsDO>(); // Key is target ID
public void saveToDatastore();
public void updateStats(Long startDate, Map<Integer, Double> hits);
}
и карта с осколком для текущего часа и предыдущегочас (который не остается здесь надолго)
private HashMap<Long, Shard> shards = new HashMap<Long, Shard>(); // Key is HitsStatsDO.startDate
Так, один раз в час я сбрасываю осколок за предыдущий час в хранилище данных.
Плюс у меня есть class LifetimeStats
, который хранит Map<Integer, HitsStatsDO>
в memcached, где map-key является идентификатором цели.
Также в моем методе back-end shutdown hook Я сбрасываю статистику за незавершенный час в хранилище данных.
Существует только одна серьезная проблемаздесь - У меня есть только ОДИН бэкэнд-экземпляр :) Возникают следующие вопросы, по которым я хотел бы услышать ваше мнение:
- Можно ли это сделать без использования бэкэнд-экземпляра?
- Что, если одного экземпляра недостаточно?
- Как разделить данные между несколькими динамическими внутренними экземплярами?Это трудно, потому что я не знаю, сколько у меня есть, потому что Google создает новый при увеличении нагрузки.
- Я знаю, что могу запустить точное количество резидентных бэкэндов.А сколько?2, 5, 10?Что делать, если у меня нет нагрузки на неделю.Постоянно работать с 10 экземплярами бэкэнда слишком дорого.
- Что мне делать с данными от клиентов, когда бэкэнд-экземпляр не работает / перезапускается?
Следует отметить, что я не могуизменить клиента оченьВ настоящее время это JavaScript, встроенный в веб-страницы клиентов.Я могу каким-то образом изменить RPC, но архитектурно я не могу заменить клиента формами Google Docs, например.
Большое спасибо заранее за ваши мысли.