Статья

Рекомендации по составлению запросов в StarRocks

2025-10-06 13:34
Продолжаем серию статей с подробным рассмотрением каждого из аспектов работы администратора.
Мы выделили пять важнейших аспектов работы администратора Селены: развертывание, моделирование данных, прием данных, выполнение запросов и мониторинг.
В предыдущей статье мы поделились советами о загрузке данных в Селену. В этой мы рассмотрим запросы.
Выполнение запросов в Селене
Составление запросов — один из важнейших навыков работы с Селеной на базе механизма StarRocks. Запросы бывают разными, и у каждого сценария есть свои нюансы. Поделимся ключевыми рекомендациями по написанию запросов в Селене.

Высокий уровень параллелизма

  • Рекомендуется: применяйте обрезку партиций и бакетов, как описано в разделах о партиционировании и бакетировании.
  • Обязательно: увеличьте лимит параллельных потоков для пользователя; по умолчанию — 100. Чтобы задать 1000: SET PROPERTY FOR 'jack' 'max_user_connections' = '1000';.
  • Обязательно: включите кэш страниц и кэш запросов.

Точность данных

  • Обязательно: для получения точных результатов используйте тип DECIMAL вместо FLOAT и DOUBLE.

Выполнение SQL-запросов

  • Обязательно: избегайте инструкции SELECT *; указывайте только нужные столбцы, например SELECT col0, col1 FROM tb1.
  • Обязательно: избегайте полного сканирования таблицы — используйте фильтры: SELECT col0, col1 FROM tb1 WHERE id=123, SELECT col0, col1 FROM tb1 WHERE dt>'2024-01-01'.
  • Обязательно: для сокращения объема данных при передаче используйте пагинацию: SELECT col0, col1, col2, ..., col50 FROM tb ORDER BY id LIMIT 0, 50000 позволяет сократить результаты, возвращенные одним запросом.
  • Обязательно: при пагинации всегда включайте в запрос ORDER BY, иначе порядок результатов не гарантируется.
  • Рекомендуется: избегайте лишних функций и выражений в предикатах.
Предикаты, содержащие cast, можно удалить.
-- Запрос 1: неэффективный
-- Запрос 1: неэффективный
select l_tax 
from lineitem 
where cast(l_shipdate as varchar) > substr('1990-01-02 12:30:31',1,10);

-- Запрос 2: эффективный
select l_tax 
from lineitem 
where l_shipdate > '1990-01-02';
Лишние функции в выражениях
-- Запрос 1: неэффективный
select count(1) 
from lineitem 
where l_shipdate >= regexp_extract("TIME:1996-01-02 20:00:00", "(\\d{4}-\\d{2}-\\d{2})", 1);

-- Запрос 2: эффективный
select count(1) 
from lineitem 
where l_shipdate >= "1996-01-02"
-- Запрос 1: неэффективный
select count(1) 
from lineitem 
where DATE_FORMAT(l_shipdate,'%Y-%m-%d') >= "1996-01-02"

-- Запрос 2: эффективный
select count(1) 
from lineitem 
where l_shipdate >= "1996-01-02"

Соединение (JOIN)

  • Обязательно: соединяемые поля должны содержать данные одного типа. Селена, конечно, выполняет неявные преобразования, но старайтесь следить за типами полей, чтобы избежать возможных неточностей. Особенно рискованно соединять типы FLOAT и DOUBLE.
  • Обязательно: избегайте функций и выражений в запросах JOIN, например JOIN ON DATE_FORMAT(tb1.col1,'%Y-%m-%d')=DATE_FORMAT(tb2.col1,'%Y-%m-%d').
  • Обязательно: если вы обращайтесь к нескольким таблицам, каждая из которых содержит более 10 млн строк, используйте функцию Colocate Join в StarRocks, чтобы не перераспределять данные между партициями.
  • Рекомендуется: избегайте декартовых произведений.
o В запросах к нескольким таблицам явно указывайте условия соединения.
-- Неэффективный запрос
SELECT *
FROM table1, table2;

-- Эффективный запрос
SELECT *
FROM table1, table2 ON table1.column1 = table2.column1;
Связь в подзапросах
  • Во внешнем запросе и подзапросе должна быть четко указана связь между столбцами.
-- Неэффективный запрос
SELECT *
FROM table1
WHERE column1 IN (SELECT column2 FROM table2);

-- Эффективный запрос
SELECT *
FROM table1
WHERE column1 IN (SELECT column2 FROM table2 WHERE condition);

Материализованные представления для ускорения запросов

Корректная дедупликация с помощью синхронного материализованного представления:
o Для примера возьмем таблицу фактов с данными о рекламе advertiser_view_record, в которой записано время клика click_time, код рекламодателя advertiser, канал рекламы channel и идентификатор пользователя user_id.
CREATE TABLE advertiser_view_record(
click_time DATE,
advertiser VARCHAR(10),
channel VARCHAR(10),
user_id INT) DISTRIBUTED BY hash(click_time);
В этом сценарии часто используется следующий запрос для просмотра количества уникальных посетителей, нажавших на рекламное объявление.
SELECT advertiser, channel, count(distinct user_id) FROM advertiser_view_record GROUP BY advertiser, channel;
Для дедупликации и ускорения запросов можно создать материализованное представление, заранее объединив данные с помощью bitmap_union().
CREATE MATERIALIZED VIEW advertiser_uv AS SELECT advertiser, channel, bitmap_union(to_bitmap(user_id)) FROM advertiser_view_record GROUP BY advertiser, channel;
После создания материализованного представления последующие запросы, включающие такие подзапросы, как count(distinct user_id), будут автоматически переписаны под материализованное представление: bitmap_union_count(to_bitmap(user_id)).
Асинхронное материализованное представление поддерживает до трех вложенных уровней.

Ускорение запросов с помощью кэша

  • Рекомендуется: включите кэширование страниц, чтобы ускорить сканирование данных; если памяти достаточно, повысьте лимит — по умолчанию 20% от mem_limit.
  • Рекомендуется: кэшируйте запросы, чтобы ускорить агрегирование данных при обращении к одной таблице или нескольким.
  • Рекомендуется: кэшируйте данные при разделении вычислительных ресурсов и хранилища и при аналитических запросах к Data Lake.
В этой статье мы дали только базовые рекомендаций. В заключительной статье серии мы расскажем о мониторинге в Селене.