Кэширование тайлов

В тайловых веб-картах основной объём данных поступает в виде растровых или векторных тайлов, загружаемых по сетке в соответствии с текущим экстентом и уровнем масштабирования. В OpenLayers система кэширования тайлов реализована на нескольких уровнях: браузерный HTTP-кэш, внутренний кэш источников данных, кэш загрузчика тайлов и дополнительные механизмы, связанные с пользовательской логикой и Service Worker.

Кэширование напрямую влияет на производительность, плавность панорамирования и масштабирования, а также на сетевую нагрузку. При корректной настройке повторное использование тайлов позволяет сократить количество запросов к серверу на порядки.


Модель тайлового источника и базовый кэш

Каждый тайловый слой в OpenLayers строится на основе объекта ol.source.Tile. Наиболее распространённые реализации — ol.source.XYZ, ol.source.OSM, ol.source.TileWMS.

Внутри каждого источника существует встроенный кэш тайлов, реализованный через структуру хранения загруженных изображений и метаданных.

Поведение кэша по умолчанию

При загрузке тайла происходит проверка:

  • существует ли тайл в памяти источника;
  • не устарел ли он согласно параметрам версии;
  • не превышен ли лимит кэша.

Если тайл уже загружен, повторный запрос не выполняется. Это особенно заметно при возврате к ранее просмотренным областям карты.


Кэш в ol.source.XYZ

Наиболее типичный случай — тайлы с URL-шаблоном:

const source = new ol.source.XYZ({
  url: 'https://tile-server/{z}/{x}/{y}.png'
});

Особенности кэширования:

  • тайлы индексируются по ключу {z}/{x}/{y};
  • одинаковые координаты переиспользуют уже загруженные изображения;
  • кэш живёт в пределах экземпляра источника;
  • при уничтожении слоя кэш освобождается.

Ограничения:

  • кэш не является глобальным между слоями;
  • при обновлении стиля слоя кэш не инвалидируется автоматически;
  • браузерный HTTP-кэш может конфликтовать с внутренним кэшем.

Управление размером кэша

Векторные и тайловые источники поддерживают настройку размера кэша через параметр cacheSize (или через внутренние механизмы tileCache).

Пример:

const source = new ol.source.XYZ({
  url: 'https://tile-server/{z}/{x}/{y}.png',
  cacheSize: 2048
});

Поведение при переполнении:

  • старые тайлы удаляются по LRU-стратегии (Least Recently Used);
  • часто используемые области остаются в памяти;
  • резкое перемещение по карте может вызвать повторные загрузки.

HTTP-кэш и заголовки сервера

На уровне сети тайлы кэшируются браузером в соответствии с HTTP-заголовками:

  • Cache-Control
  • ETag
  • Last-Modified

Корректная серверная конфигурация позволяет добиться значительного ускорения:

Cache-Control

Cache-Control: public, max-age=31536000, immutable

Такой режим подходит для тайлов, которые не меняются во времени (статические карты).

ETag

Используется для проверки актуальности ресурса без повторной загрузки тела ответа.


Инвалидация кэша через URL версионирование

Одним из наиболее надёжных способов управления кэшом является добавление версии в URL:

const source = new ol.source.XYZ({
  url: 'https://tile-server/v2/{z}/{x}/{y}.png'
});

или через параметр:

url: 'https://tile-server/{z}/{x}/{y}.png?version=2026'

Эффект:

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

TileLoadFunction и ручное управление кэшем

OpenLayers позволяет переопределять процесс загрузки тайлов через tileLoadFunction.

const source = new ol.source.XYZ({
  tileLoadFunction: function (tile, src) {
    const image = tile.getImage();
    image.src = src;
  }
});

Возможности:

  • добавление кастомного кэш-ключа;
  • внедрение локального хранилища;
  • контроль повторных запросов;
  • интеграция с IndexedDB.

Кэширование через Service Worker

Service Worker позволяет вынести кэш тайлов за пределы жизненного цикла страницы.

Стратегия Cache First

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      return cached || fetch(event.request);
    })
  );
});

Применение:

  • оффлайн-карты;
  • уменьшение задержек при повторном просмотре;
  • снижение нагрузки на сервер тайлов.

Особенности:

  • требуется строгий контроль версий;
  • возможны конфликты с динамическими слоями;
  • необходимо учитывать объём кэша браузера.

Кэширование растровых и векторных тайлов

Растровые тайлы

  • кэшируются как изображения (PNG/JPEG/WebP);
  • легко переиспользуются браузером;
  • занимают значительный объём памяти.

Векторные тайлы (MVT)

  • кэшируются как бинарные данные;
  • требуют декодирования при каждом использовании;
  • часто используются совместно с стилями, влияющими на результат рендеринга.

Поведение кэша при масштабировании

При изменении zoom уровня:

  • происходит загрузка нового набора тайлов;
  • кэш предыдущих уровней сохраняется;
  • возможен preloading соседних уровней.

Параметры:

  • preload — загрузка тайлов соседних zoom уровней;
  • maxZoom / minZoom — ограничение диапазона кэшируемых данных.

TileGrid и влияние на кэш

ol.tilegrid.TileGrid определяет структуру координат тайлов.

const tileGrid = new ol.tilegrid.TileGrid({
  extent: [-180, -90, 180, 90],
  resolutions: [...]
});

Влияние на кэш:

  • разные grid → разные ключи тайлов;
  • изменение resolution сбрасывает повторное использование;
  • одинаковая сетка позволяет эффективно переиспользовать кэш.

Кэширование векторных слоёв с источниками XYZ и MVT

При работе с MVT:

  • тайлы кэшируются как protobuf-структуры;
  • декодирование происходит после получения из кэша;
  • стилизация не влияет на сам кэш тайла.

При повторной отрисовке слоя:

  • повторно используется бинарный тайл;
  • применяется новый стиль без повторной загрузки данных.

Локальное хранение и IndexedDB

Для расширенного кэширования применяются:

  • IndexedDB для хранения бинарных тайлов;
  • localStorage для метаданных и индексов.

Пример логики:

  • при загрузке тайла проверяется IndexedDB;
  • при отсутствии выполняется сетевой запрос;
  • результат сохраняется в локальную базу.

Ограничения:

  • необходимо управление размером базы;
  • требуется сериализация изображений или ArrayBuffer;
  • возможны задержки при чтении больших объёмов данных.

Очистка и жизненный цикл кэша

Кэш тайлов имеет ограниченный жизненный цикл:

  • при удалении слоя очищается локальный кэш источника;
  • при перезагрузке страницы сбрасывается memory cache;
  • Service Worker кэш живёт отдельно и требует явной очистки.

Стратегии очистки:

  • по времени жизни (TTL);
  • по версии данных;
  • по объёму хранилища.

Оптимизация повторного использования тайлов

Эффективное кэширование достигается через сочетание нескольких факторов:

  • стабильные URL тайлов;
  • минимизация динамических параметров;
  • корректный HTTP Cache-Control;
  • ограничение количества источников;
  • использование единого tileGrid.

Проблемные сценарии кэширования

Частая смена стиля

При изменении визуального стиля без изменения данных:

  • кэш тайлов не пересоздаётся;
  • визуальный результат может не соответствовать ожиданиям.

Динамические параметры в URL

url: 'https://server/{z}/{x}/{y}.png?time=' + Date.now()
  • полностью отключает кэш;
  • приводит к избыточным загрузкам.

Несогласованность слоёв

  • разные источники с одинаковыми тайлами не разделяют кэш;
  • дублирование памяти при многослойных картах.

Поведение при оффлайн-навигации

При использовании Service Worker и предварительном прогреве кэша:

  • ранее просмотренные области доступны без сети;
  • zoom-переходы ограничены доступными тайлами;
  • новые области требуют предварительной загрузки.

Итоговая модель кэширования

Система кэширования тайлов в OpenLayers формируется как многоуровневая структура:

  • память источника (Tile Source Cache);
  • браузерный HTTP-кэш;
  • Service Worker кэш;
  • локальные хранилища (IndexedDB);
  • серверные политики кэширования.

Эти уровни взаимодействуют между собой, формируя итоговую производительность отображения карты и устойчивость к сетевым задержкам.