Что такое кеширование?
Я постоянно слышу о людях, у которых были проблемы с производительностью x, которые они решили с помощью кэширования.
Или как выполнение x,y,z в коде ваших программ может повредить вашей способности кеширования.
Даже в одном из последних подкастов Джефф Этвуд рассказывает о том, как они кэшируют определенные значения для быстрого поиска.
Кажется, в терминах "кеш" и "кеширование" есть некоторая двусмысленность, и это привело меня в замешательство относительно его значения в разных случаях. Имеете ли вы в виду кэширование приложений или баз данных, процессор и т. Д. И что это значит.
Что такое кеширование и что это за разные типы?
Из контекста я могу понять это, чтобы сохранить часто полученное значение в основной памяти и иметь быстрый доступ к нему. Однако что это на самом деле?
Это слово, кажется, используется во многих различных контекстах со слегка отличающимся значением (процессор, база данных, приложение и т. Д.), И я действительно хочу прояснить его.
Существует ли различие между тем, как работает кэширование в ваших приложениях, и кэшированием в базе данных?
Когда кто-то говорит, что он нашел фрагмент кода, который повредил бы кеширование, и после того, как он это исправил, это улучшило скорость их приложения, о чем они говорят?
Кэширование программы - это то, что выполняется автоматически? Как вы разрешаете кэширование значений в ваших программах? Я часто читал, что пользователи на этом сайте говорят, что они кэшировали значения в своих приложениях, я сижу здесь и удивляюсь, что они имеют в виду.
Кроме того, что это действительно означает, когда кто-то говорит о кэшировании базы данных? Это просто функция, которую они включают в свою базу данных? Вам нужно явно кэшировать значения или база данных выбирает, какие именно для вас кэшировать?
Как мне начать кэшировать элементы самостоятельно, чтобы улучшить производительность?
Можете ли вы дать мне несколько примеров того, как я могу начать кэширование значений в моих приложениях? Или, опять же, это то, что уже сделано, под капотом, и мне просто нужно написать свой код определенным образом, чтобы разрешить "кэширование"?
Как насчет кэширования базы данных, как мне начать это? Я слышал о таких вещах, как memcache. Требуется ли утилита такого типа для кэширования в базах данных?
Я хочу получить хорошее различие между кэшированием в приложениях и базах данных, тем, как они используются и как оно реализовано в обоих случаях.
9 ответов
Кэширование - это просто практика хранения данных и извлечения данных из высокопроизводительного хранилища (обычно памяти) в явном или неявном виде.
Позволь мне объяснить. Доступ к памяти быстрее, чем к файлу, удаленному URL (обычно), базе данных или любому другому внешнему хранилищу информации, которая вам нравится. Таким образом, если использование одного из этих внешних ресурсов является значительным, вы можете воспользоваться кешированием для повышения производительности.
Кнут однажды сказал, что преждевременная оптимизация - корень всего зла. Ну, преждевременное кэширование является корнем всех головных болей, насколько я понимаю. Не решайте проблему, пока у вас не возникнет проблема. Каждое принятое вами решение обойдется вам в цену, которую вы заплатите за его реализацию сейчас, а потом заплатите снова, чтобы изменить его позже, и чем дольше вы сможете отложить принятие решения и изменить свою систему, тем лучше.
Итак, сначала определите, что у вас есть проблема и где она. Профилирование, ведение журнала и другие формы тестирования производительности помогут вам здесь. Я не могу не подчеркнуть, насколько важен этот шаг. Количество раз, когда я видел, как люди "оптимизируют" что-то, что не является проблемой, поражает.
Итак, у вас есть проблемы с производительностью. Скажем, на ваших страницах выполняется запрос, который занимает много времени. Если это чтение, то у вас есть несколько вариантов:
- Запустите запрос как отдельный процесс и поместите результат в кеш. Все страницы просто получают доступ к кешу. Вы можете обновлять кэшированную версию так часто, как это необходимо (один раз в день, один раз в неделю, один раз в 5 секунд, в зависимости от того, что подходит);
- Прозрачно кешируйте данные через своего поставщика сохраняемости, ORM или что-то еще. Конечно, это зависит от того, какую технологию вы используете. Hibernate и Ibatis, например, поддерживают кэширование результатов запроса;
- Ваши страницы должны выполнить запрос, если результат не находится в кеше (или он "устаревший", то есть рассчитывается дольше, чем указанный "возраст"), и поместить его в кеш. Это создает проблемы параллелизма, если два (или более) отдельных процесса решат, что им нужно обновить результат, чтобы вы в итоге выполняли один и тот же (дорогой) запрос восемь раз за раз. Вы можете справиться с блокировкой кеша, но это создает еще одну проблему с производительностью. Вы также можете прибегнуть к методам параллелизма на вашем языке (например, API для параллелизма Java 5).
Если это обновление (или обновления происходят, которые должны быть отражены в вашем кэше чтения), то это немного сложнее, потому что нехорошо иметь старое значение в кэше и более новое значение в базе данных, так что вы затем предоставляете свои страницы с противоречивым представлением данных. Но в целом существует четыре подхода к этому:
- Обновите кеш, а затем поставьте в очередь запрос на обновление соответствующего хранилища;
- Запись через кеширование: поставщик кеша может предоставить механизм для сохранения обновления и блокировки вызывающей стороны до тех пор, пока это изменение не будет сделано; а также
- Кэширование с обратной записью: то же самое, что и сквозное кэширование, но оно не блокирует вызывающего. Обновление происходит асинхронно и по отдельности; а также
- Модели персистентности как сервиса: предполагается, что ваш механизм кэширования поддерживает некоторую наблюдаемость (то есть слушатели событий кэширования). По сути, совершенно отдельный процесс - неизвестный для вызывающего - прослушивает обновления кэша и сохраняет их по мере необходимости.
Какая из вышеуказанных методологий вы выберете, будет во многом зависеть от ваших требований, используемых вами технологий и множества других факторов (например, требуется ли поддержка кластеризации и отработки отказа?).
Трудно быть более конкретным, чем это, и дать вам рекомендации о том, что делать, не зная гораздо более подробно о вашей проблеме (например, есть ли у вас проблема).
Скорее всего, вы прочтете о кешировании в контексте веб-приложений. Из-за особенностей Интернета, кэширование может существенно повлиять на производительность.
Учтите следующее:
Запрос веб-страницы попадает на веб-сервер, который передает запрос на сервер приложений, который выполняет некоторый код, отображающий страницу, который должен обратиться к базе данных для динамического извлечения данных.
Эта модель плохо масштабируется, поскольку по мере увеличения количества запросов на страницу сервер должен делать одно и то же снова и снова для каждого запроса.
Это становится еще более серьезной проблемой, если веб-сервер, сервер приложений и база данных находятся на разном оборудовании и взаимодействуют друг с другом по сети.
Если у вас есть большое количество пользователей, попадающих на эту страницу, имеет смысл не проходить весь путь до базы данных для каждого запроса. Вместо этого вы прибегаете к кешированию на разных уровнях.
Resultset Cache
Кэширование Resultset хранит результаты запроса к базе данных вместе с запросом в приложении. Каждый раз, когда веб-страница генерирует запрос, приложения проверяют, кэшированы ли уже результаты, и, если они есть, извлекают их из набора данных в памяти. Приложение все еще должно отобразить страницу.
Кэш компонента
Веб-страница состоит из различных компонентов - листовок, или как вы хотите их называть. Стратегия кэширования компонента должна знать, какие параметры использовались для запроса компонента. Например, небольшая панель "Последние новости" на сайте использует географическое местоположение пользователя или предпочтения для отображения местных новостей. Следовательно, если новости для местоположения кэшируются, компонент не должен отображаться и может быть извлечен из кэша.
Кэш страницы
Одна из стратегий кэширования целых страниц заключается в хранении строки запроса и / или параметров заголовка вместе с полностью отредактированным HTML. Файловая система достаточно быстра для этого - веб-серверу все равно дешевле читать файл, чем вызывать сервер приложений для отображения страницы. В этом случае каждый пользователь, отправляющий одну и ту же строку запроса, получит одинаковое кэшированное содержимое.
Интеллектуальное объединение этих стратегий кэширования является единственным способом создания действительно масштабируемых веб-приложений для большого числа одновременно работающих пользователей. Как вы можете легко видеть, потенциальный риск здесь заключается в том, что если часть содержимого в кэше не может быть однозначно идентифицирована по его ключу, люди начнут видеть неправильный контент. Это может быть довольно сложно, особенно когда у пользователей есть сессии и существует контекст безопасности.
Есть два значения, о которых я знаю.
Одним из них является кеширование приложений. Это происходит, когда, если данные медленно откуда-то поступают (например, по сети) или медленны для вычисления, тогда приложение кэширует копию данных (чтобы не нужно было их получать повторно или пересчитывать: это уже кешируется). Реализация кеша требует немного дополнительного программного обеспечения (логика использования кеша) и дополнительной памяти (в которой хранятся кэшированные данные).
Это "кеширование", которое вы используете, когда вы цитируете здесь:
Из контекста я могу понять это, чтобы сохранить часто полученное значение в основной памяти и иметь быстрый доступ к нему.
Другой - кеширование процессора, которое описано в этой статье в Википедии. Кэширование процессора происходит автоматически. Если вы выполняете много операций чтения из небольшого объема памяти, то центральный процессор может выполнять большинство этих операций чтения из своего кэша. OTOH, если вы читаете из большого объема памяти, он не может все поместиться в кеш, и процессор должен тратить больше времени на работу с более медленной памятью.
Это "кеширование", которое вы используете, когда вы цитируете здесь:
Когда кто-то говорит, что он нашел фрагмент кода, который повредил бы кеширование, и после того, как он это исправил, это улучшило скорость их приложения, о чем они говорят?
Это означает, что они нашли способ перестроить свой код, чтобы меньше пропусков кэша.
Что касается кэширования базы данных, я не знаю.
Есть пара вопросов.
Во-первых, это гранулярность. Ваше приложение может иметь очень хороший уровень кэширования сверх того, что делает база данных. Например, база данных, скорее всего, будет просто кэшировать страницы данных, а не обязательно конкретные строки.
Другое дело, что приложение может хранить данные в своем "родном" формате, тогда как БД, очевидно, кэширует только во внутреннем формате.
Простой пример
Допустим, у вас есть пользователь в базе данных, которая состоит из столбцов: USERID
, FIRSTNAME
, LASTNAME
, Очень просто.
Вы хотите загрузить пользователя, USERID=123
в ваше приложение. Какие шаги предпринимаются?
- Выдача вызова базы данных
- Разбор запроса (
SELECT * FROM USER WHERE USERID = ?
) - Планирование запроса (т. Е. Как система собирается получать данные)
- Извлечение данных с диска
- Потоковая передача данных из базы данных в приложение
- Преобразование данных базы данных в данные приложения (т.е.
USERID
целое число, скажем, имена строк.
Кэш базы данных, вероятно, будет кэшировать шаги 2 и 3 (это кэш операторов, поэтому он не будет анализировать или перепланировать запрос) и кэшировать фактические блоки диска.
Итак, вот ключ. Ваш пользователь, USER ID 123
, название JESSE JAMES
, Вы можете видеть, что это не много данных. Но база данных кеширует дисковые блоки. У вас есть блок индекса (с 123
на нем), затем блок данных (с фактическими данными и всеми другими строками, которые помещаются в этот блок). Итак, что номинально, скажем, 60-70 байт данных на самом деле имеет кеширование и влияние данных на БД, вероятно, 4K-16K (зависит от размера блока).
Светлая сторона? Если вам нужен еще один ряд, который находится рядом (скажем, USER ID = 124
), шансы высоки, индекс и данные уже кэшированы.
Но даже с этим кэшированием вам все равно придется заплатить за перемещение данных по проводам (и это всегда по проводам, если вы не используете локальную БД, тогда это петля), и вы "демаршируете" данные, То есть преобразование его из битов базы данных в биты языка, в биты приложения.
Теперь, когда приложение получит его USER ID 123
, это заполняет значение в долгоживущей хэш-карте.
Если приложение когда-либо захочет его снова, оно будет искать локальную карту, кэш приложения и сэкономить на поиске, проводном транспорте и расходах на сортировку.
Темная сторона кеширования приложений - синхронизация. Если кто-то входит и делает UPDATE USER SET LASTNAME="SMITH" WHERE USERID=123
Ваше приложение не "знает об этом", и, следовательно, кэш грязный.
Итак, есть множество деталей в обработке этих отношений, чтобы синхронизировать приложение с БД.
Наличие большого количества кэша базы данных очень удобно для больших запросов к "горячему" набору данных. Чем больше у вас памяти, тем больше "горячих" данных вы можете иметь. Вплоть до того, что вы можете кэшировать всю БД в ОЗУ, вы устраняете задержку ввода-вывода (по крайней мере, для чтения) перемещения данных с диска в буфер ОЗУ. Но у вас все еще есть транспортные и сортировочные расходы.
Приложение может быть гораздо более избирательным, например, кешировать более ограниченные подмножества данных (БД просто кешируют блоки), а наличие "ближе" к приложению данных повышает эту производительность.
Недостатком является то, что не все кэшируется в приложении. База данных, как правило, хранит данные более эффективно, чем приложение. Вам также не хватает языка "запросов" для кэшированных данных вашего приложения. Большинство людей просто кешируют с помощью простого ключа и переходят оттуда. Легко найти USER ID 123
, сложнее для "ВСЕХ ПОЛЬЗОВАТЕЛЕЙ ИМЕНИ ДЖЕССИ".
Кэширование базы данных имеет тенденцию быть "свободным", вы устанавливаете номер буфера, а СУБД обрабатывает все остальное. Низкое влияние, уменьшает общий ввод-вывод и задержки диска.
Кэширование приложений, в частности, зависит от конкретного приложения.
Это работает очень хорошо для изолированных "статических" данных. Это очень просто. Загрузите кучу вещей для поиска таблиц при запуске и перезапустите приложение, если они изменятся. Это легко сделать.
После этого сложность начинает увеличиваться по мере добавления в "грязную" логику и т. Д.
Все это сводится к тому, что, пока у вас есть API данных, вы можете постепенно кэшировать.
Итак, пока вы звоните getUser(123)
везде, а не попадать в БД, потом вы можете вернуться и добавить кеширование getUser
без влияния на ваш код.
Поэтому я всегда предлагаю какой-то уровень доступа к данным в коде каждого, чтобы обеспечить этот уровень абстракции и перехвата.
Концепция кеша здесь перегружена. Я не знаком с основными моментами кеширования базы данных.
В приложениях есть два использования этого термина.
Когда кто-то говорит, что он нашел фрагмент кода, который повредил бы кеширование, и после того, как он это исправил, это улучшило скорость их приложения, о чем они говорят?
В этом случае они ссылаются на кэш процессора.
Кэш-память процессора - это встроенная память процессора, которая намного быстрее оперативной памяти, но не имеет произвольного доступа. То, что процессор решает загрузить в кеш, может немного усложниться. Посмотрите Ульриха Дреппера. Что каждый программист должен знать о памяти, чтобы получить множество деталей.
Помня о том, что кэш-память процессора может ускорить процесс, вы просто должны уделить немного больше внимания тому, где объекты будут размещаться относительно друг друга в физической памяти и когда они, вероятно, будут использоваться.
Один из примеров (также, вероятно, анти-шаблон для удобства сопровождения) состоит в том, что у вас есть массив структур, и вы делаете много циклов по элементам структуры, для которых вам лучше использовать структуру, в которой все поля являются массивами. Если данные, по которым вы зацикливаетесь, непрерывны в памяти, у вас больше шансов не нарушать кеш.
На эффективность использования кеша могут влиять любые вещи: прогноз ветвления для кода, загруженного в кеш, размер и выравнивание структур данных и шаблонов доступа, где и когда объявлять локальные переменные, которые будут помещены в стек.
Другое общее использование термина для прикладного программирования может быть сделано кое-что, названное запоминанием. Факторный пример на этой странице Википедии объясняет вещи лучше, чем я бы сделал.
Кэширование берет результат длинного или интенсивного процессора и сохраняет ответ, чтобы вам не пришлось снова запускать алгоритм, вы просто повторно используете результат.
Кэширование в базах данных обычно является функцией базы данных и управляется автоматически базой данных. Кэширование в приложениях будет варьироваться от одной платформы к другой.
Кэш объектов - это механизм, который можно использовать для помещения в память часто используемых объектов, чтобы вам не приходилось платить за получение данных и их повторное создание. Обычно это осуществляется с помощью кода и зависит от того, какое решение для кэширования вы используете.
Существуют решения с распределенным кешем, которые включают в себя настройку служб на нескольких серверах для создания своего рода фермы кеша. Это обеспечивает масштабируемость и избыточность. Клиенты могут запрашивать кэшированную информацию по сети. Опять же, это ручная процедура в вашем коде. Пример поставщика распределенного кэша - memcached:
http://www.danga.com/memcached/
Примером конкретного типа кэширования может быть кэширование asp.net. Asp.net поддерживает несколько видов кеша. Существует традиционный кэш объектов (который можно использовать во всех видах приложений.net, а не только на веб-сайтах). Есть также функции кэширования, которые позволяют вам настраивать страницы и пользовательские элементы управления для автоматического кэширования их вывода. Он не кэширует данные, он кэширует конечный результат (HTML-код страницы) и обрабатывает его, когда пользователь запрашивает ту же страницу с теми же параметрами строки запроса, что и предыдущий пользователь.
Кэширование не обязательно относится только к "часто извлекаемым" значениям, но и ко всему, на чем вы можете сэкономить время, уменьшив количество повторных вычислений. Простой пример, который приходит на ум, - это вычисление последовательности Фибоначчи. Простейшая рекурсивная реализация выглядит следующим образом (в псевдо-коде):
function f(n)
if n < 2 then
return n;
return f(n - 1) + f(n - 2)
Это можно улучшить с помощью кэширования, чтобы предотвратить пересчет уже известных значений:
fib_cache = {}
function f(n)
if n < 2 then
return n;
if fib_cache.contains(n) then
return fib_cache[n]
fib_cache[n] = f(n - 1) + f(n - 2)
return fib_cache[n]
Вероятно, это проще, чем вы можете себе представить - и именно поэтому люди пытаются закрыть его.
Это просто означает хранить значения в вашей памяти, а не каждый раз возвращаться к базе данных.
Есть много способов сделать это, но сама концепция тривиальна.
Изменить: Это может быть сделано на любом уровне - все, что занимает много времени, может быть кэшировано где-то, что вы можете получить быстрее.