Как стать автором
Обновить

Комментарии 90

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

ЗЫ: А масштабирование базы надо делать заранее. Чтоб потом не кодить с пеной у рта. Так что начинайте прям сейчас ;)
> ну только надо понимать, что для связей далеко не всегда нужна отдельная таблица. Только лишь в случае «многие ко многим».

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

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

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

По поводу вашего предложения я думал, более детально расписать принцип, с примерами кода, но сначала решил изучить интерес сообщества к этой теме.
Да, еще замечанице. Это отличная теория, но, как всегда, на практике иногда бывает все не так просто :)
Возьмем ваш пример избранных песен. Пусть у меня 100 песен в избранном и я хочу посмотреть этот список. Тогда сначала пойдет запрос в кеш на лист… а потом 100 запросов в кеш на песни. Иногда такое неприемлемо (например этот список не мой, а общий, и он висит на главной). Иногда рациональней закешировать все и сразу, пожертвовав атомарностью. Просто делать это надо аккуратно и с умом :)
«мы используем его, т.к. он». Кто он?
XCache
Я использую eAccelerator, просто не нарадуюсь.

Хорошо если бы кто-нибудь написал статью о правильной настройке memcached и eAccelerator.
>Хорошо если бы кто-нибудь написал статью о правильной настройке memcached и eAccelerator.

Поддерживаю
Звучит примерно как «Я пользуюсь линуксом и нарадоваться не могу! Никто не подскажет как им пользоваться?» :)
Интересна не тока теория, но и практика, как это сделано. Не секрет, что не все хорошо могут понять в теории, некоторым нужен пример реализации (а ещё желательно не тривиальный, а по сложнее)
Я планирую написать вторую часть с примерами.
ждём примеров!
Когда вы создаёте базу данных под некоторым реляционным сервером БД, то в этой базе данных автоматически создаётся набор таблиц описывающий структуру ваших данных. То есть и ваши сущности и связи между ними. Называется этот набор — Dictionary. По сути — создать базу данных это и есть создать такой описательный набор таблиц (мета-словарь). К нему всегда можно доступиться.

Таким образом, если не изобретать велосипед, а прочесть учебник и сакцентироваться на грамотном проектировании таблиц хранящих непосредственно экземпляры сущностей, то вы, во-первых, избавитесь от низкопроизводительных многоэтажных запрсов, во-вторых, почувствуете на себе всю мощь кеширования правильно настроенного SQL сервера, в третьих — cэкономите время на разработке и отладке собственных доморощенных решений, и, в четвёртых, заметно сэкономите деньги и время того, кто платит за проект. :-)
Вы уверены, что вы сейчас про MySQL говорите?

P.S. они (MySQL), кстати, сами рекоммендуют использовать memcached, и даже storage engine пишут на его основе.
Во-первых — вы не упоминали ваш сервер БД.
Во-вторых — в том или ином виде это есть у всех серверов. А как вы думаете каким образом сервер будет получать информацию о своих объектах что бы строить запросы?

www.quest-pipelines.com/newsletter-v6/0905_A.htm

Видите, вы начали проектировать не разобравшись как устроены сервера баз данных. Это опасная практика. :-)
Уверен, что разбираюсь в MySQL достаточно, чтобы понять, что вызов подсистемы (MySQL) + TCP-транзакция однозначно дороже, чем прямое обращение к ОЗУ из PHP. Мы ведь даже не memcache используем, а xcache, чтобы не по TCP работать. Да, здесь есть проблемы с тем, что у нас не получается кеш-серверов наплодить, но мы работаем над этим. Да и кластеры на основе MySQL вещь довольно опасная. Отсюда вывод, что кеширование сейчас для нас — оптимальный вариант.
Дело не в том, что кеширование плохо, а в том, что вначале вы драматически понизили производительность работы вашей БД за счёт «принципа супергибкости». А теперь пытаетесь это хоть как-то скомпенсировать кешированием. Забывая про то, что при многопользовательском транзакционном доступе к данным, кеширование есть непростая задача. То есть вам придётся довольно изощрённо управляться с вашим кешем. Что для многих случаев (в силу необходимости вручную актуализировать кеш) ещё больше понизит производительность вашей системы и скорее всего будет чревато большим количеством ошибок.
Вот здесь мы с вами наконец-то начали понимать друг-друга. Я просто не в праве говорить почему нам нужна «супергибкость», но нужна. И, да, прежде всего, я пытаюсь ее скомпенсировать.

И, да, использование кеширования на любом уровне заставляет нас быть предельно аккуратными с данными, так как их актуальность теперь полностью на нашей совести.
Ну надо значит надо. Просто обратил внимание, что вы частично продублировали структуры самого сервера.
Спасибо за то, что озвучили альтернативный взгляд на кеширование, надеюсь это сделало пост интереснее для читателя.
«Называется этот набор — Dictionary. По сути — создать базу данных это и есть создать такой описательный набор таблиц (мета-словарь). К нему всегда можно доступиться.»

Может пример приведёте или статью напишете? :)
Видимо имелась ввиду information_schema, если мы говорим о MySQL.
Ну, мне интересно увидеть разные примеры, как кэширования, так и правильного использования базы данных. Почему и попросил объяснительную статью профессионала :)
Вы как-нибудь решаете проблему блокировок кеша?
Т.е., кеш пуст, приходит одновременно 100 пользователей, необходимо перестроить кеш. Перестройка кеша длится, допустим, 1 секунду. Остальные 99 пользователей ждут или тоже перестраивают один общий кеш?)
Угу. А ещё есть проблема изолированности транзакций. Ибо синхронизация кеша с реальными данными должна делаться с учётом этого. Просто ребята не прочли как устроен сервер баз данных и начали писать свой. Похоже у них не бедный заказчик. :-)
А разработчики процессоров, не учли что есть оперативная память, и зачем-то сделали кеш прямо в процессоре. У них тоже не бедные инвесторы.
Если у вашего заказчика такой же бюджет как и у разработчиков процессоров, то и вы сделайте.

Хотя, у меня есть сильное чувство что это несколько разные кеши. Хотя бы потому, что процессор только сам доступается к своему кешу. А если ему нужно позволить туда доступаться другим контроллерам, то для этого разрабатывается специальная технология. И часто довольно не простая.
Аналония прямая. в xcache данные хранятся в родном для php виде, даже массивы. То есть не надо ничего никуда преобразовывать. Фактически мы храним в разделяемой памяти значения переменных.
Аналогия*
> 1 секунду
это очень большое время, мы стараемся избегать таких таймингов с разделяемыми (не персональными) кешами. Действует правило не более «0,01» секунды. Если в это время не укладываемся, то ищем другой способ решения проблемы.
+ ещё вопрос — на хостинге обязательно должна быть поддержка кеширования с немалыми объемами. Вот это, к сожалению, накладывает ограничение на само решение.
Размер в небольших проектах точно будет больше, чем 32 метра)
Мы пока в 64 укладываемся — посещаемость (20000 — 40000) в сутки
страниц или уникальных пользователей?
пользователей.
в новом проекте использую кэширование в виде memcached
есть несколько вопросов по работе с ним, но нигде не смог найти на них ответы. Очень надеюсь, что хабролюди смогут помочь.
Суть вопроса: почему в memcached под php нет возможности делать get_multi delete_multi запросы. Под Perl такое вроде как предусмотрено.
неужели все профи разбежались?
большинство людей читают хабр с работы ;)

про get_multi

«Memcache:: get() returns previously stored data if an item with such key exists on the server at this moment.
You can pass array of keys to Memcache:: get() to get array of values. The result array will contain only found key-value pairs. „
А можно пример многоэтажного запроса?
А вам насколько многоэтажный? :)
Я хочу сделать определение пользователей онлайн на сайте.
По команде set в memcache заношу данные о том, где находится юзер с ключем «online_»+userId например на 180 секунд.
Как дальше мне посчитать, сколько в данный момент юзеров онлайн и при необходимости всех их найти?
Не надо использовать кеш как базу данных. Он создан не для этого.
А для чего?
база данных предоставляет логику выборки. Кеш — нет. И не надо пытаться его этому научить.
Мы не учим его этому, просто храним в нем промежуточные этапы выборки. А IDы и данные разделяем только для того чтобы избежать дублирования данных в кеше.
В конкретно этом случае вы пытаетесь его научить хранить изменяемую таблицу. Таблицу бд с единственным полем. Для этого вы храните по одному ключу список возможных(!) айдишников и еще каждый каждый айдишник по своему ключу. Выборка из такой «таблицы» нерациональна.
Список IDов — не практически неизменяем и существует для каждого случая. Например это могут быть «Список любимых песен пользователя», «Список песен в эфире для хорошего настроения». Таким образом выборки из этого набора не делаются и условия на него не накладываются, а он используется целиком.
со списком песен да — они не динамичны.
Я имел ввиду ситуацию: Если вычитка списка производилась из кеша, то убиваем в списке тех, кого в кеше нет
Вам приходится проверять. Потому что это постоянно изменяемая информация.
Ну, как вы заметили, я ниже про таблицу и написал.
рад, что мы пришли к консенсусу ;)
Список IDов — не практически неизменяем и существует для каждого случая. Например это могут быть «Список любимых песен пользователя», «Список песен в эфире для хорошего настроения». Таким образом выборки из этого набора не делаются и условия на него не накладываются, а он используется целиком.
Насколько я знаю, на данный момент кешеры не предоставляют таких возможностей. Если бы нужно было хранить эту информацию в кеше, мы бы сделали это так:
хранили бы массив ID пользователей онлайн в кеше, обновляя его, в момент пояления пользователя на сайте или ухода. И выбирать пользователей из кеша по ключу «online_»+ID, при необходимости.
Тогда вам придется в списке кроме ID хранить еще и время его занесения.
Нет, запись в кеше убъеться сама, если пользователь не проявит активность.
>хранили бы массив ID пользователей онлайн в кеше, обновляя его, в момент пояления пользователя на сайте или ухода
>Нет, запись в кеше убъеться сама, если пользователь не проявит активность.
Не противоречите ли вы сами себе? Каким образом запись должна сама убиться из массива?
Поясню алгоритм работы.
1. Пытаемся загрузить массив ID онлайн-пользователей из кеша, если нет, то создаем его из БД.
2. Если вычитка списка производилась из БД, то по необходимости вычитываем пользователей из кеша, тех кого нет вытаскиваем из БД и кладем в кеш.
3. Если вычитка списка производилась из кеша, то убиваем в списке тех, кого в кеше нет, ибо их уже нет на сайте. Делаем соотвествующую отметку в БД (если надо).
4. Каждое действие пользователя на сайте пролонгирует его запись в кеше онлайн.
как будет вести себя ваш алгоритм на сайтах, где одновременно будет сидеть 1-10К юзеров? Сколько каждый юзер будет генерировать запросов к БД за одно обновление страницы? Какая база выдержит такую нагрузку?
У нас проблем нет с 2К.

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

Вам не кажется что от чего-то тут можно отказаться, и это «что-то» в данном случае не бд?

ЗЫ: Тем более вы уже написали ниже про storage engine и в этом я с вами полностью солидарен.
Мне все-таки кажется, (я не уверен, буду рад, если кто-то откроет мне глаза), что принцип подготовки результата на стороне MySQL не сильно отличается от собирания данных из кеша.

Другое дело, что использовать в продакшн коде массивы данных размером в 2000 записей — это абсурд.
Может и не очень, но вы собираете данные из кеша не в кеше, а в коде. Мускуль же собирает сам. Если учесть, что эти данные вы в Мускуле все равно храните(!) звено кеша — лишнее.

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

А если вы про списки «кто сейчас онлайн» на форумах, то, как говорит один мой хороший знакомый: «остается написать „Они отдали себя общению“, как на мемориальной доске.
а кто сказал, что все 2000 записей надо отдать пользователю? Мало ли что я с ними делаю в скрипте.

Ну а про рациональность списков «Онлайн» тема отдельная. На большинстве сайтов он, конечно же, не нужен.
Вообще в этом случае можно завести просто таблицу session с storage engine — Memory и забить на кеш. Мне кажется, что так правильнее.
вот вот
а как вы собираетесь определять момент ухода???
в принципе я уже смог с помощью php найти список всех ключей в memcache. Теперь уже не проблема из этого списка выбрать нужные мне данные.
Просто думал, что кто-то сталкивался с подобным и уже есть готовый вариант решения этого вопроса.
цитата «Но, при такой структуре запросы к БД становяться многоэтажными.»

я не знаю про какую многоэтажность тут пишут
попробуйте заджойнить 4-5 таблиц — вот вам типичный пример.
left join = select… from (select… from where… in (select..))???

В чём проблема 4 join-ов?

да всё это в контексте приведенных примеров с пестнями и артистами :)
просмотрел

простите но такие фразы срубают наповал :( после 2-ой дальше читать уже не захотелось.
Да и про многоэтажность я там так ничего и не нашел :(

«Если запрос вида
SELECT * FROM table WHERE id=123
тормозит, то ему скорее всего не хватает
скорости дисков.»

«3. при изменении данных изменяем их в кэше,
а в MySQL пишем данные раз в час
•вообще не используется mysql!
•вообще не используется диск!»
у нас явно разные понятия о многоэтажности
Видимо из-за того что и пользуемся мы разными СУБД :(
Кто учил писать такое зло? вложенные select в mysql — это зло, работают медленно и даже не кэшируются.

Статья сумбурная, всё расплывчато и без примеров. Посмотрите как пишут статьи о крупных проектах, расписывают таблицы, описывают запросы «до» и «после» оптимизации, делают выводы.
«Кто учил писать такое зло?»

вполне себе обычный запрос, уже лет 10 как назад оптимизаторы умели разруливать такие запросы. :)
Когда делали mp3.ru, лечили кэш таблицами. Варианты с листингом ста тысяч треков постранично с сортировкой даже по одной таблице уже тупит ближе к концу, а если джойнить то работать будет до первой тысячи посетителей. Крон тулзы, собирают кэши каждые 5,10,30 минут, заранее отсортированными и денормализованными и все ок ;).
Я думаю в этом случае уже было бы логичнее файловое кеширование (html страниц). И потом, нафига листинг ста тысяч треков, какая от него польза?
Файловое кэширование тут накладнее, как в разработке, так и в процессинге. Если вы имеете ввиду регенерацию файлов кэша при заходе пользователя, то тупить будет для множества пользователей. Если апдейтить все кэш файлы раз в час например, то это гораздо накладнее, чем пересобрать таблицу. Согласен, что будь все это готовыми закэшированными блоками было бы еще быстрее, но пользы от кэш таблиц как правило больше, потому что на них могут опираться сразу множество утилит. Нафига листинг — этот вопрос решают обычно не разработчики ;). Мое мнение, каталог есть каталог, хоть 100 тысяч, хоть миллион.
Слава богу на нашу дискуссию не давит ТЗ mp3.ru, и нам можно рассуждать вне его рамок, это я про листинг.

А по поводу кеш таблиц, я согласен, сами используем их для поиска.
а как вы относитесь к кешированию самих sql запросов c md5() ключом от него?
т.е. строим строку запроса, берем от нее md5(), если в кеше есть — вернуть результат из кеша (массив объектов), если нет — то выполнить запрос и положить в кеш.
И сбрасывать кеш в том случае, если в таблице(связанныех таблицах) произведено обновление/добалвнение
Ведь все запросы почти возвращают один результат в какой-то определенный промежуток времени.
Насколько я знаю собственное кеширование MySQL так и работает, так что вы просто переписываете его механизм. В этом кстати легко убедиться, если прогнать два раза подряд один сложный запрос. Первое время будет большое, а второе очень маленькое.
зато выигрываем(?) время на парсинг объектов из базы;)
Да, кэширование мускуля то же самое делает. Если мускуль на другом сервере, может немного сэкономите на сети. По факту же, скорее всего ток проиграете на таком кэшировании. Слежка за изменениями таблицы тоже требует запросов (не важно тригеры это или просто кверики проверяющие количество или стампы).
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории