MyCMS / Попытки / Теория создания CMS / 1.5. Кэширование
Кэширование
Не маловажная роль в построении практически любых серьезных программных продуктов отводится кэшированию. Как правило, большинство переменных можно вычислить «на лету», что многие неопытные разработчики пытаются делать в максимальных количествах. Но производительность машины «не резиновая», а если мы говорим о серверах хостеров, то там и вовсе все достаточно четко лимитировано. Поэтому я полагаю, что такой не маловажный вопрос следует продумать на начальном этапе разработки, чем мы и займемся в этой заметке.
Давайте вспомним, что кэш может быть по времени и по событию, думаю как это реализовать ни у кого не вызовет затруднений, поэтому в этой статье я приведу свои мысли на тему того как не сделать так, чтобы кэширование стало дополнительным «тормозом» системы.
Обращаясь к практике своей работы, я могу сказать совершенно точно, что местами, где нагрузка на сервер увеличивается значительно – являются: множественные обращения к БД, сложные запросы на большие таблицы БД и многократные обращения к файлам.
Для начала нам нужно определиться с местом для хранения наших кэш-данных – файлы или БД. Не смотря на то, что обращение к файлам происходит быстрее, а нагрузка на файловую систему в среднем ниже, нежели на БД, я все же рекомендую использовать БД. Тест на то, как поведет себя программа, когда кэш равен сотням тысяч записей и занимает порядка гигабайта места, я не проводил, но Вы можете попробовать, мне было бы интересно взглянуть на результаты. Не смотря на это, я полагаю, что в скорости работы размещение кэша в файловой системе будет выигрывать до тех пор, пока кэш занимает относительно не большой объем, порядка нескольких мегабайт и имеет не большое количество записей. Кроме того, файловый кэш сложнее идентифицировать, потому как имена файлов формируются по определенным правилам, которые не всегда подходят нам, но даже обойдя эту проблему мы сталкиваемся с тем, что обращаясь к папке с кэшем посредством ftp:// (в которой у нас имеется порядка 30 000 записей) не факт что нам удастся открыть данную папку, чтобы элементарно удалить одну из записей. В то время как в БД такие проблемы решаются посредством элементарных запросов и не требуют больших технических возможностей для их администрирования. И в конце концов, БД на то и БД чтобы хранить данные, а кэш данные в больших порталах являются одними из основных.
Тем не менее, право выбора за Вами, но не зависимо от выбранного варианта хранения данных мы столкнемся с проблемой того, что основная потеря времени при работе с данными происходит на обращении к ним, то есть на чтении и записи этих данных из/в «хранилища» (В дальнейшем, место хранения кэш-данных я буду называть БД, коим вариантом является рекомендованный).
Пример:
Пусть на каждую страницу имеется не менее 6 закэшированных переменных (индексный шаблон, счетчик баннерной системы, кэш баннеров, кэш новостей, кэш количества посетителей на странице и кэш наполнения страницы), в то время как по данной странице у нас 15 закэшированных переменных.
Допустим у нас имеется таблица cache:
id - идентификатор
url - адрес
name - имя
value – значение
Индексы установлены на url и на name, id – основной ключ.
В поле «адрес» мы будем записывать адрес страницы (при необходимости добавляя постфикс - к примеру, идентификатор пользователя для которого создана кэш переменная). Поле «имя» - имя переменной, а в поле «значение» значение кэш-переменной.
1. Для чтения попеременно:
SQL> SELECT value FROM cache WHERE name = 'VarName'
При том, что нам необходимо получить 6 кэш переменных, нам потребуется выполнить шесть запросов на чтение, и если значения кэша должны обновиться, то до шести запросов на запись. Следовательно, если одновременно происходит 20 запросов на открытие страницы (что является стандартной ситуацией для более-менее раскрученного сайта), то сервер одновременно должен выполнить 20 * 6 = 120 запросов к базе данных только для чтения кэша. А если кэш-переменных 10, 20, 30, то 20 * 30 = 600 запросов!
Вы думаете 120 это не много, а 600 у вас не получится? Хорошо, теперь представим ситуацию, что половина переменных должны обновить значения кэша, то есть при 120 считанных переменных 60 должно обновиться, и тогда мы получаем 180 запросов к базе данных. А вот тут возникнет следующая проблема – при записи в таблицу, таблица закрывается для прочих запросов на время записи, то есть БД выполнит 60 закрытий и открытий таблицы кэш при этом все 179, 178, …1 запросы будут выполнены с задержкой. Если конечно сервер выдержит, но теоретически должен… до поры до времени :)…
Возможно ли, это оптимизировать? Да возможно, и я предлагаю следующий вариант.
Во-первых, чтение и запись осуществлять пакетами, то есть, при чтении запрашивать сразу все переменные по данной странице.
2. Для пакетного чтения:
SQL> SELECT name, value FROM cache WHERE url = 'URL';
При сравнении данного запроса с предыдущим видно, что второй запрос незначительно, но сложнее, но как показывает практика при столь незначительном отличии и установленных факторах, второй запрос хоть и является незначительно более медленным, но общее время работы программы значительно ускоряется по отношению к первому варианту, потому как, основное время теряется непосредственно при обращении к базе.
То есть, если во втором варианте [количество запросов] = [количеству переменных на запись] + [1 запрос на чтение], то в первом варианте нам потребуется выполнить для каждой открываемой страницы [количество запросов] = [количество переменных на запись] + [количество переменных на чтение].
Плюсом второго варианта является еще и то, что второй вариант мы можем оптимизировать до двух обращений к БД, не зависимо от количества переменных.
3. Для пакетного чтения и записи не зависимо от количества переменных:
SQL>SELECT value FROM cache WHERE url='URL'
SQL>UPDATE cache SET value='Cache' WHERE url='URL'
, где переменная Cache – является массивом всех кэш переменных страницы.
Данный вариант, на мой взгляд, является наиболее оптимальным решением для работы с закэшированными данными. Суть его сводится к тому, что мы делаем один запрос на чтение закэшированного массива переменных и один запрос на обновление закэшированных данных.
Таким образом осуществляя запрос кэша в конструкторе системы и делая обновление кэша в деструкторе, поместив все кэш-переменные в ассоциативный массив, мы добиваемся наилучшего результата.
Тезисы:
- Кэширование должно производиться, посредством пакетного чтения и записи.
- Пакеты кэша необходимо группировать по адресам страниц (возможно добавление постфикса или префикса).
- При открытии любой страницы в переменную Cache должна быть произведена запись всех переменных кэширования по этой странице.
- В деструкторе CMS должна быть запись значений переменной в базу данных или в файл (рекомендуется БД).
- Пакетное чтение кэш данных сгруппированных по адресу страницы (и возможным доп. параметрам) позволит значительно сократить количество обращений к БД, что значительно ускорит работу сайта и сделает работу сервера более устойчивой к нагрузкам.
Владимир Бредихин 2006/04/19 00:38
