Data Cache
Понимание основных принципов Data Cache и способов ускорения запросов данных с помощью Data Cache.
Data Cache используется для кэширования данных нативных таблиц и внешних таблиц. Эта функция включена по умолчанию начиная с версии v1.5.2. Начиная с версии v2.0.0, кэш в памяти и дисковый кэш были объединены в единую систему Data Cache для удобства управления.
Data Cache состоит из двух компонентов: Page Cache (кэш в памяти) и Block Cache (дисковый кэш).
Принципы работы Page Cache
Как кэш в памяти, Page Cache отвечает за хранение страниц данных нативных и внешних таблиц после декомпрессии. Размер этих страниц не фиксирован. В настоящее время Page Cache поддерживает кэширование следующих типов данных:
- Страницы данных и индексные страницы нативных таблиц
- Информация footer файлов данных внешних таблиц
- Частично декомпрессированные страницы данных внешних таблиц
Page Cache в настоящее время использует стратегию LRU (Least Recently Used) для вытеснения данных.
Принципы работы Block Cache
Block Cache — это дисковый кэш, основная функция которого заключается в кэшировании файлов данных (из внешних таблиц, а также из cloud-native таблиц в shared-data cluster) на локальные диски. Это снижает задержку доступа к удалённым данным и повышает эффективность запросов. Размер каждого блока данных фиксирован.
Предпосылки и ценность
В сценариях аналитики Data Lake и cloud-native таблиц Selena, выступая в роли OLAP-движка запросов, должна сканировать файлы данных, хранящиеся в HDFS или объектном хранилище (далее — «удалённая система хранения»). Этот процесс сталкивается с двумя основными узкими местами производительности:
- Чем больше файлов нужно прочитать запросу, тем больше накладные расходы на удалённый I/O.
- В сценариях ad-hoc запросов частый доступ к одним и тем же данным приводит к избыточному потреблению удалённого I/O.
Для решения этих проблем в версии v1.5.2 была введена функция Block Cache. Она разделяет исходные данные из удалённой системы хранения на несколько блоков по определённой стратегии и кэширует эти блоки на локальных дисках узлов BE или CN. За счёт избежания повторного получения удалённых данных значительно повышается производительность запросов к горячим данным.
Сценарии использования
- Запросы к данным из удалённых систем хранения с использованием внешних catalog (к роме JDBC Catalog).
- Запросы к cloud-native таблицам в shared-data cluster.
Основной механизм
Разбиение данных и единица кэширования
Когда система кэширует удалённые файлы, она разбивает исходные файлы на блоки равного размера в соответствии с настроенной стратегией. Блок — это минимальная единица кэширования, и его размер настраивается.
Пример:
Если размер блока настроен как 1 МБ, при запросе файла Parquet размером 128 МБ на Amazon S3 файл будет разбит на 128 последовательных блоков (то есть [0, 1 МБ), [1 МБ, 2 МБ), ..., [127 МБ, 128 МБ)).
Каждому блоку присваивается глобально уникальный идентификатор кэша (cache key), который состоит из следующих трёх частей:
hash(filename) + fileModificationTime + blockId
| Компонент | Описание |
|---|---|
| filename | Имя файла данных. |
| fileModificationTime | Время последнего изменения файла данных. |
| blockId | ID, присвоенный каждому блоку при разбиении файла данных. Этот ID уникален в пределах одного файла, но не глобально уникален. |
Попадание в кэш и процесс чтения
Предположим, запрос попадает в блок в диапазоне [1 МБ, 2 МБ), Block Cache действует следующим образом:
- Система сначала проверяет, существует ли блок в Block Cache локального узла BE (путём сопоставления cache key).
- Если найден (попадание в кэш), блок читается напр ямую с локального диска.
- Если не найден (промах кэша), блок извлекается из удалённого хранилища и синхронизируется в Block Cache локального узла BE для повторного использования в последующих запросах.
Среда хранения кэша
Block Cache использует локальные диски узлов BE или CN в качестве среды хранения, и эффект ускорения кэша напрямую связан с производительностью диска:
- Рекомендуется использовать высокопроизводительные локальные диски (например, NVMe-диски) для минимизации задержки чтения/записи кэша.
- Если производительность диска неоптимальна, вы можете увеличить количество дисков для достижения балансировки нагрузки и снижения нагрузки I/O на отдельные диски.
Политики замены кэша
Block Cache поддерживает две стратег ии кэширования и вытеснения данных: LRU и SLRU (Segmented LRU).
LRU
Стратегия LRU основана на принципе «Наименее недавно использованный» — вытесняются блоки, к которым не обращались дольше всего. Она проста в реализации и подходит для сценариев со стабильными паттернами доступа.
SLRU
Стратегия SLRU делит пространство кэша на сегмент вытеснения и сегмент защиты, оба следуют правилам LRU:
- Данные попадают в сегмент вытеснения при первом доступе.
- Данные в сегменте вытеснения перемещаются в сегмент защиты при повторном доступе.
- Данные, вытесненные из сегмента защиты, возвращаются в сегмент вытеснения, в то время как данные, вытесненные из сегмента вытеснения, полностью удаляются из кэша.
Стратегия SLRU может эффективно противостоять внезапному разреженному трафику, предотвращая прямое вытеснение горячих данных в сегменте защиты «временными данными, к которым обращались только один раз». Она обеспечивает лучшую стабильность, чем LRU.
Включение и настройка Data Cache
Data Cache включён по умолчанию, управляется параметром конфигурации BE datacache_enable (по умолчанию: true). Page Cache и Block Cache, как два независимых компонента, также включены по умолчанию. Установка datacache_enable в false отключит Data Cache целиком, то есть и Page Cache, и Block Cache.
Вы также можете активировать или деактивировать Page Cache и Block Cache по отдельности, используя различные параметры конфигурации BE.
- Page Cache (включён по умолчанию) управляется параметром
disable_storage_page_cache(по умолчанию:false). - Block Cache (включён по умолчанию) управляется параметром
block_cache_enable(по умолчанию:true).
Вы можете дополнительно использовать следующие конфигурации BE для установки максимальных лимитов использования памяти и диска для Data Cache, предотвращая чрезмерное потребление ресурс ов:
datacache_mem_size: Устанавливает максимальный лимит использования памяти для Data Cache (используется для хранения данных в Page Cache).datacache_disk_size: Устанавливает максимальный лимит использования диска для Data Cache (используется для хранения данных в Block Cache).
Заполнение Block Cache
Правила заполнения
Начиная с версии v1.5.2, для повышения коэффициента попадания в Block Cache система заполняет Block Cache в соответствии со следующими правилами:
- Кэш не будет заполняться для операторов, отличных от
SELECT, например,ANALYZE TABLEиINSERT INTO SELECT. - Запросы, сканирующие все partition таблицы, не будут заполнять кэш. Однако, если таблица имеет только один partition, заполнение выполняется по умолчанию.
- Запросы, сканирующие все столбцы таблицы, не будут заполнять кэш. Однако, если таблица имеет только один столбец, заполнение выполняется по умолчанию.
- Кэш не будет заполняться для таблиц, которые не являются Hive, Paimon, Delta Lake, Hudi или Iceberg.
Просмотр поведения заполнения кэша
Вы можете просмотреть поведение заполнения для конкретного запроса с помощью команды EXPLAIN VERBOSE.
Пример:
mysql> EXPLAIN VERBOSE SELECT col1 FROM hudi_table;
...
| 0:HudiScanNode |
| TABLE: hudi_table |
| partitions=3/3 |
| cardinality=9084 |
| avgRowSize=2.0 |
| dataCacheOptions={populate: false} |
| cardinality: 9084 |
+-----------------------------------------+
dataCacheOptions={populate: false} указывает, что кэш не будет заполняться, поскольку запрос будет сканировать все partition.
Вы также можете точно настроить поведение заполнения Block Cache через сессионную переменную populate_datacache_mode.
Режим заполнения
Block Cache поддерживает два режима: синхронное заполнение и асинхронное заполнение. Вы можете выбирать между ними в зависимости от бизнес-требований к «производительности первого запроса» и «эффективности кэша».
Синхронное заполнение
- Основная логика: Когда удалённые данные читаются для первого запроса, данные немедленно кэшируются локально. Последующие запросы могут напрямую использовать кэш.
- Преимущества: Высокая эффективность кэша, поскольку кэширование данных завершается за один запрос.
- Недостатки: Операции кэширования выполняются синхронно с операциями чтения, что может увеличить задержку для первого запроса.
Асинхронное заполнение
- Основная логика: Для первого запроса чтение данных имеет приоритет и выполняется первым. Запись в кэш выполняется асинхронно в фоновом режиме, не блокируя текущий процесс запроса.
- Преимущества: Не влияет на производительность первого запроса и предотвращает задержку операций чтения из-за кэширования.
- Недостатки: Более низкая эффективность кэша, поскольку один запрос может не полностью кэшировать все данные, к которым обращались. Требуется несколько запросов для постепенного улучшения покрытия кэша.
Начиная с версии v1.5.2, асинхронное заполнение кэша включено по умолчанию. Вы можете изменить режим заполнения, установив сессионную переменную enable_datacache_async_populate_mode.
Персистентность
Кэшированные данные на дисках по умолчанию являются персистентными, и эти данные могут быть повторно использованы после перезапуска BE или CN.
Проверка попадания запроса в Data Cache
Вы можете проверить, попал ли запрос в Data Cache, анализируя следующие метрики в профиле запроса:
DataCacheReadBytes: размер данных, которые система читает напрямую из памяти и с дисков.DataCacheWriteBytes: размер данных, загруженных из удалённой системы хранения в память и на диски.BytesRead: общий объём прочитанных данных, включая данные, которые система читает из удалённой системы хранения, а также из памяти и с дисков.
Пример 1: В этом примере система читает большой объём данных (7,65 ГБ) из удалённой системы хранения и лишь немного данных (518,73 МБ) с дисков. Это означает, что попадание в Block Cache было минимальным.
- Table: lineorder
- DataCacheReadBytes: 518.73 MB
- __MAX_OF_DataCacheReadBytes: 4.73 MB
- __MIN_OF_DataCacheReadBytes: 16.00 KB
- DataCacheReadCounter: 684
- __MAX_OF_DataCacheReadCounter: 4
- __MIN_OF_DataCacheReadCounter: 0
- DataCacheReadTimer: 737.357us
- DataCacheWriteBytes: 7.65 GB
- __MAX_OF_DataCacheWriteBytes: 64.39 MB
- __MIN_OF_DataCacheWriteBytes: 0.00
- DataCacheWriteCounter: 7.887K (7887)
- __MAX_OF_DataCacheWriteCounter: 65
- __MIN_OF_DataCacheWriteCounter: 0
- DataCacheWriteTimer: 23.467ms
- __MAX_OF_DataCacheWriteTimer: 62.280ms
- __MIN_OF_DataCacheWriteTimer: 0ns
- BufferUnplugCount: 15
- __MAX_OF_BufferUnplugCount: 2
- __MIN_OF_BufferUnplugCount: 0
- BytesRead: 7.65 GB
- __MAX_OF_BytesRead: 64.39 MB
- __MIN_OF_BytesRead: 0.00
Пример 2: В этом примере система читает большой объём данных (46,08 ГБ) из Data Cache и не читает данные из удалённой системы хранения, что означает, что данные читаются только из Block Cache.
Table: lineitem
- DataCacheReadBytes: 46.08 GB
- __MAX_OF_DataCacheReadBytes: 194.99 MB
- __MIN_OF_DataCacheReadBytes: 81.25 MB
- DataCacheReadCounter: 72.237K (72237)
- __MAX_OF_DataCacheReadCounter: 299
- __MIN_OF_DataCacheReadCounter: 118
- DataCacheReadTimer: 856.481ms
- __MAX_OF_DataCacheReadTimer: 1s547ms
- __MIN_OF_DataCacheReadTimer: 261.824ms
- DataCacheWriteBytes: 0.00
- DataCacheWriteCounter: 0
- DataCacheWriteTimer: 0ns
- BufferUnplugCount: 1.231K (1231)
- __MAX_OF_BufferUnplugCount: 81
- __MIN_OF_BufferUnplugCount: 35
- BytesRead: 46.08 GB
- __MAX_OF_BytesRead: 194.99 MB
- __MIN_OF_BytesRead: 81.25 MB
I/O Adaptor
Для предотвращения значительной хвостовой задержки при доступе к диску из-за высокой нагрузки I/O на диск кэша, что может привести к негативной оптимизации системы кэширования, Data Cache предоставляет функцию I/O Adaptor. Эта функция направляет некоторые запросы кэша в удалённое хранилище при высокой нагрузке на диск, используя как локальный кэш, так и удалённое хранилище для повышения пропускной способности I/O. Эта функция включена по умолчанию.
Вы можете включить I/O Adaptor, установив следующую системную переменную:
SET GLOBAL enable_datacache_io_adaptor=true;
Динамическое масштабирование
Data Cache поддерживает ручную настройку ёмкости кэша без перезапуска процесса BE, а также поддерживает автоматическую настройку ёмкости кэша.
Ручное масштабирование
Вы можете изменить лимит памяти или дисковую ёмкость Data Cache, динамически настраивая параметры конфигурации BE.
Примеры:
-- Настройка лимита памяти Data Cache для конкретного экземпляра BE.
UPDATE be_configs SET VALUE="10G" WHERE NAME="datacache_mem_size" and BE_ID=10005;
-- Настройка лимита соотношения памяти Data Cache для всех экземпляров BE.
UPDATE be_configs SET VALUE="10%" WHERE NAME="datacache_mem_size";
-- Настройка дискового лимита Data Cache для всех экземпляров BE.
UPDATE be_configs SET VALUE="2T" WHERE NAME="datacache_disk_size";
- Будьте осторожны при настройке ёмкостей таким способом. Убедитесь, что вы не пропустили предложение WHERE, чтобы избежать изменения несвязанных параметров конфигурации.
- Настройки ёмкости кэша, сделанные таким образом, не будут сохранены и будут потеряны после перезапуска процесса BE или CN. Поэтому вы можете сначала динамически настроить параметры, как описано выше, а затем вручную изменить файл конфигурации BE или CN, чтобы гарантировать, что изменения вступят в силу после следующего перезапуска.
Автоматическое масштабирование
Selena в настоящее время поддерживает автоматическое масштабирование дисковой ёмкости. Если вы не указываете путь к диску кэша и лимит ёмкости в конфигурации BE, автоматическое масштабирование включается по умолчанию.
Вы также можете включить автоматическое масштабирование, добавив следующий параметр конфигурации в файл конфигурации и перезапустив процесс BE или CN:
datacache_auto_adjust_enable=true
После включения автоматического масштабирования:
- Когда использование диска превышает порог, указанный в конфигурации BE
disk_high_level(значение по умолчанию90, то есть 90% дискового пространства), система автоматически вытесняет данные кэша для освобождения дискового пространства. - Когда использование диска постоянно ниже порога, указанного в конфигурации BE
disk_low_level(значение по умолчанию60, то есть 60% дискового пространства), и текущее дисковое пространство, используемое Data Cache, заполнено, система автоматически расширяет ёмкость кэша. - При автоматическом масштабировании ёмкости кэша система будет стремиться настроить ёмкость кэша до уровня, указанного в конфигурации BE
disk_safe_level(значение по умолчанию80, то есть 80% дискового пространства).
Совместное использование кэша (Cache Sharing)
Поскольку Data Cache зависит от локального диска узла BE, при масштабировании cluster изменения в маршрутизации данных могут вызвать промахи кэша, что легко может привести к значительному снижению производительности во время эластичного масштабирования.
Cache Sharing используется для поддержки доступа к данным кэша между узлами через сеть. Во время масштабирования cluster, если происходит промах локального кэша, система сначала пытается получить данные кэша с других узлов в пределах того же cluster. Только если все кэши промахнулись, система повторно получит данные из удалённого хранилища. Эта функция эффективно снижает колебания производительности, вызванные инвалидацией кэша во время эластичного масштабирования, и обеспечивает стабильную производительность запросов.

Вы можете включить функцию Cache Sharing, настроив следующие два параметра:
- Установите параметр конфигурации FE
enable_trace_historical_nodeвtrue. - Установите системную переменную
enable_datacache_sharingвtrue.
Кроме того, вы можете проверить следующие метрики в профиле запроса для мониторинга Cache Sharing:
DataCacheReadPeerCounter: Количество чтений с других узлов.DataCacheReadPeerBytes: Количество байтов, прочитанных с других узлов.DataCacheReadPeerTimer: Время, затраченное на доступ к данным кэша с других узлов.
Конфигурации и переменные
Вы можете настроить Data Cache, используя следующие системные переменные и параметры.
Системные переменные
- populate_datacache_mode
- enable_datacache_io_adaptor
- enable_file_metacache
- enable_datacache_async_populate_mode
- enable_datacache_sharing
Конфигурации FE
Конфигурации BE
- datacache_enable
- datacache_mem_size
- datacache_disk_size
- datacache_auto_adjust_enable
- datacache_disk_high_level
- datacache_disk_safe_level
- datacache_disk_low_level
- datacache_disk_adjust_interval_seconds
- datacache_disk_idle_seconds_for_expansion
- datacache_min_disk_quota_for_adjustment
- datacache_eviction_policy
- datacache_inline_item_count_limit