Масштабирование временных записей в базе данных для подсчета итогов, чтобы прекратить переподписку - PullRequest
1 голос
/ 09 ноября 2011

Я смотрю на систему, где люди проводят RSVP на бесплатной веб-конференции, эти веб-конференции могут быть действительно заняты большим количеством интересов, и места ограничены, поэтому должен быть какой-то способ убедиться, что это не переподписаться ... Итак, нам нужен обратный отсчет с момента, когда они забронировали свое место, до момента, когда их заказ завершен и подтвержден. Таким образом, это первое количество людей, которые действительно нажали «Присутствовать», чтобы получить места для конференции. Если пользователь не завершил свое бронирование, его выделенное место помещается обратно в бассейн, чтобы кто-то еще мог его захватить ...

Обзор того, что происходит (как мне кажется, это лучше всего работает)

1) Пользователь нажимает «RSVP» в случае события, это делает AJAX-запрос к / rsvp / {event_id} /

Это идет и хранит уникальный идентификатор (упоминается как токен в остальной части этого вопроса) вместе с отметкой времени. Этот токен также сохраняется в сеансе.

Затем пользователю сообщают, что у него есть X времени (скажем, 5 минут), чтобы заполнить оставшиеся детали (name / email / d.o.b и т. Д.)

Однако: перед тем, как что-то поместить в базу данных, он проверяет, меньше ли количество отложенных ордеров, чем общее количество доступных мест, если нет (мест для конференции не осталось), то возвращает «извините, мест нет» больше не доступно, продолжайте проверять, так как заказы не завершены и больше мест становится доступным "

2) Если пользователь заполняет это вовремя, он сохраняет свои данные в базе данных как «сопровождающие» ....

Если, однако, они не заполняют форму вовремя, каждую секунду выполняется задание cron, которое проходит и удаляет все токены, которые имеют временную метку более 5 минут назад, поэтому они потеряют свой шанс посетить это место 'на конференции затем возвращается обратно в бассейн. (Они будут уведомлены, что они были неудачны и возвращены к первому шагу)

Все достаточно просто написать, просто поместив записи в базу данных, выполнив COUNT(*) FROM pending_bookings WHERE conference_id = {x} и определив, сколько мест подтверждено или ожидающих заказов, затем вычтя из общего числа мест, доступных для конференции.

Но я не чувствую, что использование MySQL было бы очень масштабируемым в этом - у них есть (и я уверен, что будет снова) более 200 тысяч человек, пытающихся захватить около 200 мест, делая СЧЕТ (*) для каждый из этих людей будет довольно дорогим, и мы не сможем сделать приличное кэширование, так как нужно в режиме реального времени проверять, сколько людей участвует в процессе.

Я рассмотрел использование Amazon SimpleDB для этого, просто для его масштабируемости развертывания и запуска, но я использовал его раньше и видел, что COUNT () не обязательно точен (природа его масштабируемости, я думаю) - по очевидным причинам, что COUNT должен быть точным на 100%, мне нужно иметь возможность добавлять к нему записи вместе с отметкой времени и иметь возможность удалять записи из него старше пяти минут.

Ответы [ 8 ]

3 голосов
/ 12 ноября 2011

выполнение COUNT (*) для каждого из этих людей будет довольно дорогим

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

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

3 голосов
/ 09 ноября 2011

Мне кажется, что все 200 тысяч человек могут получить «токен» для начала, но только 200 могут завершить токен.

Итак, на ум приходят две вещи;1) почему бы не следить за "Оставшимися местами" на самом событии, чтобы вы не делали подсчет, и резервирование не может произойти, потому что блокировка, необходимая для обновления события, предотвратит его опускание ниже нуля.

2) В любое время во время подписки на людей, если оставшееся количество мест падает до нуля, все оставшиеся токены становятся недействительными, и пользователи «выгоняются» из процесса регистрации (это приятно, извиняюсь, но все места заполнены и т. Д.)

1 голос
/ 18 ноября 2011

Вероятно, стоит повторно посетить ваш дизайн:

Остальные места - это общее количество мест за вычетом забронированных мест.

Этот номер действителен только в определенный момент времени, так какизменение количества забронированных мест.

Чтобы забронировать место, вы совершаете транзакцию: пользователь сообщает, что хочет забронировать одно или несколько мест, обрабатывает регистрацию и, наконец, бронирует (или нет).

Пока эта транзакция не была завершена, количество оставшихся мест, которое вы можете рассчитать, вероятно, неверно.

У вас есть два варианта:

  1. РазрешающийБлокировка
  2. Оптимистическая блокировка

В первом случае каждый раз, когда пользователь запускает транзакцию и сообщает приложению, что места X должны быть забронированы, эти места X блокируются.Это не означает, что эти места уже забронированы, они просто заблокированы, поэтому у пользователя есть достаточно времени для завершения транзакции бронирования.Если транзакция заканчивается, эти заблокированные места резервируются пользователем.

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

Разрешающая блокировка может помешать некоторым пользователям подписаться до начала транзакции - даже если они могут сделать это через день - ОптимистичноБлокировка может помешать некоторым пользователям подписаться в конце транзакции.

Вам необходимо выяснить, что лучше всего подходит для вашего случая.Обычно оптимистическая блокировка более приятна для пользователей (так как только некоторые из них заканчиваются неудачей в конце), однако разрешительная блокировка поможет вам не расстраивать пользователей в конце транзакции.Если на билетах всегда есть пробежка, возможно, лучше использовать разрешительную блокировку.

Вы можете подумать о том, как сделать вещи менее разочаровывающими для пользователей, введя юзабилити в игру.Например, с оптимистической блокировкой, каждая страница в транзакции может иметь счетчик оставшихся AJAX сверху, показывающий текущее количество оставшихся мест, поэтому вы можете заранее сообщить пользователям, если места закончились.Таким образом, даже если они уже что-то вложили в формы, они могут видеть, насколько удачливы (или достаточно быстры) они в своих действиях.

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

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

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

Кстати, дизайн базы данных должен отражать процедуру, которую вы определили заранее, я неЯ думаю, что вы столкнетесь с реальными проблемами здесь, пока знаете, чего пытаетесь достичь.Поскольку все делается в торговых операциях, вы можете даже вести простой подсчет для каждого события общего, забронированного и заблокированного места.Это один простой запрос, нет необходимости в агрегировании, как при COUNT(*).Также могут быть полезны триггеры и хранимые процедуры.

1 голос
/ 16 ноября 2011

Я согласен с xQbert: при каждом успешном завершении заполнения посещаемости вы просто уменьшаете сумму в строке базы данных для события (или в соседней таблице).

Если вас беспокоит коллизия при бронировании, просто уменьшите ее в начале заполнения информации о посещаемости; Затем добавьте строку во временную таблицу, в которой говорится, что человек с этим идентификатором сеанса заполняет форму участия в этом событии, и у него есть 5 минут от этой отметки времени, чтобы завершить ее. Когда они заполняют форму, их запись удаляется из этой временной таблицы.

Затем запустите для этой таблицы задание cron, которое запрашивает метки времени, которые меньше или равны текущему времени - 5 минут. Все, что выпадет, будут удалены со стола, а их события получат увеличенное место. Затем, когда они отправят форму после 5-минутного периода, она проверит, чтобы убедиться, что у них все еще есть запись в таблице, прежде чем сохранить свое место навсегда.

Если они этого не сделают, проверьте, остались ли места. Если есть, то уменьшите количество мест и вставьте их запись бронирования. Если нет, то дайте им ошибку, которая говорит о том, что они не заполнили форму за отведенное время, потеряли свою позицию в очереди, и у вас закончились места.

Это достаточно просто и может быть сделано на стороне PHP. Возможно, вы захотите установить таймер JS на их странице, чтобы они знали, сколько времени у них осталось.

1 голос
/ 16 ноября 2011

Redis http://redis.io хорошо подходит для того, что вы хотите сделать. Вы можете сохранить счетчик доступных мест, сохранить временные данные и автоматически истечь ... Супер универсальный.

1 голос
/ 13 ноября 2011

Извините, что неправильно прочитал вопрос.В этом случае я бы предложил иметь кеш-таблицу (да, вы можете), которая будет обновляться с помощью триггера (ON INSERT / DELETE) на таблице, где находятся транзакции.

Когда вы позволяете пользователю войти в транзакцию, вы вставляете в эту таблицу его хэш и метку времени истечения.Он запускает триггер, который обновляет кэшированное значение (то есть значение в таблице кэша) в соответствии с этим - +1 для вставки / -1 для удаления.

Проверяя, есть ли свободные места, вы проверяете таблицу кеша.

Будет ли работать этот тип кэширования?:)

1 голос
/ 09 ноября 2011

Вы можете кэшировать счетчик, доступный в базе данных, и обновлять его всякий раз, когда вы выпускаете (регистрация началась), подтверждаете (регистрация завершена) или отзываете (более 5 минут) токен.Но если вам нужна такая производительность, это действительно не то, для чего нужна база данных SQL.

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

Когда регистрация будет завершена, вам придется удалить этот токен из кучи (довольно дорогая операция, но вы делаете это только 200 раз).Таким образом, ваша куча всегда будет иметь токены, равные количеству свободных + ожидающих слотов.

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

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

0 голосов
/ 17 ноября 2011

Я думаю, COUNT (*), примененный к 200k записям, может быть получен довольно быстро. Я только что попробовал запрос на 243 тыс. Записей, и это заняло менее 1 секунды.

Но я не думаю, что вам даже нужен COUNT, за исключением последнего шага подтверждения.

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

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

Кроме того, если осталось много мест, зачем ограничивать пользователя 5 минутами? Зачем очищать их сессию с помощью "cron job" и заставлять их начинать все сначала? Что делать, если осталось еще 100 мест? Вместо этого, когда зарезервировано 200 мест, но не все подтверждены, новые кликеры вытесняют старые по очереди.

На первой странице, где у вас есть «кнопка регистрации», нет необходимости выполнять запрос COUNT (*).

Вы можете просто сделать это:

SELECT `event_status` from `events` WHERE `event` = 'this_event';

Event_status может быть обновлен, как только все места будут подтверждены.

Затем, используя ваш PHP или что-то еще, вы либо отображаете кнопку, либо выводите «Извините, событие заполнено».

if($event_status == 'full') { echo 'Sorry, it's full; }
else { echo $press_this_button; }

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

SELECT MIN(time_initiated) as time_initiated, id, sessID FROM testtime WHERE `time_initiated` <= DATE_SUB(NOW(), INTERVAL 5 MINUTE);

Если это даст вам какие-либо результаты, значит, у вас есть парень, который исключит список ожидания. Просто пометьте его сеанс как «Истек», и, если он когда-либо нажмет «Отправить», проверьте, полностью ли подтверждено событие. Если так, скажите ему, что извините, что он опоздал. Если нет, то повторите приведенный выше запрос, исключите кого-либо из нижней части списка и подтвердите его, если только все люди в списке ожидания не младше 5 минут, в этом случае вы можете попросить его подождать несколько минут и посмотрим, освободятся ли какие-нибудь пятна.

После каждого успешного подтверждения вы можете запрашивать СЧЕТ подтвержденных гостей, а если число превышает 200, вы изменяете event_status на «полный» и не позволяете никому другому нажимать эту кнопку. Они просто увидят сообщение, что даже полон. Вы должны быть в состоянии обернуть эти 2 запроса в транзакции, чтобы гарантировать, что несколько человек не смогут забронировать одновременно, и вы в итоге будете перебронированы.

Это только мои 2 цента.

...