Список вопросов
Как зайти в Даркнет?!
25th January, 01:11
5
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
893
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
912
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4380
0
Помогите пожалуйста решить задачи
24th November, 23:53
6084
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4350
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4395
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Чат на PHP: узкое место БД — как решить?
Просмотров: 443
 
Ответов: 15
Есть задача организовать простой чат с веб-интерфейсом и полной историей на действующем сайте на самописном движке (PHP5.3.3/MySQL5.1). Гугление по существующим решениям ничего хорошего не дало, либо избыточно, либо производит ощущение «наколенной поделки» и чаще всего давно не поддерживается, да и хотелось бы иметь одну архитектуру и стиль кодирования. В общем принято решение реализовать самостоятельно. С кодированием особых проблем нет, прототип реализовали, но нагрузочное тестирование с разными вариантами индексов и таблиц показало, что при уже ~20 хостах «читателей» и одним «писателем» в секунду MySQL затыкается (VDS c 1Гб RAM, мускулу половина отдана, и 2ГГц проц, nginx+php-frpm под Debian) даже на денормализованной таблице, т. к. кэшированию средствами БД запросы не поддаются (фильтры у каждого «читателя» свои, ибо приват, фильтрация в серверном приложении вряд ли будет эффективней чем в БД, как мне кажется, а у клиента недопустима). А хотелось бы на этом «железе» хотя бы 40-50 держать помимо основной нагрузки. Что может помочь? Опыта «хайлоад» нет, возникли такие идеи:
— написать демона для чата на субдомене, чтобы читал в основном потоке из БД только при старте (последние N сообщений) или редких специфичных запросах, хранил их у себя в памяти процесса (убивая старые), а писал в БД только «логи» для следующего старта (тогда фильтрация будет эффективна, имхо, плюс её можно будет осуществлять опережающе и инкрементно, храня сами сообщения в едином пуле, а для каждого читателя добавлять в список ссылок на «его» сообщения при поступлении сообщения от «писателя» лично для него или публичного, и удалять их оттуда при прочтении)
— аналогичным образом задействовать мемкэш (хотя пока с трудом представляю как обеспечить целостность, до того только с файловыми кэшами работал, которые сами не «испаряются») для обычного PHP-обработчика (то есть чтобы куча воркеров имела доступ к общему пулу сообщений и инкрементным личным спискам ссылок на них между запросами)
— перевести чат на NoSQL СУБД (какую? главная задача эффективная фильтрация по паре полей последних записей, типа WHERE timestamp > {last_time} (или id>{last_id}) AND (recipient_id IS NULL OR recipient_id={user_id}) ORDER BY timestamp (или id) DESC LIMIT {max_records} )
Что стоит попробовать или ещё какие могут быть варианты? Демона писать не хочется, так как усложнит администрирование и сервера, и собственно чата (аналог IRC команд делать?), опыта работы с кэшем и NoSQL практически нет.
Во-первых, надо уточнить, что за VDS. Если мастерхост, то сразу предупредите, т.к. это отдельная песня.
Во-вторых, нужно понять, из-за чего тупит mySQL. Выше правильно писали про lock на время записи.
Так же может не успевать диск (из-за ограничений VDS).
Я бы попробовал перевести в бд innodb и commit в 0 выставить (сброс раз в секунду на диск).
Далее есть такая штука как представления. И у них есть режим с кэшем в памяти. А SQL запросы уже к нему делать. К тому же их можно наплодить на разные случаи.
Ещё хорошо бы не забыть про memory таблицы. Скажем, писать ещё и в не memory, но читать массово только из неё.
Попутно стоит помнить про индексы. Их отсутствие делает select долгим, а чрезмерное присутствие долгим insert. Да и сами индексы новички обычно неправильно делают.
Ещё бы я вернулся к настройкам VDS, потому что они режут среднюю нагрузку по процу, памяти и дисковым операциям. Вас тупо может это резать. И заодно ещё вспомнил про объём БД в памяти. Если там уже много данных, а кэши не большие, то резать по диску будет.
Советы от себя
1) попробывать сервер реализовать на node.js (один процесс вроде 800 соединений держит в секунду)
2) тяжелые запросы насколько я понимаю у вас в случае когда в чате 30 человек, один из них пишет, вы это сохраняете в базе данных, потом 29 человек присылают запрос на «обновление» чата… и выполняется 29 выборок из БД, на мой взгляд это не совсем правильно, попробуйте немного по другому: когда один юзер присылает сообщение, вы конечно один раз сохраните его для «истории», но при этом в том же мемкэше создавайте для всех «заинтересованных» юзеров очередь обновлений, не нужно хранить одно сообщение на всех, нужно для каждого хранить полный список того в чем он заинтересован, ну и естественно когда 29 человек обратятся за обновлениями, вы вообще не будете обращаться в БД, а просто возьмете данные из кэша (и почистите его естественно)
Я помню лет 10-15 назад чаты были очень популярными. Кроме чатов и форумов ничего ещё изобретено не было. Ни блогов, ни социалок, ни твиттеров ни, даже, ajax. И тусили в этих чатах тысячи человек (можно было открыть несколько каналов). Или сотка людей в чате без каналов.
Чаты были на фреймах и каждые 5 сек. фрейм со списком сообщений обновлялся ПОЛНОСТЬЮ (весь HTML со всей пачкой сообщений).
Железо в те времена было что-то вроде 300-400 МГц. Ваш ВДС, я уверен, порвал бы те сервера.
Как-то странно сейчас в 2010 году слышать, что чат не выдерживает больше 30 одновременных юзеров.
Вы пробовали на другом сервере? Хотя бы на локале сколько держит?
Можно написать демон. Обычный PHP-демон.
nanoserv или phpDaemon (о последнем информация проскакивала на хабре, но он для настоящих кунг-фу панд :)).
Обычные асинхронные сокет-соединения, зачем вам БД? Зачем вам такие выборки? Вы хотите хранить историю чатов?
Если только публичные-приватные сообщения — элементарно при поступлении сообщения демон либо отдает его конкретному сокету, либо бруткастит всем. Никаких «запросов раз в секунду».
На стороне клиента флешка, держащая этот самый сокет и получающая/отдающая сообщения.
пиши демона, алгоритм очень простой:
1. скрипт на который отсылаются сообщений. не демон. сообщение кладёшь в базу и при желании в мемкэш, чтобы был доступен демону. я делал без мемкэша.
2. демон, работает и в цикле перезаписывает файлик post.js в котором хранятся в json виде последних N сообщений. В определённый момент времени можно чистить таблицу и убирать старые посты вовсе или в другую таблицу.
3. с сайта ajax-ок забираешь файлик post.js?r=случайное_число и обновляешь панель сообщений. т.к. забирается js, не поднимается php такую нагрузку выдержит даже apache.
можно вовсе бд не использовать, оставив только memcache или пользуясь шареной памятью php.net/shmop
Если нужно делить посты для пользователей, то генерировать не один файл post.js, а несколько /{user_key}/post.js и давать секретный ключ пользователям.
ну если хочется именно БД то я-б думал в сторону промежуточного слоя — т.е. данные из него в БД пишутся асинхронно при определенном заполнении а чтение только из него, из бд в него данные заливаются только в слычае тотального краша, П.С. это мысли не более, тему про «невозможность кеширования запросов» не осилил
Не совсем понятно зачем вообще база. Насколько я понимаю она используется для передачи данных между процессами запросов ( запросы тоже не нужны — используйте websocket ) а для этого либо надо писать отдельный демон, либо использовать сервер с настоящим fastcgi для php или fastcgi на любом другом языке, где данные будут общими. Ну и разруливать их семафорами корректно.
Еще как вариант — мемкеш, но в нем надо делать структуру данных что бы было удобно добавлять сообщения и собирать ответ, зато не надо думать о сборке мусора.
Может быть база нужна для истории… хотя кому она нужна в чатах? :) сделать ее можно в офлайне, кроном.
Для старта сервера — если все правильно сделать то и ее не надо будет, поскольку клиенты имеют текущую ситуацию, надо просто их оповещать об изменениях. А они на старте не нужны.
<?php
# Connect to memcache:
global $memcache;
$memcache = new Memcache;
# Gets key / value pair into memcache… called by mysql_query_cache()
function getCache($key) {
global $memcache;
return ($memcache)? $memcache->get($key): false;
}
# Puts key / value pair into memcache… called by mysql_query_cache()
function setCache($key,$object,$timeout = 60) {
global $memcache;
return ($memcache)? $memcache->set($key,$object,MEMCACHE_COMPRESSED,$timeout): false;
}
# Caching version of mysql_query()
function mysql_query_cache($sql,$linkIdentifier = false,$timeout = 60) {
if (($cache = getCache(md5(«mysql_query». $sql))) !== false) {
$cache = false;
$r = ($linkIdentifier !== false)? mysql_query($sql,$linkIdentifier): mysql_query($sql);
if (is_resource($r) && (($rows = mysql_num_rows($r)) !== 0)) {
for ($i=0;$i<$rows;$i++) {
$fields = mysql_num_fields($r);
$row = mysql_fetch_array($r);
for ($j=0;$j<$fields;$j++) {
if ($i === 0) {
$columns[$j] = mysql_field_name($r,$j);
}
$cache[$i][$columns[$j]] = $row[$j];
}
}
if (!setCache(md5(«mysql_query». $sql),$cache,$timeout)) {
мутим тут запрос к БД и возвращаем ответ
}
}
}
return $cache;
}
?>
Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться