Здравствуйте! Есть статья, к статье есть древовидные комментарии. Комментарии, естественно, кешируются — абсолютно все дерево, ни по каким страницам не разбито (например, на Хабре, у любой статьи дерево каментов посмотрите). Кешируется все средствами PEAR::Cache_Lite — из базы вытаскиваем комментарии, генерируем ХТМЛ код и кешируем его в файлик. Так вот проблема в том, что после публикации свежей статьи на протяжении пары-тройки часов, комментарии льются буквально каждую секунду, а-то и по несколько комментариев в секунду. Так вот вопрос — что и как здесь кешировать — ибо через секунду появится 1–3 новых комментария и смысл кеша просто пропадает. Что делать? (В Гугле ответ не искал, т.к. даже не могу сформулировать по каким ключевым словам (кроме «cache») искать )) UPD Достаточно комментариев по-делу, я рад, что тема получила отклик, но проблема (в принципе) еще не решена. Поэтому, хочу здесь еще кое-что прояснить. Как вы уже поняли, ситуация надуманная. Естественно, нету у меня ресурса на котором зарегистрировано стотыщ мильёнов человек и в онлайне сидят несколько миллионов одновременно и поэтому комментарии к статьям сыпятся каждую секунду. Это просто пример. Возможно преувеличенный, но не думаю, что 1 комментарий каждую секунду это что-то новое для сервиса с огромной посещаемостью. Неужели ни у кого никогда не возникало вопроса — а что делать с кешем, если его приходится обновлять очень часто. Каковы тогда его достоинства и нужен ли он вообще, если мне приходится каждую, скажем, секунду-три его удалять и обновлять? Получается, что кеш просто несостоятелен. Тогда что идет ему на замену, какая технология? Псевдорешения проблемы Предлагают отказаться от файлового кеша и использовать памяь (memcached). Ок, спасибо, я знаю что такое memcached, но суть проблемы не в файловом или другом типе кеширования. Как файлы мне придется стирать/обновлять каждую секунду, так и memcache мне придется очищать/обновлять каждую секунду. И тот и другой подход теряет смысл. Предлагают делать кеш каждые, скажем, 100 секунд, к примеру. Или нелинейной функцией рассчитывать время кеширования. Окей. Тогда, получается, что вновьзашедшему пользователю будет выдаваться старый кеш! — Прошло 100 секунд, сделали кеш, следующий кеш будет сделан через 100 секунд… А что будет видеть пользователь, который зашел на 175-ой секунде? Ога, праильно, он будет видеть старый кеш семидесяти пяти (75) секундной давности. А у нас по условию, за эти 75 секунд добавляется куча новых комментов. Но пользователь будет вынужден лицезреть их только на 200-ой секунде. Пффф… Предлагают делать кеш каждые, скажем, 100 секунд, к примеру, а в промежутках выдавать этот самый кеш + новые комментарии, взятые из базы. Мдяяя… ну и как вы это организуете? Древовидные комментарии — добавление коммента идет либо в конец всех комментов, либо в какую-нибудь ветку. Ну и как, скажем новые комментарии мы будем впихивать в уже существующий кеш? В конец кеша да, без проблем. А в ветку? Яваскиптом? Ппц, дорогая редакция. Все предложенные решения несостоятельны. Детский сад. Решение проблемы Проблема не решена. Придется делать как в ЖЖ (примерно — я, лично, не знаю, как у них там все организовано) или комбинировать — 1) разбивать комменты на страницы и кешировать каждую страницу; 2) кешировать каждый комментарий первого уровня и его ветку 3) комбинировать 1-й и 2-й способы. Так, примерно, и происходит в ЖЖ — вот только прекрасно видно сверхтормознутость (в каментах у Темы, скажем) и невозможность создать больше 10 000 комментариев. UPD2 Вообщем, тема может быть закрыта. Остановлюсь на версии, что даже кеш на 1 секнду будет успешен и сделает то, что от него требуется.
artuska, неужели будет так критично, если пользователи увидят сообщение не сразу же после добавления, а через 5 секунд? Почему бы не поставить кеш от 5 секунд? Тем более, это только крайне незначительная часть сайта с такой посещаемостью и необходимостью столь регулярного обновления.
Хм, хорошая мысль, но юзер привык видеть свое сообщение сразу же. Иначе он добавит комментарий, но не увидит его после обновления страницы, следовательно, возьмет и второй раз тот же комментарий добавит. В результате через 5 секнуд у нас будут 2 одниаковых коммента. Так не годится. В любом случае, даже такой кеш (на 5 секунд, как вы предлогаете) несостоятелен — ну что это за кеш на 5 секунд? Абсурд. Пропадает смысл кеша.
artuska, давай на "ты". А что если сделать ДжавоСкрипт. Пользователь нажал кнопку "отправить" - сообщение передалось Аджаксом, закоммитилось и появилось на странице, а пользователь даже не знает, что оно пока не обновилось в кеше. А можно и написать: "Появится на виду у всех через 4 секунды" А в чем проблема? Если добавляется 5 комментариев в секунду к теме, то это где-то 100 просмотров за секунду (я думаю). Значит данные будут получены из кеша 500 раз. По-моему этого уже достаточно, чтобы кеш считался молодцом, что так хорошо отработал. А когда количество комментариев уменьшается до 1 за 10 секунд - можно уже кешировать сразу после обновления комментария.
Про Аякс это без проблем так сделать. Это не обсуждается. Так же, проблема не с тем, кто постит комментарий, а с теми, кто их читает. За 5 секунд запостятся примерно 10 новых комментов, но люди их не увидят, если зайдут на сайт в промежутке между 1-й и 5-й секундой. Человек будет обновлять броузер каждую секунду и только на 6-й секунде появятся новые комменты. Но вообщем, я согласен. Не очень изящное решение, но окей, пускай будет. Итак, первое решение найдено — обновлять кеш в некий промежуток времени (5 секунд, а не каждую секунду, как было в начале обсуждения). Но проблема остается не решенной. А самый корень проблемы — это несостоятельность кеша, если его нужно обновлять каждую секунду или каждые 5 секунд. Как не крути с секундами, но проблема остается.
Вот в этом вся правда. Если он не состоятелен - не надо его делать. Начинай кешировать спустя 5 минут после публикации материала. До этого не кешируй. Это что так трудно сделать или это не стандартно, какие проблемы?
Мдяяя… вот ты выдал. 5 минут. Пять! Это целых пять минут я должен выдават устаревший кеш? Ты думаешь о чем вообще говоришь? Вообще, на свете имеются сайты с огромной посещаемостью. Твиттеры, Фэйсбуки, Дигг и прочая ерунда. Я свято верю, что они как-то организовали вот этот самый кеш, а не делают каждый раз коннект к базе и выборку статей и прочего. Если он не состоятелен, то его нужно сделать состоятельным.
Не тупим, я что не правильно выразился? Поясняю еще раз. Выдаем свежие данные спустя 5 минут после публикации. Потом НАЧИНАЕМ керишровать. Пишется нелинейная функция, которая управляет временем кеширвания. Но обычно она сперва не кеширует вовсе или кеширует довольно долго. Поэтому см. вариант выше.
Дык я не туплю. Смотри, что ты предлагаешь: 0 минут 0 секунд — опубликовалась статья от 0:00 минут до 5:00 минут — публикуются комментарии. Это значит, что в этот промежуток пользователи заходят на сайт и каждый раз идет выборка комментариев из базы данных. Зашел юзер, произошел коннект к базе и выборка комментов, зашел второй юзер, скажем, через 10 секунд — опять коннект и выборка и так далее в течении 5-и минут. Ппц. А за 5 минут зайдет человек 100 — сто коннектов и выборок из базы. Дальше. от 5:00 и далее — ты предлагаешь начинать кешировать комменты. Окей. Ровно в 5 минут я сделал кеш. Прошло, например, 10 секунд, добавилось около 20 новых комментов, зашел пользователь — и что он видит? А видит он еще старый кеш без этих новых 20-и комментариев. Ппц. Ты это имеешь ввиду?
Ну ладно, я не имею в виду строго пять минут. Я делаю упор на то что надо динамически менять время кеширвания. Пять минут это я так ляпнул. Сделай -логарифм + 1 частоты обновления. Тоесть через некоторое время частота обновления становиться постоянной, в первые моменты времени она довольна велика и стремиться к отсутствию кеширования. Так понятнее?
artuska¸ а ты сообщи пользователям, что материал показывается с отставанием и покажи время до следующего обновления
Я тебя прекрасно понял. Но твоим способом следуя, получится так, что в некоторые промежутки времени пользователю будет выдаваться старый кеш.
интересно, что это за проект такой, где комменты сыпятся с такой скоростью.. а может тут вовсе не нужен файловый кэш, а memcache?
есть. я на dezinfo делал. там все лежит в мемкеше. в кеш сыпется все, что можно, в том числе куски HTMLи SQL данные на уровне результатов выборок. когда что-то добавляется то я сбрасываю нужные ключи в кеше и при следующем запросе кеш снова генерится. т.е. есть пост. он закеширован. как только пользователь добавляет камент, то я сюбрасывю кеш поста, топовые показатели (ТОП5 юзеров), ну и что-то там еще уже не помню. т.е. логика такая 1. в кеш все, что можно, благо memcache это позволяет. 2. при добавлении/изменении данных я удаляю нужные ключи из кеша и при следующих запросах в кеш ляжет новая инфа. 3. далее п1. например когда добавляется новый пост, то я сбрасываю много ключей. главная страница, страницы, пейджеры и и.д. практически всеь кеш собственно кому интересно: грузится аяксом. addcomm.php PHP: <?php function StopWord($text) { /* стоп слова. ищется полное вхождение, т.е. если такое словосочетание встрeчается каммент просто не поститься. */ $w[] = 'ziza'; $w[] = 'trinixy'; $w[] = 'kolyan'; $w[] = 'колян'; $w[] = 'vvw'; $w[] = 'money'; $w[] = 'васи.нет'; $w[] = 'vasi'; $w[] = 'Деньги'; $w[] = 'Яндекс'; $w[] = 'mixeron'; $w[] = 'затрат'; $w[] = 'фишки'; $w[] = 'fishki'; $w[] = 'pаrk'; $w[] = 'x'; $w[] = 'name'; $w[] = 'upload'; $w[] = 'files'; $w[] = 'zippp'; // ггг foreach($w as $a) { if(strstr($text,$a)) return false; } return true; } if(isset($_REQUEST['q'])) extract($_REQUEST['q']); $id = (int) isset($post) ? $post : 0; $form = true; if($id) { if(!empty($nick) AND !empty($text) AND StopWord($text)) { $nick = trim($nick); $text = trim($text); $name = htmlspecialchars(ResolveSlashes($nick)); $nick = mysql_escape_string(ResolveSlashes($nick)); $text = ResolveSlashes($text); if( ( !strstr($text,'http://') AND !strstr($nick,'http://') ) AND !empty($nick) AND !empty($text)) { $add = true; if($OOPSGlobal['SES']->uid->uid == '0') $last = $OOPSGlobal["SES"]->db->QueryObject("SELECT * FROM `comments` WHERE user = '".$OOPSGlobal['SES']->uid->uid."' ORDER BY date DESC LIMIT 1"); // $last = $OOPSGlobal["SES"]->db->QueryObject("SELECT * FROM `comments` WHERE post = $id AND user = '".$OOPSGlobal['SES']->uid->uid."' ORDER BY date DESC LIMIT 1"); else $last = $OOPSGlobal["SES"]->db->QueryObject("SELECT * FROM `comments` WHERE user = '".$OOPSGlobal['SES']->uid->uid."' ORDER BY date DESC LIMIT 1"); if($last) { if( (time() - $last->date) < 30 ) { $add = false; $form = false; $div .= 'извините. слишком быстро. мы не справляемся. =)<br /><br />'; $click = "doLoad('forminfo','formtext','addcomm', { 'post' : {$id}}); return false;"; $div .= '<a href="#" OnClick="'.$click.'">а нука исчо?</a>'; } } if($add) { $div .= 'спасибо <B>'.$name.'</B>. ощущения подпитаны...<br /><br />'; $click = "doLoad('forminfo','formtext','addcomm', { 'post' : {$id}}); return false;"; $div .= 'подпитать <a href="#" OnClick="'.$click.'">исче</a>, посмотреть <a href="#endcomments" >что писнулось</a> или <a href="http://li.ru/go?http://www.dezinfo.net/comments/">что пишут</a> другие?'; $text = mysql_escape_string($text); $cnt = $OOPSGlobal["SES"]->db->Count('comments',"post = {$id}"); $OOPSGlobal["SES"]->db->Query("INSERT INTO `comments` (`nick`,`text`,`date`,`ips`,`post`,`user`) VALUES('{$nick}','{$text}',".time().",'".OOPSGetIP()."',{$id},'".$OOPSGlobal['SES']->uid->uid."')"); $OOPSGlobal["SES"]->db->Query("UPDATE `posts` SET COMM = COMM + 1 WHERE id = $id"); if($OOPSGlobal['SES']->uid->uid != '0') { if($cnt == 0) { $OOPSGlobal["SES"]->db->Query("UPDATE `oops_users` SET POSTS = POSTS + 1, NAH = NAH + 1 WHERE id = '".$OOPSGlobal['SES']->uid->uid."'"); } else { $OOPSGlobal["SES"]->db->Query("UPDATE `oops_users` SET POSTS = POSTS + 1 WHERE id = '".$OOPSGlobal['SES']->uid->uid."'"); } } $mkey = 'postcomm'.$id; $MEMCACHE->Delete($mkey); $MEMCACHEKEY = "post.{$id}.1.0"; $MEMCACHE->Delete($MEMCACHEKEY); $MEMCACHEKEY = "post.{$id}.1.1"; $MEMCACHE->Delete($MEMCACHEKEY); $MEMCACHE->Delete('index.0.1'); $MEMCACHE->Delete('postsleaders'); $MEMCACHE->Delete('top5userscamm'); $MEMCACHE->Delete('comments'); $js = "doLoad('camminfo','cammtext','showcamm', { 'page' : 1, 'post' : $id }, 'секундочку. ща добавим...');"; $form = false; } } } } if($form) { if($OOPSGlobal['SES']->uid->uid != '0') { $nick = htmlspecialchars($OOPSGlobal['SES']->uid->displayname); $div .= <<<ENDDIV <form id="sendcomm" method="post" enctype="multipart/form-data"> <input type=hidden name=post value="$id"> <input type=hidden name=nick value="$nick"> Имя: <B>$nick</B><br /> Сообщение:<br /> <textarea id="wtxt" name="text" class="int" rows=5></textarea> </form> <a href="#" OnClick="return doLoadForm('addcomm',document.getElementById('sendcomm'));">Оставить комментарий</a><br><p><b><font size=1>Кто скажет "баян" - тот писька :)</font></b> ENDDIV; } else { /* $div .= <<<ENDDIV <form id="sendcomm" method="post" enctype="multipart/form-data"> <input type=hidden name=post value="$id"> Имя:<br /> <input name="nick" type=text class="in"><br /> Сообщение:<br /> <textarea id="wtxt" name="text" class="int" rows=5></textarea> </form> <a href="#" OnClick="return doLoadForm('addcomm',document.getElementById('sendcomm'));">Оставить комментарий</a><br><p><b><font size=1>Кто скажет "баян" - тот писька :)</font></b> ENDDIV; */ $div .= <<<ENDDIV <div style="padding: 10px; border: 1px solid red;color: red"> писать в каменты могут только <a href="/registration/">зарегистрированные</a> пользователи. </div> ENDDIV; } } ?>
Советую: Кешировать данные в определенный промежуток - 100 кооментов набежало - ты их закешировал. свежие пока отображаются стандартно. Набежало еще - обновил кеш. Не обязательно 100, число подбирается индивидуально.
Оно-то понятно, что у нормальных пацанов кеш не в файлах а в памяти. Я пока что присматриваюсь к APC. Но там не совсем ясно как сбрасывать по ключам. У меня будут наверно очень длинные имена переменных. Так ТС от этого и пытается избавиться. Он же говорит, что в первые N минут посты сыпятся пачками. флоппик во. Этот вариант мне по нраву. Теперь мой вопрос. Замечено, что сейчас стало модно показывать дату поста в виде "отправлено секунду назад", обновляешь - "10 секунд назад", прошло 2 дня - "2 дня назад". Это замечено в trac'е, redmine, activecollab, ... . Как быть в таком случае? Первый промежуток времени не кешируем совсем? upd: да-да, результаты вывода из БД можно кешировать, они не меняются в общем-то. Тут про то, что если б я хотел кешировать результаты парсинга.
Достаточно умен, чтобы не быть самонадеянным, а спросить совета у тех, кто умнее меня. А ты даже не дочитал мой первый пост, но уже рвешься пестрить саркастическими шутками.