Страницы

Поиск по вопросам

Показаны сообщения с ярлыком кэширование. Показать все сообщения
Показаны сообщения с ярлыком кэширование. Показать все сообщения

четверг, 19 марта 2020 г.

Кэширование - очень большая база

#php #кэширование #mysql


Гугл выдает результат по одному из разделов сайта -
14 600 000.
База данных MySQL кряхтит, запрос выполняет от 5 до 30 сек.
Сделал кэширование с сохранением кэшированных файлов в папку,
причем для поисков один кэш, для юзеров - совсем другой.
Получилось:
  /cache/1.txt - это для юзеров
  /cache/1r.txt - это для поисковиков
  /cache/2.txt
  /cache/2r.txt
  /cache/3.txt
  /cache/3r.txt
  /cache/4.txt
  /cache/4r.txt
  и т.д.

Естественно вообще можно так делать то?
Со временем в папке появится 14 600 000 * 2 = 29 200 000 файлов.
Все это дело займет 2 - 3 месяца.
Файл занимает 17 кб в среднем.
Отсюда считаем.
17 * 14 600 000 = 248 200 000 кб / 1024 = 242 383 мб / 1024 = 237 гб * 2 = 474 гб
Получается, что в папке будет лежать 474 гб файлов, общее количество которых будет
составлять 29 200 000 файлов.
Не загнется все это дело?
Что посоветуете?
И не забывайте - это всего один раздел сайта, а таких несколько.
С кэшированием страницы загружаются мгновенно.    


Ответы

Ответ 1



Файловый кэш, как вы описали, можно разнести по (бинарному?) дереву подпапок. 14600000 это 23 бита. Например, вариант раскидать по папкам соотв. старшим N битам: cache/1/0/0/1/0/1/0/1/65535.txt БД кряхтит, выполняя один запрос, без других параллельно? Пересмотрите запросы и индексы в таблицах — возможно, только с помощью этой меры удастся сделать, чтобы запросы летали. 14 600 000 страниц это не так много. Часты ли повторяющиеся 1:1 запросы/ответы? Статистика запросов - ровное горизонтальное поле или "колокол"? Кэширование имеет смысл если повторов много. Может, пора расти: поднимать кластер, или хотя бы еще один сервер с MySQL? Сделать его slave'ом, на котором работает копия бд и обрабатывает ровно половину всех запросов. У них может быть общий кэш, напр. на memcached - доступный для всех серверов, чтобы часто повторяющиеся запросы не грузили БД. Upd. 5. Роботов можно попридержать, настроив robots.txt - например, пусть заходят не чаще раза в неделю. Для понимания того, как в данном случае оптимизировать сайт, надо подробно видеть, что именно там происходит. Как устроена база, какие запросы, персонифицированы ли страницы под посетителей. Инуитивно подозреваю, что длинные запросы вызваны банальным отсутствием нужных индексов в БД. Если же БД уже никак не оптимизировать индексами и изменением запросов, а дышит она тяжело именно от обилия данных, можно разнести разросшиеся таблицы по партициям . Напр. строки с индексом от 1 по N хранятся на одном сервере, от N+1 по M — на другом. Ещё варианты для размышления: страничный кэш типа Varnish; nginx proxy, установив разумный срок дискового кэширования страниц.

пятница, 13 марта 2020 г.

Как настроить кеш браузера для статики и медиа в Django

#django #кэширование


Подскажите пожалуйста, как настроить кеш браузера для статики и медиа в Django



вот справка гугла.. developers.google.com/speed/docs/insights/LeverageBrowserCaching

Я настроил кэширование фаловой системе по мануалу djbook.ru/rel1.8/topics/cache.html#filesystem-caching

т.е. добавил в настройки

CACHES = {
'default': {
    'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
    'LOCATION': '/var/tmp/django_cache',
    }
}


и

MIDDLEWARE_CLASSES = (
    'django.middleware.cache.UpdateCacheMiddleware',
    ....
    'django.middleware.cache.FetchFromCacheMiddleware',
)


При загрузке страниц появился Expires в заголовке.

Но вот как сделать чтобы оно было для файлов из статики и из media, непонятно..
    


Ответы

Ответ 1



Django не должен отвечать за установку хедеров статики. За это должен отвечать сервер который будет её обслуживать, например Nginx или Apache. На локальной машине вашу ститику обслуживает сам django(скорее всего), но как и написано в мануале - это крайне неэффективно с точки зрения производительности. Для ясности, картина работу выглядит так. Приходит http запрос -> Nginx определяет тип запроса - если статика, то сам её отдает, если нет отдает далее стоящему приложению -> т.к Django общается с сервером по wsgi протоколу то саму django обслуживает (запускает) другое приложение, например uWSGI(uWSGI реализация wsgi протокола) -> запрос приходит в саму django. Ответ уходит по цепочке вверх. Теперь становится понятно почему это так неэффективно и почему django не должен этим заниматься. Почитайте документацию Django Managing static files Определитесь с тем - кто будет обслуживать статику на боевом сервере, и читайте в этом направлении. Например настройка Nginx

CSS, JS: как указать браузеру, что нужно обновить закэшированный файл?

#javascript #css #кэширование


Например, браузер при первой загрузке закэшировал файлы CSS и JS. Позже я изменил
содержимое этих файлов и загрузил их на сервер. Как указать браузеру, что файлы были
обновлены, чтобы он перегрузил их и обработал?

Можно обновить страницу с помощью клавиш Ctrl+F5, однако не будем же мы вешать объявления
на сайте, чтобы все пользователи обновляли страницу указанным способом.
    


Ответы

Ответ 1



Традиционно к URL файла, на который есть ссылка, добавляется какое-нибудь уникальное значение в query, например, script.js?1337. Формат запроса значения не имеет, так как веб-серверы игнорируют его для файлов. Надо отметить, что в большинстве случаев это избыточно. Протокол HTTP предоставляет множество возможностей по настройке кэширования. Обычно запрос на статический файл уходит всегда, но при этом клиент (браузер то есть) может сообщить, что файл уже закэширован, и указать версию (дату, метку). В случае, если на сервере файл не обновился, сервер отдаст пустой ответ, говоря клиенту пользоваться кэшированной версией. Если файл обновился, то сервер отошлёт новый файл. К сожалению, есть всякие глупые кэширующие прокси, криво настроенные браузеры и прочий шибко умный софт, который мешает нормальному функционированию HTTP согласно стандарту. Поэтому такие костыли и необходимы.

воскресенье, 8 марта 2020 г.

Как перехватить кнопку “назад” браузера?

#javascript #http #button #кэширование #браузер


На странице используется JavaScript, пользователь может изменить вид страницы. При
переходе по ссылке на другую страницу того же сайта и затем нажатии на кнопку "Назад"
браузера, теряются сделанные пользователем изменения на первой странице.

Как перехватить событие возврата на страницу, чтобы можно было ее обновить?
    


Ответы

Ответ 1



В комментариях верно написали, что ответ кроется не в перехвате события "назад" у браузера, а сохранении состояния страницы Например, пользователь может изменить тему страницы. Что бы при возвращении на эту страницу, была выбрана нужная тема, сохраним ее в localStorage const savePageState = (name, state) => { localStorage[name] = state; } const loadPageState = (name) => { return localStorage[name]; } Дальше, при нужных вам событиях, вы просто сохраняете в localStorage нужные вам данные, а при загрузке страницы (onload событие, например) выгружаете нужные данные и используете их

Ответ 2



Раз пользователь может изменить вид страницы, значит, работает javascript. В нем надо при обработке пользовательских действий подменить последний элемент в истории браузера, чтобы возврат происходил уже на указанную вами страницу (или тот же адрес, но с параметрами). Вот пример. Код скрипта: $('.portfolio_filter').each(function(){ var $this_filter = $(this); $this_filter.on('click', 'a', function(){ var project_type = $(this).data('optionValue').slice(1); var url = window.location.href; if (url.indexOf('?') >= 0) { url = url.slice(0, window.location.href.indexOf('?')); } if (project_type) { url = url + '?type=' + project_type; } // console.log('url: ' + url); window.history.replaceState(null, document.title, url); return false; }); }); При клике не кнопку на странице в историю дописывается url типа http://sws-group.zephyrlab.ru/projects/?type=commercial После просмотра проекта категории commercial возврат идет уже на url с ?type=commercial.

Ответ 3



Перехватить событие возврата на страницу не получилось, задача решена другим способом. Я добавил на стороне сервера в http-заголовок Cache-Control значения no-store, must-revalidate, как написано здесь: https://stackoverflow.com/questions/49547/how-to-control-web-page-caching-across-all-browsers Таким образом, при нажатии "Назад" страница автоматически обновляется браузером вообще без использования дополнительного кода JavaScript. Сделанные пользователем изменения при этом не теряются, т.к. они до этого были сохранены на сервере. При обновлении страницы по F5 также получаем с сервера самое последнее состояние страницы. Если бы состояние хранилось не на сервере, пришлось бы дописать немного JavaScript-кода (для работы с localStorage или куками), но history.replaceState я бы использовать не стал. Вместо хранения состояния (на стороне клиента) в history, гораздо удобнее хранить то же самое состояние в localStorage.

среда, 26 февраля 2020 г.

Уровни кэша процессора

#память #кэширование


Возьмем пример:

L1 - 128Kb
L2 - 512Kb
L3 - 2Mb


Зачем нужно несколько уровней кэш памяти?

Почему скорость L1 > L2 > L3 (разный SRAM дизайн?)?

Зачем нужно L1i для инструкций и L1d для данных?

Как рассчитывается оптимальный размер и количество кэш уровней?
    


Ответы

Ответ 1



Зачем нужно несколько уровней кэш памяти? Если бы могли, то весь кэш сделали бы L1. Да и вообще, все ОЗУ затащили бы в процессор в виде кэша. Но тогда процессор будет много потреблять и расплавится (при заданной технологии производства и допусках). Почему скорость L1 > L2 > L3 (разный SRAM дизайн?)? Опять же чтобы процессор не расплавился. Зачем нужно L1i для инструкций и L1d для данных? Теперь же две очереди, отдельно для инструкций, отдельно для данных. Вот и кэша два. Как рассчитывается оптимальный размер и количество кэш уровней? Там ничего особо не рассчитывается. Сколько места на кристалле остается после размещения ядер, все отдается под кэши разных уровней. UPD1: А вообще-то есть форумы разработчиков процессоров, даже русскоязычные (да-да не смейтесь). На крайний случай есть форумы разработчиков плат, раньше это было на каких-нибудь телесистемах а сейчас не знаю где, но можно найти. Спросите там, там Вам подробнее объяснят.

Ответ 2



Основная проблема в обеспечении максимальной загрузки процессора и минимизации простоев из-за загрузки данных из памяти. Несколько уровней кэша различаются скоростью доступа в первую очередь. Уровневое кэширование позволяет оптимизировать загрузку данных из основной памяти и держать для процессора нужные страницы "под рукой". Тут хорошая аналогия склад - магазин - холодильник - тарелка (кэширование продуктов). Подробно кэши описаны у Танненбаума (Архитектура ЭВМ).

четверг, 13 февраля 2020 г.

Какое кэширование использовать для социальной сети?

#база_данных #сервер #кэширование


Мы написали социальную сеть, в которую ожидается огромное количество посетителей.
Мы решили использовать memory cache для нее. В нашей социальной сети мы используем
комбинации серверов Windows и Linux. Память должна быть установлена ​​на Linux сервере.
Если у вас есть опыт работы с кэш-памятью, подскажите, пожалуйста, которая является
лучшим выбором для такого типа задач? И еще что вы думаете о RADIS, MongoDB, Hibernate
или Memcache?    


Ответы

Ответ 1



Вам надо сначала понять, сколько данных и какие данные вы хотите кешировать! Отсюда сразу станет ясно преимущества и недостатки того или иного решения. Так как непонятно, что Вы хотите кешировать, то напишу от себя (проверено на редис): все статичные шаблоны, используемые для генерирования динамических страниц и данных сложите в кеш (сократите на файловых операциях). Что касается базы данных, то какое бы вы решение ни выбрали, у вас туда войдут только справочники (неизменяемые или редко изменяемые), к примеру кладр, для выбора адреса. Ни в коем случае не ложите туда изменяемые данные или растущие от количества посетителей (уников). На этапе старта будете прыгать от счаться, а впоследствии все упадет мертвым грузом (головные боли, ночи программирования напролет и т.д.). Что еще можно положить (все то, что вешает базу и надолго, но опять же с учетом, что это не будет расти в памяти). Все это легко объясняется, прочитав концепцию (на примере редис): все данные хранятся в памяти. А сколько у Вас памяти (8 гб или супер сервер 16 или распределенная сеть из нескольких серваков (16 или 8*количество серверов + широкий канал между ними))? P.S.: Что касается redis: если Вы хотите использовать расширенный функционал по обработке данных, то несомненно его. Если просто key-value, то любой.

Написание прокси-сервера для чайника

#сервер #proxy #кэширование #прокси


В прошлом вопросе (Вопрос) я выяснил , что для запроса погоды мне нужен прокси сервер,
который будет кэшировать данные с погодного сервера. Писать сервер придется мне самому,
но до этого ни с чем подобным не сталкивался (серверной частью). Писать решил на python
3. Можете ли ткнуть в литературу или дать ссылки как можно реализовать такой кэширующий
сервер, и правильно ли я вообще выбрал python?
    


Ответы

Ответ 1



«По-науке» эта конструкция называется «Обратный прокси» (reverse proxy). У вас есть некоторый backend-сервер, который генерирует какие-то полезные данные, и есть frontend-сервер, который кеширует на себе редкоизменяемые данные. Такая схема чаще используется, для кеширования локальных ресурсов какого-нибудь сайта, чтобы не гонять данные лишний раз, через основной backend-сервер. В вашем случае backend-сервером будет погодный сервер. Если, логику формирования запросов клиенты возьмут на себя, то проще будет использовать некий уже готовый и отлаженный web-сервер. В принципе, тут подойдет любой (nginx, lighttpd, apache, Ваш собственный). Чаще всего для таких целей используют nginx. Он весьма шустр и достаточно прост в настройке. Вот, например, настройки для nginx: http://reviewsignal.com/blog/2013/08/29/reverse-proxy-and-cache-server-with-nginx/ http://ashep.org/2011/nginx-obratnyj-proksi-server/#.V6ECut_XfmE Официальная документация: https://www.nginx.com/blog/nginx-caching-guide/ В итоге у вас получится что-то вроде: events { worker_connections 8096; multi_accept on; use epoll; } http { proxy_cache_path /tmp/cache levels=1:2 keys_zone=cache:60m max_size=1G; server { listen 80; server_name your-proxy-server-name.com; location / { proxy_pass http://any-weather-server.com; proxy_redirect off; proxy_set_header Host $host; proxy_cache STATIC; proxy_cache_valid 200 1d; proxy_cache_use_stale error timeout invalid_header updating \ http_500 http_502 http_503 http_504; } } } Тут надо будет внимательно отнестись к заголовкам, которые ожидает сервер погоды от вас. Вероятно, вам придется немножко поколдовать с proxy_ignore_headers proxy_hide_headers proxy_set_headers Подобную проблему обсуждают тут: https://stackoverflow.com/questions/9230812/nginx-as-cache-proxy-not-caching-anything Заголовки, вам придется настраивать в любом случае, в том числе и если frontend-сервер будете писать сами. Если писать самому, я думаю, было бы проще найти некоторое готовое решение, и его переиспользовать. Например, достаточно просто это сделать с помощью tornado. А в качестве кеша использовать наример memcached или redis. https://stackoverflow.com/questions/16524545/how-to-write-a-web-proxy-in-python

четверг, 23 января 2020 г.

Как сохранять и загружать данные в/из кэша при повторном запуске приложения на Android?

#android #база_данных #sqlite #кэширование


Есть полезные ссылки на данную тему? Я так понимаю, что мне нужно будет создать БД
на SQlite в приложении, занести туда данные. А вот как дальше действовать? Как мне
их отобразить из кэша, а не заново "скачивать"? 
    


Ответы

Ответ 1



Из вопроса не ясно какой объем данных (переменных, массивов) необходимо сохранять при выходе из приложения?! Если данных немного, то можно воспользоваться механизмом: "SharedPreferences": Назначение переменных для сохранения: static final String SAVE_USERID = "save_userid"; static final String SAVE_PASSWORD = "save_password"; public static String un = "MyName", pw = "MyPassword"; Метод сохранения: public void saveSettings() { SharedPreferences.Editor ed = getSharedPreferences("setting",MODE_PRIVATE).edit(); ed.putString(SAVE_USERID, un); ed.putString(SAVE_PASSWORD, pw); ed.commit(); } Медод восстановления: public void loadSettings() { un = getSharedPreferences("setting",MODE_PRIVATE).getString(SAVE_USERID, ""); pw = getSharedPreferences("setting",MODE_PRIVATE).getString(SAVE_PASSWORD, ""); } Если данных много, то необходима работа с БД.

Ответ 2



посмотрите на Firebase, там реализованы БД для NoSQL (для сохранения структурированных данных типа JSON) и реализовано кеширование, а так же много других полезных фич

Кэширование в AngularJS

#angularjs #кэширование


Доброго времени суток. Столкнулся с такой проблемой с кэшированием. Сначала я проверяю
есть ли данные в кэше, если да, то достаю их оттуда и заношу в scope контроллера, если
нет, то гружу их с сервера. Так вот, если данные уже занесены в кэш и я где-то изменю
данные через ng-model в $scope.players, то они также обновятся и в кэше. Почему так
происходит? Ведь нигде явно не прописано mycache.put("players", newVals)

var mycache = $cacheFactory.get("myCache");
$scope.players = mycache.get("players");

if (!$scope.players) {
    $http.get("test.php").success(function(response) {
        mycache.put("players", response);
        $scope.players = response;
    });
}


И еще вопрос. Можно ли как-нибудь получить доступ к данным в mycache через mycahe.get(),
которые кэшируются неявно, вот таким способом:

var mycache = $cacheFactory.get("myCache");

$http.get("test.php", { cache: mycache }).success(function(response) {
    $scope.players = response;
});

    


Ответы

Ответ 1



Для объяснения, посмотрите на этот jsfiddle В кэше мы сохраним объект response, тот же самый объект, на который $scope.players ссылается. Поэтому, когда обновляем $scope.players, обновляем одновременно то, что в кэше. Функция put из $cacheFactory выглядит вот так: put: function(key, value) { [...] if (!(key in data)) size++; data[key] = value; // <-- Нету копия объекта [...] return value; } Кстати говоря, функция put возвращает value, поэтому можете заменять: mycache.put("players", response); $scope.players = response; со следующим: $scope.players = mycache.put("players", response); Можно ли как-нибудь получить доступ к данным в mycache Если вы хотите узнать всё, что в кэше, есть возможность использовать функцию info(), которая возвращает что-то в этом роде: {"id": "myCache", "size": 1}

среда, 22 января 2020 г.

Круговой буфер в системах кэширования

#php #алгоритм #кэширование #memcached #расширения


Скажите, а есть ли механизм в PHP и в одной из систем кэширования, используя который
можно назначить максимальное количество записей и разные типы поведений для блоков
где они хранятся? Приведу пример - есть данные которые нужно сохранять после окончания
работы на определенное время (скажем до 10 минут), но нужно добавить контроль того
сколько «мест» (скажем, до 1000) выделяется под эти данные и на какое время. Также
нужно учесть что есть разные типы данных (конечное число около 25-20) и сделать круговой
кэш для каждого типа. В memcache так можно сделать храня заголовок о данных в отдельном
блоке, но есть ли плагины или расширения с таким функционалом?

Опять же факт что тип данных по размеру будет фиксированный, так что если есть решения
для переиспользования выделенной памяти для уменьшения фрагментации памяти то это ещё
один большой плюс.
    


Ответы

Ответ 1



Memcache работает эталонно быстро - там нечего оптимизировать. Единственное на что можно пожаловаться в плане производительности - это время подключения к мемкеш-серверу: но при проблемах ускорение делается с помощью прямых рук сисадмина. Также нужно учесть что есть разные типы данных (конечное число около 25-20) А почему бы вопрос преобразования типов данных и "заталкивания чего-угодно в кеш" не назначить на сервис внутри PHP-кода? Как правило, всё что выходит за рамки int и string хранят в JSON-е, а уже прослойка через которую идёт кеширование(есть например в любом PHP фреймворке ) - отдаёт вам сразу всё в нужном формате. Чем такой подход не устраивает? а есть ли механизм в PHP и в одной из систем кэширования, используя который можно назначить максимальное количество записей и разные типы поведений для блоков где они хранятся? Всё это регулируется "прослойками кеша" , например Zend\Cache или yii\caching . Вы можете написать свой класс-адаптер, или трейт, который расширяет существующий класс во фреймворке: который добавляет требуемое вам поведение. При чём один адаптер может использовать сразу несколько бекендов кеша (под бекендом я имею ввиду сервис кеша - memcache, sql, tarantool, file, apc, ...). Например там где требуется "безразмерный" кеш не падающий при перезагрузке и есть админ - tarantool, sql - где нет админа, там где требуется небольшой объём - memcache, где не требуется масштабирование и нужна простота методом топора file . Так можно добиться любой производительности, вне зависимости от форматов данных в кеше - скорее тут факторы другие. Потери производительности от некой "эталонной производительности кешированаия", если и будут - то явно не от фрагментации памяти. Напомню - что memcache, это сервис, общение с которым происходит через TCP : то есть к нему надо подключаться, тратить время на сетевой цикл принятия-отправки пакетов, собирать пакеты, и другие свойства TCP, если мемкеш-сервер отделён от сервера выполнения - это ещё и сетевые ожидания. Всё это на порядок больше съест тактов процессора, чем затраты самого мемкеша на извлечение фрагментированных данных. Плюс сервис он на то и сервис, что берёт на себя вопрос оптимизации и хранения ваших данных - вам об этом думать не надо. Если только не хотите написать свой "самый лучший" сервис кеша: но это уже будет совсем другой вопрос. Затраты на преобразование форматов будут, да. Но они будут в любом случае - как с бубном не пляши. И главное помнить, что процессорные мощности машин стоят обычно копейки по сравнению со стоимостью времени, которое программисты убивают чтобы достичь идеальной производительности под свою задачу :)

понедельник, 6 января 2020 г.

Грамотное кэширование в NodeJS проекте

#nodejs #кэширование #redis #highload


Планируется highload-проект с использованием NodeJS, MySQL, Redis, NGINX.
За основу взят nodejs фреймворк express, и для работы с сокетами (может это и не
хорошее решение) - socket.io. Проект представляет собой single page application, предполагается,
что сокет-соединение будет открыто повсеместно. Будет большое количество страниц, которые
являются статистично-информативными, и не щадят бд тяжелыми запросами.

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


Например, есть запрос к БД, его результат мы можем сохранить в Redis в виде JSON,
при следующих обращениях уже будем брать его из Redis и, когда необходимо - делать
инвалидацию данного кэша. - Насколько правильно это? И стоит ли именно в JSON хранить
данные?
Нам иногда нужна закэшированная страница не только в JSON, а кусок или целиком HTML.
- Правильно ли хранить эти куски в Redis?
Есть идея реализовать session-storage в Redis, как это сделать правильно? например
ключом будет id сессии, а значением - массив данных о клиенте. - Это правильно или
нет? Как будет лучше?
В каких случаях стоит записывать кэш в файлы на диск?
Стоит ли вообще пихать все что выше описано в redis? 


Я не имею опыта в highload, поэтому мне интересно все, что вы скажете, очень нужны
грамотные советы в построении проекта на перечисленных компонентах. Может, есть какие-то
рекомендации по работе с сокетами в highload, либо еще что-то, что касается основ такого
проекта. 

Признателен за ваши полезные советы, благодарю за внимание!
    


Ответы

Ответ 1



Например, есть запрос к БД, его результат мы можем сохранить в Redis в виде JSON, при следующих обращениях уже будем брать его из Redis и, когда необходимо - делать инвалидацию данного кэша. - Насколько правильно это? На сто процентов, но своевременная инвалидация кэша - довольно большой челлендж. И стоит ли именно в JSON хранить данные? Вообще меня несколько коробит сохранять данные строкой, но особого выбора нет, и вообще это не должно влиять ни на что. Нам иногда нужна закэшированная страница не только в JSON, а кусок или целиком HTML. - Правильно ли хранить эти куски в Redis? Нет, у вас же SPA, весь рендеринг должен быть на клиенте. Есть идея реализовать session-storage в Redis, как это сделать правильно? например ключом будет id сессии, а значением - массив данных о клиенте. - Это правильно или нет? Как будет лучше? Если сессия у вас считается за постоянное хранилище, то неправильно, потому что Redis не предназначен для постоянного хранения данных (хоть и умеет время от времени скидывать данные на диск). Вместо этого лучше организовать двухуровневое (или даже трехуровневое) хранилище из связки Redis - БД (In-Memory - Redis - БД в случае трехуровневой связки). В каких случаях стоит записывать кэш в файлы на диск? Как правило, ни в каких, есть база данных. Туда можно скидывать результаты тяжелых запросов, чтобы все ноды их видели, но даже в этом случае я бы организовывал промежуточный Redis-слой. Стоит ли вообще пихать все что выше описано в redis? Чем больше будет висеть в кэше, тем лучше, но время от времени посматривайте на количество съедаемой оперативной памяти. Может, есть какие-то рекомендации по работе с сокетами в highload, либо еще что-то, что касается основ такого проекта. Помните про две вещи Проект обязан масштабироваться горизонтально, т.е. простым добавлением новых серверов. Из этого вытекает, что изменения на одном сервере должны быть видны для всех остальных (это достигается за счет использования одного Redis и БД) Существует такая штука, как dogpile effect, который означает кучу пользователей, одновременно запросивших один и тот же ресурс. В случае с вышеописанным трехуровневым хранилищем сто синхронно пришедших пользователей не обнаружат запись в кэше и ломанутся (все сто) к БД. В случае, если в БД 20 одновременных подключений, и каждый запрос занимает 30мс, то последний пользователь получит данные не раньше, чем через 150мс (это в идеальном случае, на самом деле там будет значительно большее число), что очень нехорошо.

Кеширование картинок Nginx & Laravel

#php #laravel #nginx #кэширование


Для кеширования картинок (nginx) у меня на сейте прописано правило:

location ~* \.(gif|ico|jpe?g|png)$ {
             expires     1w;
    }


Сами картинки хранятся в ларавеле в папке site/storage/app/dir
и есть картинки из /site/public/img по итогу фалы из /site/public/img после кеширования
доступны а из site/storage/app/dir нет - отдают 404.

Примечание в структуре site/storage/app/dir вместо dir могут другие папки например
slider category shop 
    


Ответы

Ответ 1



Вы уверены что картинки из папки storage вообще должны быть доступны? По-умолчанию все доступные клиентам ресурсы хранятся в папке public и ресурсы для фронта отдаются по параметру public_path()/ссылка на файл. У меня к примеру в папке storage находятся "исходники" файлов, а в папке public уже обработанные, сжатые и подготовленные для клиентов

Ответ 2



Лучше всего заведите папку public_path()/upload с правами на запись для пользователя www-data. storage_path() используется для того, что не должно быть доступно публично. То что выделаете сложно назвать кешированием т.к. никак не увеличивает скорость доступа. Это бесполезное дублирование файлов на диске. Часть конфига nginx который вы прислали вполне подойдет Вам на начальном этапе.

Ответ 3



В итоге пришлось сохранять все в public так как при другом подходе терял в скорости.

воскресенье, 29 декабря 2019 г.

Как построить эффективное кэширование

#кэширование #memcached #php


У меня VPS сервер. Кожу на php, в качестве базы использую MySQL. В последнее время
очень нужно повысить пропускную способность сервака.
Уже и так все кэшируется по файлам на жестком диске - это конечно повышает производительность
в разы, но меня и этого теперь мало.
Расскажите что эффективней использовать для этих целей? Именно для кэширования. Из
соседнего поста я узнал, что есть:

memcache для кэширования данных
apc - для кэширования пхпшечки

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


Ответы

Ответ 1



это конечно повышает производительность в разы, но меня и этого теперь мало Для начала давайте определимся - чего вам мало? Вы проводили тесты, измерения и вам выдало - php код выполняется слишком долго, в результате чего вместо 1000 человек можно обслужить только 100? Надо ускорять там, где действительно это надо, а то получится - экономили, экономили, а оказалось на спичках экономим, хотя в кармане куча зажигалок. Memcache и APC предназначены для разных целей (данные и код, как вы уже заметили). Надо понимать, что обращение к PHP, если грубо, состоит из следующего: открыли соединение к серверу на определенном порту, определили какой файл должен исполняться интерпретатор прошелся по этому файлу (типа скомпилировал), отправил на выполнение php-скрипт начал свое выполнение, выполнил нужные действия, получил определенные данные, вернул данные клиенту Это если грубо, но смысл думаю ясен. Если больше всего тормозит п.3 - значит в первую очередь необходимо кэширование данных, Если п.2 - кэширование кода (дабы не интерпретировать каждый раз одно и тоже), либо перенос части функционала, например, на С/С++ Хотя может быть просто используется неоптимальный алгоритм? Или может оказаться, что PHP-код отрабатывает быстро, даже очень быстро, но все равно тормоза есть. повысить пропускную способность сервака Может вы все-таки упираетесь в сеть? Пропускная способность сети может слишком маленькая, или настройки сервера не оптимальны (например, выставлено небольшое количество возможных подключений?) В общем без конкретных тестов на узкие места трудно сказать что и как необходимо. Но ведь вопрос не в этом? Вопрос - что лучше? Ответ: memcache - кэширование данных, APC - кэширование кода. Что использовать? См. выше.

Ответ 2



Используйте javascript-MVC фреймворки. Там можно закешировать все шаблоны, а данные будут приходить от RESTfull-сервера как JSON. В этом случае вы не будете париться о рендеринге шаблонов на стороне сервера. Это уменьшит нагрузку на файловую систему. Как сказали выше - Local Storage, кешируйте сами данные на стороне клиента. Memcache (хотя по мне лучше - Redis). Для кеширования результатов запросов и для хранения сессий. Меньше запросов к медленному MySQL и к файловой системе. APC для ускорения php-сервера. nginx вместо медленного Apache. Примерно так выглядело мое последнее нагруженное приложение

четверг, 26 декабря 2019 г.

Как организовано кэширование в клиентах соцсетей?

#ios #android #java #objective_c #кэширование


Всем добрый вечер!
В процессе написания приложения возникла необходимость кэширования определенной информации
на устройстве, но для меня пока не понятен алгоритм такого кэширования!
То есть, например в приложении клиент ВК на андроид в режиме оффлайн я могу просмотреть
довольно большое кол-во заранее просмотренной информации, например переписку, собственный
профиль, некоторые фото, список друзей и т.д.. 
Я пытаюсь написать подобный клиент и меня интересует как это лучше организовать,
может существуют готовые рекомендации или набор определенных правил, алгоритмов.
Буду рад вашей помощи!
Заранее всем большое спасибо!    


Ответы

Ответ 1



По науке делается так: Пишем сервис/фоновый поток который берет из сервера по расписанию необходимые данные и складывает их в SQLite базу. Этот же поток/сервис должен следить за размером кэша, авторизацией и проч. вещами. Наружу он практически не должен высовываться - тихо сидеть в фоне и подгружать данные по мере необходимости Над SQLite базой организуем ContentProvider выдающий наружу интересующие нас поля Сверху ContentProvider'а нахлобучиваем CursorAdapter, который в свою очередь отображает данные на кастомный ListView Если сделать ListView по уму то можно его "научить" операциям pull-to-refresh - то есть при достижении донышка "дергать" фоновый поток и подгружать старые записи.

Ответ 2



В SQLite скидывать закэшированные данные (ну или просто в директорию складывать какую-нибудь), в случае отсутствия сети показывать эти данные. И даже, если есть сеть, можно показывать эти данные, если они валидны. К примеру, сделать, чтобы запросы возвращали помимо данных ещё и хэш данных, чтобы каждый раз всю ленту не грузить в случае, если хэш не изменился.

Ответ 3



Кроме метода, описанного выше, иногда применяют прямую сериализацию в файл: объект сохраняется в файл в формате json или xml, либо вообще используется бинарная сериализация. Последняя - самая быстрая (потому что не требует парсинга), но требует немало хлопот, если язык не поддерживает сериализацию объекта "как есть". В таком случае приходится делать описание необходимых свойств объекта и по очереди их складывать в файл, чтобы потом их ровно в таком же порядке прочитать в те же самые свойства, либо выдумывать собственные костыли по сохранению справочников и подобных сложных типов. Также обычно приложению предоставляется механизм для сохранения данных (если делаете Телеграм - вам нужен IsolatedStorage), но он зачастую оказывается медленнее - где-то видел статью, что твиттер на виндофоне в результате плюнул на все и перешел на бинарную сериализацию, потому что все остальное по скорости было сопоставимо с прямым интернет-запросом. Там же писали, что использовали максимальное дробление данных по файлам, что позволяло подгружать данные только в том случае, если они нужны, что уменьшало оверхеды. И последнее касательно твиттера - эта штука своим кэшем съела у меня 70мб памяти на андроиде. Если делаете реальный кеш - не забывайте удалять устаревшие записи.

воскресенье, 22 декабря 2019 г.

Как кешировать страницы в Django?

#python #django #кэширование


Читал, что с помощью кеширования можно добиться значительного ускорения генерации
страницы, экономя при этом ресурсы сервера. win-win, как не поверни. Достаточно ли
будет навесить на view нужный декоратор, описанный в документации, чтобы все было круто?
    


Ответы

Ответ 1



Достаточно для полностью статических страниц. Совсем недостаточно и вредно (приводит к ошибкам, далеко не всегда очевидным) для страниц динамических. Предположим, что у нас есть некая страница, требующая кеширования. Она не статична, присутствуют элементы, наличие и вид которых зависит от конкретного пользователя и его прав. Самый простой пример - шапка сайта, где написано Привет, {{ username }}. Также есть элементы, общие для всех пользователей. Сам шаблон выглядит как-то так: {% include header.html %} {% for elem in elements %}

{{ elem.text }}

{% endfor %} {% if user.has_rights %} {% endfor %} где elements = Elements.objects.filter(display=True).all(), то есть список элементов общий, но кнопка только для администраторов. Еще пример - магазин, где список товаров - общий, а корзина различна для всех. Django из коробки предоставляет несколько способов закешировать данные: Глобально для всех запросов: нужно включить UpdateCacheMiddleware и FetchFromCacheMiddleware: MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', # ... Другие слои ... 'django.middleware.cache.FetchFromCacheMiddleware' ) Глобально - значит пользователь ВАСЯ залогинившись закеширует данные, специфичные для своего пользователя (например, приветствие "Привет, Вася!") и все пользователи, пришедшие после Васи получат его приветствие вместо своего. И все минуты или часы до протухания они будут видеть себя как Васю, и у них не будет никакой возможности отменить или избегать такого поведения. Это будет печальное поведение, путающее пользователей. Пригодно, если у нас пара статичных страниц и ни пользователей, ни динамического контента нет. Но тогда зачем Django? Индивидуально под каждую вьюху: @cache_page(<время в секундах>) Страдает от тех же самых проблем, но хотя бы можно поставить период обновления индивидуально. TTL (Time to live) не сделает этот инструмент волшебным, но подойдет для каких-то редких страниц без шапки с приветствием. Закешировать что-нибудь в шаблоне, используя директиву {% cache %}. Уже много лучше - можно отдельно закешировать шапку с приветствием для каждого пользователя отдельно, а для анонимов отдельно + общие данные отдельно. Однако, в этом случае страдает скорость генерации страницы, потому что процесс загрузки шаблона и его отрисовки крайне неспешный и выгоды в скорости не так высоки, как может ожидаться. Также не факт, что другие шаблонизаторы поддерживают такой функционал. Также тот факт, что мы закешировали конечный результат (HTML разметку) не отменяет необходимости обратиться к БД во вьюхе и обращаться мы тогда будем совершенно зря, потому что результаты не будут использованы. Однако, такое решение тоже имеет право существовать - генерация HTML на основе шаблонов не самая быстрая операция, а в сжатом виде занимает считанные мегабайты на тысячи страниц. Если отойти от производительности, то с точки зрения архитектуры у кеша внутри шаблонов также не все гладко. Нужно, например, внимательно помнить обо всех условиях отображения, обо всех условиях во вьюхе и в шаблоне, что в долгосрочной перспективе с несколькими разработчиками и кучей абстракций в течение лет может затруднить разработку и хуже того - чревато трудноуловимы ошибками, связанных с кешированием частей, специфичных для какой-то одной группы пользователей. Например, мы не учитываем статус суперпользвателя. Вася-админ запросил страницу первым после момента ее протухания - и вот уже в кеше страница с админскими кнопками. Или чаще наоборот - админ-Вася потеряет все свои кнопки, потому что в кеше страница для анонима. Кеширование на уровне моделей, предоставляемое несколькими сторонними приложениями. Кажется, что это и есть серебряная пуля, но на деле у такого подхода множество ограничений и самое главное ограничение - все механизмы скрыты от программиста и подключив эту штуку в уже существующий проект будет сложно ответить сломается ли что-нибудь или нет. С такими страхами невозможно будет использовать основную фичу подобного подхода (кеширование без TTL с обновлением по событиям CRUD). Если бы кеширующая библиотека предоставляла гарантии того, что как бы не изголялись, то данные в кеше никогда не протухнут - это было бы лучшее решение. Единственное решение, которое явно пишет о гарантиях (и поддерживает старую жангу 1.6+) и внушает этим доверие - django-cachalot, однако, мне неизвестно, выполняются ли это гарантии на практике в краткосрочной и долгосрочной перспективе. Даже если так, то всплывают другие ограничения - например, необходимость синхронизации часов нескольких серверов или репликаций. Забыть про все это, положившись на внутренне кеширование Django внутри QuerySet. Тоже могло бы быть каким-то временным выходом, однако, в этом случае памяти на это не напасешься.

пятница, 20 декабря 2019 г.

Redis, как кэш для SQL запросов в веб-приложении

#проектирование #веб_программирование #кэширование #структуры_данных #redis


Я впервые использую редис в веб-приложении. Изначально, цель была - кэшировать результаты
сложных запросов, которых было не очень много, это не так сложно, и я подумал, почему
бы не кэшировать всё? Ну или почти всё.

И так, я написал конструктор mysql-запросов, который всегда перед тем как выполнить
запрос к бд, принимает необязательный параметр - ключ, по которому хранятся данные
в redis. В итоге если такой ключ был передан, конструктор сначала посмотрит, есть ли
данные по ключу в редисе, и если есть - то он вернет данные из редиса и к мускулу обращаться
не будет, а если нет - то выполнит sql-запрос, вернет данные в контроллер и в соседнем
потоке запишет их в редис (используя тот самый, переданный ключ). С запросами на выборку
понятно, а вот если мы делаем insert/update, то мы удаляем из редиса все данные которые
попали под маску переданного ключа, и тогда при новой выборке кэш обновится.

Все что попадает в редис, в основном хранится в виде JSON. И, вот настал тот ужасный
момент, когда количество хранимых данных стало большим (а на проде, будет еще в over
9999 раз больше), и самое важное - много дублирующихся данных. Приведу простой пример.
Мы заходим в раздел articles, загружаются посты которые находятся на первой странице
(пусть их по умолчанию отображается по 50). В этот момент, контроллер сформировал ключ
articles:list:page_1 по которому будет искать данные в редисе. Дальше походили по страницам,
сформировали кэш для articles:list:page_2 articles:list:page_5 articles:list:page_19
articles:list:page_28 .... После чего, нажали на кнопку "Показать все статьи", тем
обратились по ключу articles:list:all - понятно, что тут хранятся все записи (и я думаю
- это ужасно!).  Потом мы открыли несколько статей articles:post:id1234 articles:post:id567
articles:post:id890. Вы представьте сколько уже есть дублирующих данных в редисе, а
если мы дадим пользователю выбирать кол-во выводимых записей, то тогда появятся такие
ключи: articles:list:page_1_55 articles:list:page_3_55 и тд (_55 записей на странице).
Это приведет к большой беде. Теперь дальше, если  в админке мы отредактируем какой-то
пост, а заголовок этого поста выводится в листинге, тогда мы удалим из кэша старые
данные так articles:post:id890 и очистим весь листинг articles:list:* - что есть тоже
очень плохо.

А что касается сложных запросов к бд, есть еще один случай, когда сам пост хранится
в одной таблице, комменты к нему в другой, лайки в третьей и еще 5 таких зависимостей.
Сейчас (без редиса) это вытягивается с помощью сложного запроса с JOIN'ами (допустим
их 7 шт.). Это все можно сохранить в редис, но тогда если кто-то лайкнет пост, нужно
инвалидировать кэш, и заново придется выполнять сложный запрос. Есть вариант разбить
это на 7 простых запросов и сохранить каждый в редисе, и тогда если кто-то оставит
коммент или лайкнет, то нужно будет только один простой запрос выполнить. Вот, как
в такой ситуации было бы правильно поступить? (не будем говорить о тестах, будем рассуждать
примерно, берем какие-то средние значения и среднестатичные случаи). 

Что в этом случае вы посоветуете делать? Как правильно реализовать хранение данных
в кэше? Возможно стоит пересмотреть способ хранения, может не хранить коллекции :list:
целиком, а извлекать всегда какие-то части (элементы)? Я вот не могу в этом разобраться.

p.s. буду благодарен за ссылки на интересные статьи по теме (желат. на русском),
и по настройке редис на сервере, да и вообще как можно больше про редис хочется узнать
(не просто из постов на хабре, а что-то более подробное и доходчивое для человека который
впервые с ним работает)
    


Ответы

Ответ 1



Вы столкнулись с некоторыми вещами, которые не очень хорошо помещаются в тот мир, в котором вы работали раньше. В какой-то степени можно это назвать просто NoSQL, но это не совсем так, в любом случае могу поздравить с важным шагом в карьере. Дублирование данных Первое, о чем хотелось бы сказать: самое важное - много дублирующихся данных Дублирующиеся данные сами по себе нормальны. Нормализация базы данных SQL невольно учит нас тому, чтобы данные были в единичном экземпляре, но это, на самом деле, не аксиома. Дублирующиеся данные сложнее поддерживать, но само их наличие в целях улучшения работы приложения - это абсолютно нормально. Кроме этого, стоит вставить мою любимую ремарку про модель работы приложения: если сейчас у вас это де-факто pull-on-change - сформировать данные по запросу, то есть гораздо более интересная модель push-on-change - когда данные для запросов подготавливаются при добавлении, и в этом дублирование данных подразумевается само по себе. Представьте себе некую социальную сеть с лентой новостей. При стандартном подходе (и SQL-бэкенде) придется формировать гигантский SQL-запрос, который будет проверять необходимость показа каждой записи, наличие доступа к ней (мы же не хотим показывать приватные записи, верно?) и прочие атрибуты. Push-on-change же предлагает в этом случае в момент обновления записи в ленте вычислять, кому она должна быть показана, и формировать таблицу вида 'id пользователя | id записи | datetime', чтобы просто доставать из нее N первых записей, по которым уже формировать запрос по новостям. Данные в этом случае де-факто будут продублированы, но это необходимо, чтобы ускорить многократные read-операции за счет однократной write-операции. С переходом в хайлоад это становится особенно актуально, потому что один сервер не в состоянии хранить все данные и/или выдержать нагрузку, а поэтому необходимо разделять ответственность между серверами (что может поставить запрет на джойны, например). Однако, Отказ от подготовки ответов на все запросы геометрическая прогрессия, конечно, не позволяет подготовить заранее готовые ответы на все возможные запросы: Вы представьте сколько уже есть дублирующих данных в редисе, а если мы дадим пользователю выбирать кол-во выводимых записей, то тогда появятся такие ключи: articles:list:page_1_55 поэтому от вышеприведенного примера действительно стоит отказаться - у Redis нет столько оперативки (как и от :all - тут проблема не в оперативке, а в количестве данных, которые надо будет передать по сети и затем разобрать). Конкретно в вашем случае есть две вещи, которые я считаю необходимым отметить Текущий подход работает, де-факто, на кэширование конкретных запросов Redis используется как KeyValue-кэш Первое подразумевает тот самый рост данных в геометрической прогрессии со степенью, зависящей от количества параметров, по которым может проводиться выборка. Вам не обязательно получать из Redis уже готовый ответ - вы можете получать его либо по частям, либо бОльшим куском. В случае со страницами по 55 записей вы можете просто хранить "верхушку" таблицы в редисе: # пусть верхушка состоит из 500 записей from = 63 to = 63 + 55 if to < 500: top_entries = redis.get 'articles:top' # в редисе может не оказаться ничего или оказаться устаревший и слишком короткий список if top_entries && top_entries.length > to: return top_entries.slice from, to return article_repository.get_slice from, to В этом случае приложение знает, что у него (возможно) есть кусман на 500 записей в редисе, и запрос, скорее всего, проще обслужить оттуда, поэтому пытается сделать именно это. В случае со сложной иерархической структурой проще наоборот, разбить сущность на составляющие: article = redis.get 'article:' + id if not article: article = article_repository.get id if not article: throw new ResourceNotFoundException comments = redis.get 'comments:by-article:' + id if not comments: comments = comment_repository.get_by_article id likes = redis.get ' В этом случае вы делаете из сущностей некоторое подобие строительного материала, который не слишком сильно дублируется и позволяет собирать различные результаты на основе одних и тех же источников (например, поиск по статьям в любом случае пойдет через БД, но лайки для них можно вытащить из Redis без особых затрат по времени). Это не самый богатый арсенал, но он поможет избежать избыточного дублирования, которое может возникнуть, если иерархические сущности кэшируются целиком, и, заодно, уменьшит развесистость операции, необходимой для обновления одной атомарной сущности (т.е. не придется сбрасывать половину кэша из-за одного проставленного лайка). Закешировать всё Кроме всего вышеописанного, я бы относился немного более практично к идее закешировать всё. Сейчас вы пытаетесь уменьшить нагрузку на сервис за счет кэширования, но переносите в Redis буквально вообще все, но это вам не нужно. До десятой страницы новостей доберется один читатель из сотни - вам действительно нужно оптимизировать ее быстродействие? В том случае, если она срендерится за 100 мс, а не за 10, пользователь это не почувствует, и сервер тоже не почувствует, потому что основная нагрузка у него идет на получение первых записей. LazyLoad Все вышеописанное только вскользь говорило о том, как данные попадают в Redis - что в случае добавления записи необходимо пересчитать специально подготовленную выборку. Однако мы знаем, что записи в любом in-memory кэш-сервисе (будь то Redis, Memcache, Aerospike) вечны максимум вплоть до первой перезагрузки машины, и даже подготовленные выборки в этом случае умрут. В этом случае поможет механизм ленивой загрузки - если грубо, то он пытается получить данные из некоего источника данных А, и, если не находит, загружает их из источника Б, кладет в А, и возвращает. В программировании этот подход часто применяется для инициализации тяжелых объектов и вызовов из БД, которые могут быть не нужны: private heavy_object private settings get_heavy_object(): if not heavy_object: heavy_object = new HeavyObject return heavy_object get_settings(): if not settings: settings = read_settings_from_database() return settings В случае с кэшем хранилищем А является не переменная, а кэш, а хранилищем Б - БД: get_article(id): redis_id = 'article:' + id article = redis.get redis_id if not article: # не обнаружен в Redis - ищем в БД article = repository.get id if not article: # значит, он вообще не существует return null redis.put id, article # кладем в Redis для последующих вызовов return article Если вы будете применять эту парадигму везде, то приложению можно будет хоть живьем подменить Redis, оно все равно будет работать. Кроме того - для меня это самый важный пункт - в этом случае можно спокойно инвалидировать существующий кэш, не боясь за само приложение (но, конечно, стоит иметь в виду резко возрастающую нагрузку на БД). Инвалидация (и немного про CAP) Следующий вопрос, который у вас так же возникает - это инвалидация данных, т.е., когда они должны исчезнуть из кэша или обновиться. Самый напроломный вариант - это обновление данных in-place, т.е., как только обновилась сущность, обновились и все связи в кэше. Однако, это плохой вариант - точнее, он отличный, но его невероятно сложно реализовать, не забыв про что-то и не раздув код до невероятных размеров. В случае, если кэш условно-бесконечен, и какой-то тип записей не обновляется, то все пользователи будут видеть устаревшие данных и спрашивать у вас, почему на разных страницах у одной и той же статьи разные комментарии. Тут на помощь приходят две стратегии инвалидации, которые есть в Redis: LRU (Least Recently Used). При переполнении указанного в параметре max-memory размера Redis начнет удалять записи, которые не запрашивались дольше всех. Это гарантирует некоторую ротацию записей (при добавлении новых записей некоторые из старых будут удаляться, чтобы, при повторном запросе, быть обновленными из БД) TTL (Time To Live). Запись с указанным TTL может быть удалена по истечении этого TTL (может быть - в связи с проблемами в выполнении задачи не могу сказать, насколько вовремя это сделает Redis, но вряд ли задержка составит больше трех секунд). Именно это я и хочу предложить в качестве серебряной пули - делайте всем записям TTL в пределах 1-60 секунд, и устаревшие данные у вас гарантированно не продержатся дольше минуты, а благодаря lazy load воскреснут вновь. Резюмируя, проще всего задавать небольшое время жизни записям, и сильно не беспокоиться об инвалидации, пока продукт не вышел на поддержку. В этом случае у вас страдает целостность данных (consistency), но в большинстве случаев она на самом деле не является критичной, более того, существует применимая к распределенным системам теорема CAP (NB: обсуждаемое в этом ответе приложение - это не распределенная система), которая (если очень грубо) говорит о том, что невозможно одновременно поддерживать доступность и согласованность данных - это просто некоторое свойство нашей вселенной, и небольшая задержка в обновлении данных чаще всего не только остается незамеченной, но и не всегда может быть замеченной (если у вас есть задержка в обновлении выдачи статей, то пользователь не ожидает получить новую в тот же момент, когда она была написана - он не знает момента написания). Dogpile effect В связи с параграфом про TTL и lazyload нельзя пропустить так называемый эффект собачьей стаи. С момента запроса обнаружения пустоты в кэше первым клиентом до момента его обновления проходит некоторое время X, за которое могут прийти еще N клиентов. Приложение в этом случае добросовестно попытается реконструировать кэш еще N раз. Единого решения у этой проблемы нет, но вы можете либо ставить лок на время обновления кэша (но если у вас приложение стоит более, чем на одном сервере, то придется использовать сервис, поддерживающий распределенные блокировки - например, consul, etcd или даже реализация с помощью самого Redis - см. Redlock), либо просто считать, что в SLA приложения добавлена возможность работать без кэша вообще (если за кэшем у вас скрывается один запрос для одной записи в БД, то почему бы и нет). Поиск И, наконец, самое веселое. Как объединить сам поиск и подготовку данных для него? В общем случае - никак, ищите по базе, вы не сможете запихнуть все необходимое для запроса в KV-хранилище. Но если вас интересует, как это решается в серьезных случаях (имеется в виду поиск по базе данных, а не уровня яндекса/гугла, конечно), то есть специализированные решения, например, Lucene (и построенный на его основе ElasticSearch), Solr, Sphinx, YoctoDB. В общем случае поиск сводится к построению индексов из полей документа, поиску по этим индексам и агрегированию результата. Микросервисная архитектура Предложенные мной решения по разбиению на building blocks так или иначе приведут к появлению в приложении менеджеров каждой сущности, каждый из которых заведует отдельной сущностью. Хочется сказать, что это разбиение может продолжиться и вне приложения - приложение можно разбить на отдельные приложения, каждое из которых будет заниматься своим доменом. Это так же убьет всякую возможность джойнов, но, на самом деле, она и не нужна. Если пользователи у вас лежат в одном приложении на одном кластере, а статьи, в которых автор указан в виде идентификатор - в другом приложении на другом кластере, то вряд ли вам потребуется искать все статьи, в имени автора которого стоит "Андрей". Как обо всем позаботиться? Скорее всего, через весь ответ так и сквозят вопросы вроде "какой TTL ставить", "нужно ли кэшировать Х" и "какйо прирост производительности я получу". Ответ простой - это вообще никак не узнать, это выясняется только на практике. Деплойте приложение, анализируйте статистику, и через некоторое время вы просто поймете, где оно подтормаживает. Если оно не подтормаживает в месте, где вы забыли поставить кэш - возможно, там и нет смертельной необходимости в нем, и к этому месту нужно будет вернуться, если вообще не останется других задач? Просто пара слов про Redis Во-первых просто хотелось сказать, что кроме Redis есть еще сервисы, выполняющие те же функции (мой любимый - Aerospike). Redis довольно прост в использовании, но не умеет, например, шардиться, у него были проблемы с фрагментацией памяти (порог в 100 мб теоретически имел право забрать до 200 мб оперативки), он однопоточный и в мире NoSQL довольно похож на MySQL в мире баз данных. Тем не менее, с небольшим приложением по меркам интернет-гигантов он наверняка справится без особых проблем.

Ответ 2



MySQL предлагает собственный похожий механизм кэширования: query cache. Только тестирование объективно покажет, что эффективнее именно для вашего веб-приложения: работа без кэша, query cache или ваш велосипед с Redis.

Ответ 3



Может быть, перед тем как пытаться совместить SQL c не-SQL стоит сначала хорошо разобраться хотя бы в чём-то одном? Ваш случай не уникален и в SQL уже есть всё, что может понадобиться для кэширования сложных запросов. В MySQL для этого есть Materialized Views. В других базах данных это называется чуть по другому, но тоже ничего изобретать не приходится.

суббота, 14 декабря 2019 г.

Медленное умножение матриц 1024х1024

#кэширование #матрицы #любой_язык


Почему матрицы размеров 1025x1025, 1023x1023 перемножаются быстрее матриц 1024x1024
стандартным алгоритмом?
    


Ответы

Ответ 1



В комментарий не влезет, но это не ответ. Это именно комментарий. Написал для проверки (см. ниже). Обалдел, ибо у меня, скомпилированное VC++ 2017, таки дало: 1023: 2106433 1024: 7664347 1025: 2106884 При отключенной оптимизации эффект выражен меньше: 1023: 9592402 1024: 11608342 1025: 9480406 Это для 64-разрядного приложения. 32-разрядное, впрочем, почти такое же. 1023: 2910957 1024: 7729545 1025: 2192236 "А я что? сама в шоке!" (с) Анекдот про пограничную собаку... Вот код. #include #include #include #include #include using namespace std; class muTimer { using Clock = std::chrono::high_resolution_clock; bool active = false; Clock::duration duration_; Clock::time_point start_ = Clock::now(), stop_ = Clock::now(); muTimer(const muTimer&) = delete; muTimer& operator=(const muTimer&) = delete; public: using ns = std::chrono::nanoseconds; using mks = std::chrono::microseconds; using ms = std::chrono::milliseconds; muTimer() { reset(); start(); } ~muTimer() = default; muTimer& reset() { duration_ = std::chrono::nanoseconds(0); active = false; return *this; } muTimer& start() { if (!active) { start_ = Clock::now(); active = true; } return *this; } muTimer& stop() { if (active) { stop_ = Clock::now(); duration_ += stop_ - start_; active = false; } return *this; } template unsigned long long duration() { return static_cast (std::chrono::duration_cast(stop_-start_).count()); } }; double m1024_1[1024][1024], m1024_2[1024][1024], m1024_3[1024][1024]; double m1025_1[1025][1025], m1025_2[1025][1025], m1025_3[1025][1025]; double m1023_1[1023][1023], m1023_2[1023][1023], m1023_3[1023][1023]; int main(int argc, const char * argv[]) { for(int i = 0; i < 1024; ++i) for(int j = 0; j < 1024; ++j) { m1024_1[i][j] = rand(); m1024_2[i][j] = rand(); } for(int i = 0; i < 1025; ++i) for(int j = 0; j < 1025; ++j) { m1025_1[i][j] = rand(); m1025_2[i][j] = rand(); } for(int i = 0; i < 1023; ++i) for(int j = 0; j < 1023; ++j) { m1023_1[i][j] = rand(); m1023_2[i][j] = rand(); } { muTimer mt; for(int i = 0; i < 1023; ++i) for(int j = 0; j < 1023; ++j) { double s = 0.0; for(int k = 0; k < 1023; ++k) { s += m1023_1[i][k]*m1023_2[k][j]; } m1023_3[i][j] = s; } mt.stop(); cout << "1023: " << mt.duration() << endl; } { muTimer mt; for(int i = 0; i < 1024; ++i) for(int j = 0; j < 1024; ++j) { double s = 0.0; for(int k = 0; k < 1024; ++k) { s += m1024_1[i][k]*m1024_2[k][j]; } m1024_3[i][j] = s; } mt.stop(); cout << "1024: " << mt.duration() << endl; } { muTimer mt; for(int i = 0; i < 1025; ++i) for(int j = 0; j < 1025; ++j) { double s = 0.0; for(int k = 0; k < 1025; ++k) { s += m1025_1[i][k]*m1025_2[k][j]; } m1025_3[i][j] = s; } mt.stop(); cout << "1025: " << mt.duration() << endl; } } При 1024 ассемблерный код отличается: Вот основной код для 1024: ; 104 : for(int k = 0; k < 1024; ++k) ; 105 : { ; 106 : s += m1024_1[i][k]*m1024_2[k][j]; movsd xmm1, QWORD PTR [rcx-8192] mulsd xmm1, QWORD PTR [rax-8] movsd xmm0, QWORD PTR [rax] mulsd xmm0, QWORD PTR [rcx] addsd xmm1, xmm2 movaps xmm2, xmm1 movsd xmm1, QWORD PTR [rcx+8192] mulsd xmm1, QWORD PTR [rax+8] addsd xmm2, xmm0 movsd xmm0, QWORD PTR [rcx+16384] mulsd xmm0, QWORD PTR [rax+16] addsd xmm2, xmm1 movsd xmm1, QWORD PTR [rcx+24576] mulsd xmm1, QWORD PTR [rax+24] addsd xmm2, xmm0 movsd xmm0, QWORD PTR [rcx+32768] mulsd xmm0, QWORD PTR [rax+32] addsd xmm2, xmm1 movsd xmm1, QWORD PTR [rcx+40960] mulsd xmm1, QWORD PTR [rax+40] addsd xmm2, xmm0 movsd xmm0, QWORD PTR [rcx+49152] mulsd xmm0, QWORD PTR [rax+48] add rcx, 65536 ; 00010000H add rax, 64 ; 00000040H addsd xmm2, xmm1 addsd xmm2, xmm0 sub r8, 1 jne $LL37@main А вот - для 1023: ; 89 : for(int k = 0; k < 1023; ++k) ; 90 : { ; 91 : s += m1023_1[i][k]*m1023_2[k][j]; movsd xmm1, QWORD PTR [rcx-8184] mulsd xmm1, QWORD PTR [rax-8] movsd xmm0, QWORD PTR [rcx] mulsd xmm0, QWORD PTR [rax] addsd xmm1, xmm2 movaps xmm2, xmm1 movsd xmm1, QWORD PTR [rcx+8184] mulsd xmm1, QWORD PTR [rax+8] addsd xmm2, xmm0 add rax, 24 add rcx, 24552 ; 00005fe8H addsd xmm2, xmm1 sub rdx, 1 jne SHORT $LL28@main Объяснений не даю, сам хочу услышать :) P.S. Вносим единственное изменение: double m1024_1[1025][1025], m1024_2[1025][1025], m1024_3[1025][1025]; double m1025_1[1025][1025], m1025_2[1025][1025], m1025_3[1025][1025]; double m1023_1[1025][1025], m1023_2[1025][1025], m1023_3[1025][1025]; Имеем: 1023: 1973593 1024: 1983533 1025: 1981114

Ответ 2



Решил и свою лепту внести на java. 3615298956 4363341525 4608966672 Новые ответы считает Считало где-то по 10 минут /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package time; /** * * @author milan */ public class Time { /** * @param args the command line arguments */ public static void main(String[] args) { System.out.println(i1023()); System.out.println(i1024()); System.out.println(i1025()); } public static long i1023() { int mas1023_1[][] = new int[1023][1023]; int mas1023_2[][] = new int[1023][1023]; long ans[] = new long[10]; long time1023 = 0; for (int k = 0; k < 10; k++) { long start; long finish; int[][] res = new int[1023][1023]; start = System.nanoTime(); for (int i = 0; i < 1023; i++) { for (int j = 0; j < 1023; j++) { for (int l = 0; l < 1023; l++) { res[i][j] += mas1023_1[i][l] * mas1023_2[l][j]; } } } finish = System.nanoTime(); time1023 = finish - start; //System.out.println(time1023); ans[k] = time1023; } long sum = 0; for (int i = 0; i < 10; i++) { sum += ans[i]; } sum = sum / 10; return sum; } public static long i1024() { int mas1024_1[][] = new int[1024][1024]; int mas1024_2[][] = new int[1024][1024]; long ans[] = new long[10]; long time1024 = 0; for (int k = 0; k < 10; k++) { long start; long finish; int[][] res = new int[1024][1024]; start = System.nanoTime(); for (int i = 0; i < 1024; i++) { for (int j = 0; j < 1024; j++) { for (int l = 0; l < 1024; l++) { res[i][j] += mas1024_1[i][l] * mas1024_2[l][j]; } } } finish = System.nanoTime(); time1024 = finish - start; //System.out.println(time1024); ans[k] = time1024; } long sum = 0; for (int i = 0; i < 10; i++) { sum += ans[i]; } sum = sum / 10; return sum; } public static long i1025() { int mas1025_1[][] = new int[1025][1025]; int mas1025_2[][] = new int[1025][1025]; long ans[] = new long[10]; long time1025 = 0; for (int k = 0; k < 10; k++) { long start; long finish; int[][] res = new int[1025][1025]; start = System.nanoTime(); for (int i = 0; i < 1025; i++) { for (int j = 0; j < 1025; j++) { for (int l = 0; l < 1025; l++) { res[i][j] += mas1025_1[i][l] * mas1025_2[l][j]; } } } finish = System.nanoTime(); time1025 = finish - start; //System.out.println(time1025); ans[k] = time1025; } long sum = 0; for (int i = 0; i < 10; i++) { sum += ans[i]; } sum = sum / 10; return sum; } }

воскресенье, 8 декабря 2019 г.

Blade Cache in Laravel 5+

#php #laravel #кэширование


При обновлении шаблонов Blade приходится долго ждать изменений  по причине кеширования.
Отключать кеширование в php.ini не вариант. Нашел на просторах решение  с прописанем
middleware:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Contracts\Routing\Middleware;

class ClearCache implements Middleware {

    /**
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $cachedViewsDirectory = app('path.storage').'/framework/views/';
        if ($handle = opendir($cachedViewsDirectory)) {
            while (false !== ($entry = readdir($handle))) {
                if(strstr($entry, '.')) continue;
                @unlink($cachedViewsDirectory . $entry);
            }
            closedir($handle);
        }
        return $next($request);
    }
}


но при загрузке страницы blade говорит что не нашел последний кеш файл и попросту
выбивает ошибку. Есть ли вариант уменьшить время или убрать вовсе.
    


Ответы

Ответ 1



Для Laravel 5.0 нужно установить http://packalyst.com/packages/package/kyslik/view-clear В Laravel 5.1+ уже идёт в комплекте. namespace App\Http\Middleware; use Closure; use Illuminate\Contracts\Routing\Middleware; use Illuminate\Support\Facades\Artisan; class ClearCache implements Middleware { /** * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { Artisan::call('view:clear'); return $next($request); } }

вторник, 16 июля 2019 г.

Как указать браузеру, что это уже другой PHP скрипт?

Я хочу ограничить количество одновременных обработок запросов в PHP до одной или двух с одного IP адреса. Под обработкой подразумевается обработка, к примеру, прогой Imagick (ImageMagick для PHP), например, сильное размытие (blur) большой картинки, что занимает достаточно много времени и, следовательно, сильно грузит процессор и приводит его в полный стопор.
Написал простой php скрипт с записью IP пользователя в Mysql. И вот что он делает:
Получение IP пользователя Проверка, есть ли он в базе Если есть больше двух таких же IP, то показать посетителю сообщение, что запускать две обработки одновременно нельзя. Остановить дальнейшее выполнение php-скрипта. Если такого IP нет - записать его в БД Обработать картинку, и пусть обработка будет длится 25 секунд, т.е. заменил функцией sleep() (задержка в секундах) После обработки (не важно успешной или нет) удалить IP из БД Всё, отключиться от Mysql.
Объясняю суть проблемы: если создать в браузере 3 вкладки (оганичение-то не более двух одновременно в PHP) с адресом текущего скрипта, просто вставив его из буфера обмена и вручную запустить их поочерёдно (то есть просто запустить копии этого php-скрипта), то каждый скрипт почему-то ждёт предыдущего, пока он не завершится, а в БД после их запуска добавляется только одна запись и лишь через 25 секунд добавляются остальные! Выходит, что можно запустить хоть 20 обработок и они не будут ограничены!
Если в базу заранее добавить 10 таких одинаковых ip, то скрипт моментально показывает, что нельзя производить две одновременных обработки, то есть работает как надо, если в БД уже есть два или больше двух одинаковых IP.
Если этот скрипт запускать в разных браузерах, то тоже работает нормально - две одинаковых записи в базу данных добавляются практически моментально после запуска. А вот в одном браузере не получается - каждый последний запущенный скрипт как-бы ждёт завершения предыдущего и записи IP в БД добавляются только через 25 секунд.
Если назвать копии этого скрипта по-разному, например: script1.php, script2.php и script3.php а затем запустить их также сразу вручную и в одном и том же браузере, то тоже работает правильно - записи в БД добавляются как раз после того, как я их запустил, а не через 25 сек и без ожидания завершения предыдущего (выходит + 1-3 секунды, в зависимости как быстро их вручную запускать по очереди).
У меня только есть подозрение, что где-то надо сбрасывать какую-то сессию или что-то в этом роде, так как несколько запущенных копий скрипта в одном браузере не ограничиваются, а если запустить в разных браузерах - всё работает правильно!
Что может мешать добавлять сразу два ip в БД после одновременного запуска двух копий одного и того же скрипта в браузере? (вместо этого IP из 1-го скрипта добавляется как надо сразу, а 2-й IP из второго скрипта только тогда, когда завершается 1-й скрипт)


Ответ

Все правильно, дело в сессии.
PHP не начнет обрабатывать новый запрос, если не завершена работа с сессией предыдщуего, т.к. файл сессии заблокирован первым запросом. Это сделано для того, чтобы два обработчика не писали одновременно в один и тот же файл.
Чтобы следующий запрос стал обрататываться при еще не завершившейся обработке предыдущего, требуется в первом запросе закрыть файл сессии с помощью функции session_write_close(). В Вашем случае это надо сделать перед запуском обработки изображения. Разумеется, после вызова этой функции Вы уже не сможете ничего записать в сессию в текущем обработчике.

четверг, 11 июля 2019 г.

Google PageSpeed кеширование сторонних JS скриптов

Здравствуйте. Google PageSpeed рекомендует кешировать скрипты
http://connect.facebook.net/az_AZ/sdk.js (20 минут) http://apis.google.com/js/client:plusone.js?onload=render (30 минут) https://apis.google.com/js/api.js (30 минут) http://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js (60 минут) https://oauth.googleusercontent.com/…e:rpc:shindig.random:shindig.sha1.js?c=2 (60 минут) https://pagead2.googlesyndication.com/pagead/osd.js (60 минут) http://www.google-analytics.com/analytics.js (2 часа)
Они располагаются на сторонних серверах. Как это сделать?


Ответ

Ничего делать не надо. Сами сервера отдают эти файлы с указанием времени кэширования. Пример: