четверг, 29 октября 2009 г.

Продажа контента за SMS

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

Рассмотрим пример оплаты и прокомментируем каждый этап исходным кодом на PHP.

0. Размещению на сайте оплаты через смс предшествует заключение договора с компанией предоставляющей эти услуги.
1. Пользователь заходит на страничку, где ему предлагается скачать приложение, предварительно отослав смс с текстом на указанный номер.
Реализация: Выводится статичные текст с текстом и телефоном, который предоставила на предыдущем этапе организация
2. Клиент отправляет СМС. Ему приходит ответная с уникальным кодом на доступ
Реализация: Сервер смс компании, получает сообщение с нужным тектом на номер. Генерируется событие, вызывающее произвольную (настраиваемую) страницу на нашем сайте. На это странице мы произвольны сделать, что угодно.
Генерируем код, и записываем в БД:
function getCOde() {
  $code = sprintf("%06x", rand(1000000, 10000000)); //рандомное число, преобразованное в hex
  //проверяем на повторения
  $res = db_query("SELECT COUNT(*) AS cnt FROM {sms_pay} WHERE code = '%s'", $code);
  $codes = db_fetch_array($res);
  if($codes['cnt'] > 0) {
    getCode(); //генерируем еще раз
  }
  db_query("INSERT INTO {sms_pay} (code) VALUES('%s')", rand(), $code); //записываем код в БД
  return $code;
}
Сгенерированный код, вываливается через echo в браузер, а сервер весь сгенерированый текст отправляет в виде смс обратно клиенту.
На данном этапе код еще не привязан ни к одному материалу и его можно активировать на любом.
3. Клиент получил код, вводит его, чтобы получить доступ к материалу
Реализация: Сервер сайт получает код, проверяет его наличие в БД. Если код верный, то в БД вносятся данные о файле к которому был получен доступ, также для защиты от передачи кода третьим лицам в бд заносится ip адрес клиента и ставится кука.
Файл может быть скачан не сразу, а по частям. В этом случае пользователю дается несколько часов на скачку файла: в бд заносим также время первого обращения к файлу.
После этого пользователю файл стримится прямо в браузер с утановкой необходимых хеадреов, чтобы у клиента не было прямого доступа к файлу (Об этом можно почитать отдельно, я пользуюсь стандартными функциями Drupal)
4. Клиент повторно обращается к этому же файлу.
Реализация: При повторном обращении к файлу, снова проверять код не обязательно. Необходимо сверить лишь куки и ip пользователя, а также id файла к которому идет обращение.
5. Если пользователь пытается обратится к другому файлу с этим же кодом, то это ему не удастся, т.к. он не пройдет проверку по id файла.
6. Код был передан третьим лицам. Пользователь не пройдет проверку по ip и куке.
Примерный код для предыдущих пунктов можно видеть в листинге: (php/Drupal, точка входа функция sms_pay_file_download)

пятница, 2 октября 2009 г.

Что быстрей LEFT JOIN или подзапрос в SELECT

Часто требуется выбрать дополнительные параметры плюсом к основной выборке.
Это можно сделать двумя способами:
  • LEFT JOIN к основному запросу
  • Подзапрос в секции SELECT
Пример запроса, выбирающего номенклатуру и товарную группу номенклатуры:

Подзапрос в секции SELECT:
SELECT nom.Наименование,
(SELECT tg.Наименование FROM Товарная группа tg WHERE tg.ID = nom.Товарная группа)
FROM Номенклатура nom

Вариант с LEFT JOIN:
SELECT nom.Наименование, tg.Наименование
FROM Номенклатура nom
LEFT JOIN Товарная группа tg
 ON(tg.ID = nom.Товарная группа)

После разбора плана выполнения запроса в MS SQL Server, было выявлено:
  • При выборке в секции SELECT требуется дополнительное время на слияние основной выборки и подзапроса. Вышло 1% от общего времени.(+ LEFT JOIN / - SELECT)
  • Оказалось, что каждый LEFT JOIN выполняется отдельным потоком! В то время как подзапросы выполняются последовательно после основной выборки. (+ LEFT JOIN / - SELECT)
  • Каждый LEFT JOIN объединяется в результирующую выборку, что требует дополнительной памяти. В то время как подзапрос вернет нам одно значение на каждую строку, т.е. для получения результата нам нужно меньше памяти. (- LEFT JOIN / + SELECT)
Вывод: В условиях многоядерных серверов LEFT JOIN имеет неоспоримые преимущества

Ограничения в подзапросах:
При выборке дополнительных параметров в секции SELECT мы можем столкнуться с ситуацией, когда подзапрос вернул нам более одной записи. Отсечь это можно, указав принудительно число выбираемых записей:
SELECT nom.Наименование,
(SELECT TOP 1 tg.Наименование FROM Товарная группа tg WHERE tg.ID = nom.Товарная группа)
FROM Номенклатура nom
Такой запрос выполнялся дольше на 96% по сравнению с аналогичным без TOP 1

Разбор плана показал, что 1% дополнительного времени тратится вложенный цикл, а 95% на просмотр определенных строк не кластеризованного индекса!
Из этого можно сделать один вывод: никогда не используйте без надобности ограничения в подзапросах.

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