Xpoint
   [напомнить пароль]

Работа с массивом

Метки: [без меток]
2009-11-10 05:35:01 [обр] JIEXA[досье]

Представим магазин с каталогом товаров
Каждый товар имеет такие свойства:

ID
CAT_ID - категория
IS_PIC - картинка (0/1)
IS_FAVORITE - избранный?(0/1)
CNT - кол-во товара
NAME - название товара

Товаров 500 штук.

Все товары должны быть в одном массиве. Структура массива еще создана. Но скорее всего будет такая:

$calatog = array (
                 array("id" => 1, "cat_id" => 3, "is_pic" => 1, "is_favorite" => 0, "cnt" => 20, "name" => "Пепельница"),
                 array("id" => 2, "cat_id" => 1, "is_pic" => 0, "is_favorite" => 0, "cnt" => 14, "name" => "Сигареты"),
                 array("id" => 3, "cat_id" => 5, "is_pic" => 0, "is_favorite" => 1, "cnt" => 2, "name" => "Сигареты")
);

Теперь самое главное.

Перед мной стоит задача сделать выборку из этого массива с возможностью:

условия:
1. возможность указать чтобы выбирались только товары из определенных категорий
2. выбирать только товары, кол-во которых больше 0, а если таковых нет, то выбирать избранные
3. чтобы выбранные товары имели разные категории
4. с картиной/без

сортировка:
1. сортировка должна быть непростая. должны сортироватся рандомно. НО вероятность показа тех товаров, у которых CNT(кол-во) больше - должна быть выше.

лимит
1. возможность указать кол-во товаров,которое нужно выбрать

У меня получилось примерно такое

<?
function get_tizers($catalog, $catId, $isCatUnique, $isPic, $limit)
{
   $filtred = array(); // В этот массив будем складывать отфильтрованные
   $cat_exist = array(); // Сюда будем складывать категории которые уже взяли
   
   foreach ($catalog AS $item)
   {
      if((!$catId OR in_array ($item['cat_id'], $catId)) // подходит ли категория
      AND(!$isCatUnique OR !in_array ($item['cat_id'], $cat_exist)) // если стоит флаг уникальности категории, то проверяем не брали ли мы уже сайт этой категории
      AND(!$isPic OR $item['is_pic']) // картинка
      AND($item['is_favorite'] OR $item['cnt'] > 0)) // Либо кол-вот товаро должно быть больше 0 либо он должен быть избранный
      {
         // Добавляем к item`у поле, ко которому будем сортировать с рандомом
         $item['sort_filed'] = $item['cnt']*rand(1,1000);
         
         $filtred[] = $item;
         $cat_exist[] = $item['cat_id'];
      }
   }
   
   // Сортируем
   usort($filtred, "cmp");
   
   // Обрезаем
   $filtred = array_slice ($filtred, 0, $limit);
   
   return $filtred;
}


// Функция сортировки
function cmp ($a, $b) {
    if ($a['sort_filed'] == $b['sort_filed']) return 0;
    return ($a['sort_filed'] > $b['sort_filed']) ? -1 : 1;
}
?>

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

Тестовый массив вместе с моим вариантом, можно взять здесь: http://dumpz.org/14178/

спустя 3 часа 6 минут [обр] Филипп Ткачев(20/112)[досье]
Мне вот интересно, что будет с вашим магазином, если товаров станет хотя бы штук 500.
Советую вам сразу использовать базу данных. Большую часть требований можно будет легко эффективно реализовать на SQL.
спустя 7 часов [обр] JIEXA[досье]

Филипп, нет нет нет, это не магазин. И это не товары.
Реализация совсем для другого, и как раз сейчас все реализовано на БД.
Нужно от нее уйти по некоторым причинам.
В общем это не суть.

Так что по коду можете сказать?

спустя 1 час 48 минут [обр] Давид Мзареулян(536/1003)[досье]
Тогда озвучивайте причины ухода с БД. Без этого ничего сказать нельзя.
спустя 1 час 6 минут [обр] JIEXA[досье]

Это не магазин
И это не тавары

Это баннерная система.
Есть много. Очень много баннеров.
Но они все одновременно не ротируются.
Ротируется только штук 500.

Хочу сделать, чтобы баннеры для ротации извлекались из БД в массив. А массив положить в мемкеш.

Раз в минуту массив в мемкеш будет заново загружаться из БД. (чтобы обновить ротацию)

Почему я выбрал именно этот вариант?

  1. Мне не нужна сохранность данных, если сервер перезагрузится, то массив в мемкеше заново наполнится данными из БД.
  2. Основная нагрузка как раз идет на выборке баннеров. Как олько я перенесу это на мемкеш - у меня вырастит производительность в разы!
  3. Легко масштабирование мемкеша.
спустя 9 минут [обр] Давид Мзареулян(536/1003)[досье]

Понятно. Возможно, для 500 штук это будет работать вполне быстро.

Попробуйте посмотреть ещё в сторону БД в памяти. Например, тот же SQLite умеет работать не из файла, а из памяти. Выборки будут гораздо легче и быстрее.

спустя 7 минут [обр] JIEXA[досье]

SQLite..
Мне же еще очень важно масштабирование

В принципе мой код из первого поста работает довольно быстро, если даже в массив положить 3 тыс. элементов, то скорость выполнения 0.22 сек. Но все равно хотелось бы узнать, не допустил ли я каких ошибок? Правильно то, что я начал перебирать каждый элемент foreach`ом?

спустя 1 минуту [обр] Давид Мзареулян(536/1003)[досье]
А что Вы собираетесь масштабировать? 500 элементов в мемкеше?
спустя 46 минут [обр] JIEXA[досье]
Да.
Не из-за размера естественно.
А из-за кол-ва обращений.
В общем это оффтоп.
Вопрос мой в другом был!
спустя 30 минут [обр] Давид Мзареулян(536/1003)[досье]

Мне кажется, Вам стоит определиться с потенциальным узким местом, с которым Вы собираетесь бороться. Если это обращения, то обращениями занимается PHP. Тогда масштабирование = поднятие новых машин, на которых будет крутиться тот же скрипт. В этом случае ничто не мешает скрипту брать из мемкеша дамп, разворачивать в памяти SQLIite и делать из неё выборки. Это будет горздо быстрее, чем ворочать php-массив при каждом обращении.

P. S. 0.22 сек — это безумно медленно.

спустя 1 минуту [обр] Давид Мзареулян(536/1003)[досье]
А вообще, смотря какая частота обращений у Вас ожидается…
спустя 47 минут [обр] Прокаев2(13/35)[досье]

по алгоритму:
- не хватает breaka внутри if ( если ищем уникальные и размерность $cat_exist равна $catId)
- что мешает после выборки из БД не вносить в $catalog элементы, не удовлетворяющие последнему условию(is_favorite & cnt>0)?
- завести отдельный массив для элементов, имеющих картинки, и передавать его в foreach

и экономия на спичках ;) isset чуть быстрее in_array ?

а много у вас категорий ?

спустя 6 часов [обр] JIEXA[досье]

Прокаев2, спасибо за советы

Категорий не много. Порядка 10.

На самом деле, очень интересует возможность отфильтровать сразу часть ненужных данных, до переборки массива. Или вообще избавится из foreach, возможно ли?

спустя 4 часа [обр] Прокаев2(13/35)[досье]
а почему вы не хотите проверить вариант с SQLIite ?
или же перепишите алгоритм на с, если скорость настолько критична
спустя 7 часов [обр] Давид Мзареулян(536/1003)[досье]

Прокаев2[досье] Потому что Алексею кажется, что SQLite — примитивная и тормозная база для лохов, и ручная реализация на PHP будет работать гораздо быстрее. Хотя фактически он руками делает тот же самый SQL-запрос, только лобовым образом и без малейшей оптимизации.

JIEXA[досье] Если у Вас алгоритм выборки не зависит от запроса, то выбирайте сразу в нужном порядке. Если зависит, то эмулировать SQL на PHP — очень плохая идея.

Имейте ещё в виду, что доставание данных из мемкеша и рассериализация тоже занимают время.

Если у Вас действительно большая частота запросов (сотни в секунду), имеет смысл написать постоянно живущий демон. Можно на том же PHP. Сильно сэкономите на развёртывании данных.

спустя 7 часов [обр] Thirteensmay(17/157)[досье]
JIEXA[досье] Не надо на PHP. Во первых задачи вашего рода (выборки по условиям и хитрые сортировки) а короче типичная работа с данными, имеют обыкновение со временем изменяться и усложняться, база тут будет весьма кстати. Во вторых вместо того чтобы перекладывать из базы в мемкеш а потом ковырять его PHP, лучше перекладывайте из большой обычной таблицы в маленькую расположенную в памяти, в той же базе, и пользуйтесь стандартным SQL, будет надежнее, проще, и скорее всего быстрее. Сотня-другая не сложных запросов в сек для небольшой таблицы в памяти для БД нормально, а пара сотен PHP скриптов каждый со своим массивом ? Тогда и мемкеш за избыточностью выкидываем. C масштабированием проблем не вижу, какая разница что масштабировать, серверы с кучами PHP скриптов или базы с демонами.
спустя 5 часов [обр] JIEXA[досье]

Thirteensmay, сейчас в данный момент как раз так и реализовано. Т.е. данные лежат в большой таблицы MySQL, а текущая ротация лежит в HEAP таблице того же мускула.

Но что я буду делать, когда мне станет не хватать одного сервера? Как я буду масштабировать HEAP таблицы мускула? Если они не поддерживают этого... В этом то и проблема

спустя 5 часов [обр] Филипп Ткачев(20/112)[досье]

У меня сделана простая баннерная система. Простая выборка.
Сейчас в таблице 50 баннеров, тоже MySQL.
Компьютер Intel Celeron 700 MHz, 128 RAM, FreeBSD 7, Apache/2.2.6.

$ ab -n 100 http://vlib.scc/pro.php
...
Requests per second:    71.34 [#/sec] (mean)
...

Какие могут быть тормоза, если сервер будет быстрым. Напишите на C модуль к nginx вместо php.
И будет летать со скоростью пули.

спустя 1 час 16 минут [обр] Thirteensmay(17/157)[досье]
JIEXA[досье] А как вы собираетесь масштабировать PHP ? FastCGI ? Ну так точно также, на запрос балансировщика будут отвечать демоны работающие с каждой конкретной базой
спустя 20 часов [обр] JIEXA[досье]

Thirteensmay
еще пока не решил как :)

Вообще по хорошему мне бы найти решение БД которая поддерживает хранение данных в оперативке и масштабируется легко.

спустя 6 часов [обр] Филипп Ткачев(20/112)[досье]
SQL_CACHE попробуйте. http://www.petefreitag.com/item/390.cfm
спустя 2 часа 18 минут [обр] Давид Мзареулян(536/1003)[досье]
JIEXA[досье] Всё-таки, расскажите, что Вы понимаете под масштабированием? Пока это звучит как мантра, без малейшей конкретики.
спустя 14 часов [обр] JIEXA[досье]

Филипп Ткачев, SQL_CACHE не получится задействовать, т.к. в запросе присутствует ORDER BY RAND()*clicks DESC - а из-за этого мускул не кеширует...

Давид Мзареулян, сразу признаюсь, я до этого не сталкивался с масштабированием, но себе представляю это так: как только мой сервер перестанет справляться с нагрузкой, я куплю еще один, поставлю там MySQL и настрою репликацию. НО MySQL не поддерживает репликацию HEAP таблиц.

Хотя мне всего-то надо, чтобы:

  1. На этих двух серверах данные в этих HEAP таблицах были одинаковые.
  2. Раз в минуту они очищались, и брались новые из основной таблицы.
  3. Чтобы при выборке балансировалась нагрузка по серверам.
спустя 8 часов [обр] Thirteensmay(17/157)[досье]
Синхронизатор таблиц в крайнем случае (если ничего готового не найдете) можно сделать самому, при вашей задаче это по идее не сложно. Балансировку можно делать не на этапе выборки а на этапе определения кто будет обрабатывать запрос, есть соответствующие модули для web серверов.
спустя 10 часов [обр] Давид Мзареулян(536/1003)[досье]

JIEXA[досье] смотрите, прежде всего надо понять, откуда возьмётся эта «нагрузка», с которой Ваш сервер не сможет справиться. У Вас есть основная база, из которой Вы периодически вытаскиваете данные для ротации и их показываете. Собственно работа заключается в показе, основная база по большей части бездельничает. Правильно? Тогда нагрузка на основную базу никогда не станет существенной. Из неё как доставались раз в минуту данные, так и будут доставаться.

А разослать эти данные по N реально работающим серверам и дать им команду на обновление базы в памяти — это тривиальная задача.

Powered by POEM™ Engine Copyright © 2002-2005