discount_transactions_iptraffic_all и discount_transactions

Технические вопросы по UTM 5.0
Ответить
andrew.rbe
Сообщения: 36
Зарегистрирован: Ср фев 10, 2010 14:05

discount_transactions_iptraffic_all и discount_transactions

Сообщение andrew.rbe »

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

Что есть - время агрегации трафика 5 мин.
Списание периодической услуги - раз в месяц на пользователя.


Думаю, у многих коллег написаны свои интерфейсы к UTM со своими отчетами и тд, а у кого их нет, тот рано или поздно приходит к этому. Например, нужно, чтобы при нажатии на кнопку заполнялся тикет для тех. отдела, нужны минимальные скрипты диагностики из интерфейса и т.д., и, конечно же, отчеты, чтобы можно было доходчиво объяснить абоненту, что 5го числа у него закончился предоплаченный трафик и насчиталось 10грн трафа по превышению, ну или так: 6-го числа абонент заплатил 1 гривну и на счету у него 1 гривна, а 7-го числа у него снялась абонплата 2 гривны, поэтому он заблокирован.
Посмотрим, как мы можем это организовать. Для данных отчетов есть 3 таблицы: payment_transactions, discount_transactions_iptraffic_all, discount_transactions.
Первая содержит данные по платежам (комментарий к платежу, метод и тд).
Вторая хранит данные о трафике (какого класса был трафик, сколько, его стоимость).
В третью пишется все выше перечисленное и данные о периодических слугах.
Попробуем организовать отчет, содержащий следующие данные: дата, входящий остаток, название услуги, сумма.:
01.01.2011 | 100 | Абонплата | 5
01.01.2011 | 95 | IP трафик | 10
01.01.2011 | 85 | Доп. Услуга | 50
01.01.2011 | 35 | IP трафик | 50
Т.е. мы не будем показывать данные, какой именно класс трафика. Нам просто важно знать, что в период между снятием абон. платы и доп. услуги был трафик и за него были сняты деньги. На первый взгляд discount_transactions нам подходит. Нам необходимо просто выбрать записи по 2м параметрам – ид аккаунта и дата. Смотрим дальше - данные о трафике поступают каждые 5 мин (время агрегации в настройках UTM). Значит необходимо группировать выборку и суммировать по полю суммы: GROUP by slink_id , YEAR(from_unixtime(discount_date)), MONTH(from_unixtime(discount_date)), DAY(from_unixtime(discount_date)). Тут получаются некоторые грабли: если мы выбираем еще поле incoming_rest то при группировке мы можем выбрать только первое значение в сгруппированных рядах. В нашем случае, услуга «IP трафик» будет упоминаться один раз в отчете. И входящий остаток будет – 95. Получим такой отчет:
01.01.2011 | 100| Абонплата | 5
01.01.2011 | 95 | IP трафик | 60
01.01.2011 | 85 | Доп. Услуга | 50
Разобраться в данном отчете НАМНОГО сложнее – 95 отнимаем 60 – получаем 35 а не 85 –как в отчете. Грубо говоря, подобного отчета не получатся. Нам необходим, какой-то параметр, по которому мы будем группировать выборку – его нет.
Отсюда такое предложение – писать в дополнительную колонку ид последней транзакции другого типа. Если записываем что был трафик - то в доп. колонке будет ид транзакции со списанием периодической услуге. Не знаю насколько правильно со стороны теории БД :(.
Вспомним что таблица эта – огромна. Из нее возможно нормально выбрать только отчет по периодическим услугам. Зато есть ненужное поле - outgoing_rest. Дополнительно вспомним что в mysql нет индексов по функциям – те мы не можем уплотнить индекс по дате округлив его до часа или например (совсем круто) до даты последнего списания.
Вопрос: зачем писать в эту таблицу ip трафик?
В таблицу пишутся дополнительно платежи. Зачем? Да это несравненно меньшее количество записей. Почему не сделать 1 большую (по строкам а не по длине ряда) таблицу – ид(автоинкремент), дата, ид аккаунта, вх. остаток, сумма, ид услуги, slink_id…….. и тд; со всеми необходимыми индексами. И три таблицы поменьше – с трафиком, периодическими услугами и платежами связанные с первой по первичному ключу (ид). Если поля не будут повторяться – то суммарно таблицы будут меньше по объему. Не факт что это правильно. Или просто не писать туда ип трафик - но тогда следующие грабли: в таблице с трафиком нет поля вх. баланс, которое (думаю многим) крайне необходим. Главный тезис - ликвидировать дублирование на таких огромных таблицах. Почему упразднили такую замечательную утилиту как оптимизатор? Какая разница мне, что 1го июня именно в 17:04 у меня был трафик? Мне важно знать что он действительно был в этот день и если посчитать все списания то баланс сходится и провайдер меня не обманывает.
Сейчас у нас дела обстоят так: 70 Гб – данных в архиве. Это без индексов. Индексы занимают 14 Гб. Индексы свои и используется MyIsam:
Для таблицы с трафиком индекс по полям ид аккаунта и discount_date_hour
Для второй таблицы после архивирования делается
"ALTER TABLE ${db}.discount_${year}_${month} ADD discount_date_hour INT NOT NULL"
И после – такой же индекс. Т.е. для архива необходимо 14 Гб памяти. И каждый следующий месяц это значение будет увеличиваться на 1Гб и больше. Сейчас конец месяца и вот что получается по еще не архивированным данным:
-rw-rw---- 1 mysql mysql 8.9K 2011-01-28 10:38 discount_transactions_all.frm
-rw-rw---- 1 mysql mysql 4.4G 2011-01-31 21:53 discount_transactions_all.ibd
-rw-rw---- 1 mysql mysql 9.0K 2011-01-28 10:55 discount_transactions_iptraffic_all.frm
-rw-rw---- 1 mysql mysql 4.8G 2011-01-31 21:53 discount_transactions_iptraffic_all.ibd
Получаем еще 9Гб памяти.
Теперь про архивирование. Было предложение делать просто переименование таблиц и пересоздавать их. Мне этот подход не нравится – хочется архивировать по месяцу, чтобы в таблицу попал строго один месяц. И все тут хорошо – но получаем доооолгий делит. И не один. Почему из биллинга не складывать статистику по месяцам? Неужто так сложно дописать данный функционал? Плохо что приходится городить костыли чтобы трафик безлимитчиков складывался например раз в час.


Кто все это осилил - прокомментируйте: какие индексы используете, стоит ли отказываться от архивирования строго по месяцам ? Ибо при текущем положении надо или немеринно памяти или забить на быстрые выборки статистики.

Аватара пользователя
Magnum72
Сообщения: 1947
Зарегистрирован: Чт сен 22, 2005 06:54
Контактная информация:

Сообщение Magnum72 »

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

andrew.rbe
Сообщения: 36
Зарегистрирован: Ср фев 10, 2010 14:05

Сообщение andrew.rbe »

Magnum72 писал(а):Ну в принципе, если не хочется так как предложено, есть еще один замечательный вариант, это триггеры. Создай свою табличку и триггерами на вставку данных в основные таблицы транзакций, формируй свою, причем ты в своей можешь сразу же аггрегировать одинаковые списания по часам или по суткам, можешь распихивать по разным ежемесячным таблицам, итп.
Могу. Скоро буду писать триггеры для этих таблиц. Я вобщем то ныл больше про плохую изначальную продуманность таблиц со списаниями. Был совсем радикальный метод - переделать таблицы в BLACKHOLE и триггерами писать данные в собственные таблицы, но думаю это слишком радикально :).

andrew.rbe
Сообщения: 36
Зарегистрирован: Ср фев 10, 2010 14:05

Сообщение andrew.rbe »

Вот еще идея, может кому то понадобится :). По поводу логинчных отчетов. Группировать можно по полю incoming_rest. тогда, как в примере будут 4 строки а не 3 как было бы без группировки дополнительно по этому полю :). Как то сразу не догадался :)

Витька
Сообщения: 236
Зарегистрирован: Вс дек 16, 2007 21:54

Re: discount_transactions_iptraffic_all и discount_transacti

Сообщение Витька »

andrew.rbe писал(а): Теперь про архивирование. Было предложение делать просто переименование таблиц и пересоздавать их. Мне этот подход не нравится – хочется архивировать по месяцу, чтобы в таблицу попал строго один месяц. И все тут хорошо – но получаем доооолгий делит. И не один. Почему из биллинга не складывать статистику по месяцам? Неужто так сложно дописать данный функционал? Плохо что приходится городить костыли чтобы трафик безлимитчиков складывался например раз в час.
По поводу архивирования: есть два подхода.
Первый - в лоб. Выдернуть данные в отдельную таблицу, потом удалить их за ненадобностью. Где-то я слышал или читал, что после удаления каждой строки перестраиваются индексы, поэтому DELETE занимает столько времени.
Второй вариант в теме "Архивирование списаний" предложил Магнум.
В чуть-чуть доработанном мной виде он выглядит так:
1. Переименовываем архивируемую таблицу, давая ей временное имя.
2. Смотрим значение autoincrement для неё.
3. Создаём по её образцу пустую таблицу вместо переименованной и устанавливаем autoincrement, который запомнили. С этого момента данные снова текут туда, биллинг работает как ни в чём не бывало. Все эти операции занимают ничтожное время и у меня не было никаких проблем даже при работающем биллинге.
4. Во временной таблице, содержащей неархивированные данные, находим MIN(time). Из этой даты я вычисляю год и месяц для самого раннего неархивированного месяца и генерирую переменные $start = mktime(0,0,0,$month,1,$year), $end = mktime(0,0,0,$month+1,1,$year)-1. Так я могу быть уверен, что у всех архивов для того месяца будут одинаковые интервалы.
5. В цикле перебираем месяцы, выдирая данные в архивную базу с архивными таблицами, пока не дойдём до месяца, предшествующего текущему.
6. Селектом возвращаем данные текущего месяца в настоящую таблицу.
7. Дропаем временную таблицу.
8. Создаём запись в таблице archives
Естественно попутно используется преобразование архивных таблиц в MyISAM и прогон через myisampack.

Аватара пользователя
Magnum72
Сообщения: 1947
Зарегистрирован: Чт сен 22, 2005 06:54
Контактная информация:

Сообщение Magnum72 »

8. Создаём запись в таблице archives
Естественно попутно используется преобразование архивных таблиц в MyISAM и прогон через myisampack.
Оппа, а у вас разве эти таблицы изначально не в MyISAMе ?
Просто хранить их в иннодб смысла нет никакого, транзакции на них не используются, объединенные индексы тоже. и я бы предложил еще один пункт, это блокировка таблицы во время этих операций, биллинг перетопчится на столь короткое время :)

Витька
Сообщения: 236
Зарегистрирован: Вс дек 16, 2007 21:54

Сообщение Витька »

Magnum72 писал(а): Оппа, а у вас разве эти таблицы изначально не в MyISAMе ?
Просто хранить их в иннодб смысла нет никакого, транзакции на них не используются, объединенные индексы тоже. и я бы предложил еще один пункт, это блокировка таблицы во время этих операций, биллинг перетопчится на столь короткое время :)
У нас всё изначально по принципу "как биллинг сказал, так и будет". Нужды оптимизировать не возникало, база не такая уж и большая по сравнению с тем, что здесь иногда приводят :)
Поэтому и архивные таблицы я создаю с помощью CREATE TABLE LIKE, хотя можно вручную перечислить необходимые столбцы. И даты при архивировании можно округлять, и индексы свои навешивать. Из возможных оптимизаций я только делал пересортировку данных по account_id
Витька писал(а): 4. Во временной таблице, содержащей неархивированные данные, находим MIN(time).
И, да. На самом деле MIN(time) я делаю ещё до переименования, чтобы можно было принять решение, нужна ли вообще архивация. Таким образом ничего страшного не случится, если скрипт будет запущен случайно, когда архивация уже проведена.

Ответить