--truststore-path /opt/presto/etc/presto-keystore.jks \
--truststore-password password \
--user clientC \
--password
Password:
presto> show catalogs;
Error running command: Authentication failed: Access Denied: Invalid credentials
Запуск консоли Presto
Введите в браузере адрес узла-координатора ( https://localhost:8443). Вы увидите диалоговое окно с приглашением ввести логин и пароль. Введите одно
из корректных имен, clientA или clientB, и соответствующий пароль, который вы создали на предыдущем шаге (см. рис. 7.2).


Аутентификация 139
Рис. 7.2 Диалог аутентификации в консоли Presto
LDAP
LDAP – это стандартный протокол аутентификации с использованием службы каталогов. Presto также поддерживает аутентификацию через LDAP. Схема
взаимодействия Presto со службой LDAP изображена на рис. 7-3. Клиент при
соединении с узлом-координатором указывает логин и пароль, а затем координатор проверяет предоставленные учетные данные с помощью внешней
службы. Развертывание LDAP-каталога выходит за рамки данной книги. Для
тестирования вы можете воспользоваться, например, пакетом FreeIPA1.
LDAPкаталог
Клиент Presto A
Узелкоординатор
Рабочий узел
Каталоги
Клиент Presto B
Доверенное хранилище данных
Рис. 7.3 Интеграция Presto и LDAP
1
140 Безопасность Presto
После того как служба LDAP сконфигурирована и запущена, настройте
координатор Presto для использования LDAP-каталога. Для этого добавьте
в файл password-authenticator.properties следующие строки: password-authenticator.name=ldap
ldap.url=ldaps://<url_to_the_ldap_service>:636
ldap.user-ldap.user-bind-pattern=<your_bind_pattern>
Например, в качестве LDAP-каталога вы используете Active Directory, шаблон для построения имени пользователя будет выглядеть так:
ldap.user-ldap.user-bind-pattern=${USER}@<your domain name>
Когда Presto будет обращаться к каталогу, вместо переменной ${USER} будет
подставлено имя пользователя, введенное при попытке аутентификации.
Еще один вариант настройки – определить идентификатор пользователя так: ldap.user-ldap.user-bind-pattern=
+uid=${USER},cn=users,cn=accounts,dc=ipa,dc=test
Чтобы начать использовать LDAP-каталог, требуется перезапуск узла-координатора.
Kerberos
Протокол Kerberos обеспечивает взаимную аутентификацию между клиентом и сервером с помощью системы квитанций ( tickets), удостоверяющих
предъявителя. Presto поддерживает как внешнюю, так и внутреннюю аутентификацию с использованием Kerberos. Внешней называется аутентификация клиента (точнее, взаимная аутентификация клиента и узла-координатора), а внутренней – взаимная аутентификация координатора и рабочих
узлов. Установка сервера Kerberos выходит за рамки данной книги, поэтому
мы предполагаем, что служба Kerberos у вас уже настроена и запущена.
Kerberos и LDAP
Оба протокола, и LDAP, и Kerberos, используются для аутентификации. Но устрой-ство этих протоколов и цели их создания – разные. Главной целью Kerberos было
обеспечение универсального удостоверения для множества сервисов, SSO (single sign-on), и он решает задачу безопасной передачи данных аутентификации в недо-веренной среде. LDAP же обеспечивает доступ к каталогу, где хранится информация
о пользователях, узлах сети, сервисах и других объектах и субъектах сетевого взаимодействия.
Условия функционирования Kerberos
На рис. 7.4 показана схема взаимодействия Presto со службой Kerberos. На
всех узлах, участвующих в процессе аутентификации, должен быть настроен
доступ к серверу Kerberos. Настройки состоят из двух файлов – keytab и etc/

Аутентификация 141
krb5.conf. В файле keytab хранятся пары из зашифрованных ключей и соответствующих им субъектов аутентификации – пользователей или узлов. Файл
etc/krb5.conf содержит настройки доменной аутентификации, шифрования
и т. д.
Сервер
аутентификации
Kerberos
keytab
keytab
keytab
/etc/krb5.conf
/etc/krb5.conf
/etc/krb5.conf
Узелкоординатор
Клиент Presto
C подключением
Рабочий узел
клиента Kerberos
Доверенное хранилище данных
Рис. 7.4 Интеграция хранилища на базе Presto со службой Kerberos В файле /etc/krb5.conf должен быть раздел [realms], содержащий как минимум параметр kdc:
[realms]
PRESTO.MYDOMAIN.COM = {
kdc = kdc.mydomain.com
}
[domain_realm]
.presto.mydomain.com = PRESTO.MYDOMAIN.COM
presto.mydomain.com = PRESTO.MYDOMAIN.COM
В разделе [domain_realm] вы можете также указать сетевой домен, в котором находится кластер Presto.
Настройка узла-координатора и рабочих узлов
Первым делом сконфигурируйте кластер так, чтобы он использовал протокол
HTTPS. Для этого следуйте инструкциям из предыдущего раздела. Затем раз-решите использование Kerberos, внеся соответствующие настройки в файл
config.properties для узла-координатора и для рабочих узлов: http-server.authentication.type=KERBEROS
http.server.authentication.krb5.service-name=presto
http.server.authentication.krb5.service-hostname=presto.mydomain.com http.server.authentication.krb5.keytab=</path/to/keytab>
http.authentication.krb5.config=/etc/krb5.conf
142 Безопасность Presto
Если вы хотите использовать Kerberos только для внешней аутентификации (т. е. аутентификации клиентов), не добавляйте эти строки в config.
properties рабочих узлов и оставьте возможность соединения по HTTP. В документации Presto1 вы найдете дополнительную информацию об использо-вании Kerberos.
Настройка клиента Presto
При запуске клиента Presto укажите в командной строке параметры, начинающиеся с krb5-:
presto \
--server https://presto-coordinator:8443 \
--truststore-path /opt/presto/etc/presto-keystore.jks \
--truststore-password password \
--krb5-config-path /etc/krb5.conf \
--krb5-principal [email protected] \
--krb5-keytab-path /path/to/keytab \
--krb5-remote-service-name presto
Более детальные инструкции по настройке клиентских приложений, как
обычно, – в официальной документации2.
Создание нестандартного аутентификатора
При помощи Presto SPI вы можете интегрировать Presto с любым сервисом
аутентификации. В главе 3 вы уже научились разрабатывать коннекторы.
Если вы хотите написать расширение для работы с сервисом аутентификации, вам следует применить специализированные интерфейсы3, которые
также входят в Presto SPI. Например, чтобы создать расширение, исполь-зующее парольную аутентификацию, реализуйте интерфейс PasswordAuthenticator:
public class MyAuthenticator
implements PasswordAuthenticator{
...
}
В качестве образца можете взять исходный код расширения для LDAP-аутентификации4. И не забудьте, что для включения аутентификации узлы
должны общаться по протоколу HTTPS.
1 .
2
3 .
4
Авторизация 143
Авторизация
В процессе авторизации пользователь получает права выполнить какие-ли-бо действия. Авторизованный пользователь может работать с системой, но
только с теми ее объектами и функциями, к которым ему предоставлен доступ.
Авторизация доступа к REST API
Авторизация в Presto выполняется на двух уровнях – на системном уровне и на уровне каталога (коннектора). Авторизация на системном уровне
определяет глобальные политики доступа и выполняется до авторизации
на уровне каталога. Для авторизации вы можете либо использовать одно из
встроенных расширений Presto, либо реализовать свое собственное, следуя
инструкциям в соответствующем разделе документации1. Встроенных модулей у Presto три: allow-all (аутентифицированный пользователь может
выполнять любые действия, такая авторизация включается по умолчанию), read-only (любой пользователь может только читать данные и метаданные) и file access (можно создать конфигурационные файлы с настройками доступа).
В главе 4 мы рассмотрели различные способы подключения клиента Presto, включая REST API. Настроим авторизацию на уровне API, т. е. укажем каждому пользователю, какие функции он может вызывать.
В Presto определены три роли:
User – пользователь, которому необходим доступ к данным;
Internal – узлы кластера, как координатор, так и рабочие узлы; вызовы
API требуются им для запуска задач на рабочих узлах или для обмена
данными между рабочими узлами;
Admin – администратор, которому требуется доступ к системным функциями, например «получить состояние узла».
Чтобы сконфигурировать авторизацию, сначала настройте аутентифика-
цию – как это сделать, рассказано в предыдущем разделе. В этом разделе мы
будем использовать аутентификацию, основанную на файлах. Для авторизации можно либо использовать встроенный класс ConfigurationBasedAuthorizer, либо создать собственное расширение, как это описано в документации2.
Настройка управления доступом
Presto позволяет подключать расширения, обеспечивающие авторизацию
пользователей на глобальном уровне. Вы можете создать собственные рас-1
2
144 Безопасность Presto
ширения или использовать одно из встроенных. Встроенных расширений
три: allow-all, read-only и file. Последнее позволяет задавать правила доступа
в файле конфигурации. Более подробную информацию об управлении до-
ступом можно найти в документации Presto1.
Рассмотрим сценарий, показанный на рис. 7-1. Наша задача – создать правила, позволяющие пользователю clientA читать каталог tpch, а пользователю ClientB – каталог mysql. Полный код рассматриваемых здесь примеров
находится в папке 07/presto-authorization нашего репозитория. Чтобы включить управление доступом, добавьте файл access-control.properties в папку etc координатора Presto. Если включено управление доступом на основе файлов, Presto запрещает любые операции над каталогами, если только они явно не
разрешены соответствующим правилом. В файле access-control.properties укажите тип управления доступом, который вы хотите использовать (в нашем
случае – файловый), и путь к файлу с правилами:
access-control.name=file
security.config-file=/path/to/rules.json
Сами правила указываются в файле формата JSON, подробно описанном
в документации2. Мы хотим разрешить пользователю clientA читать данные
из каталога tpch, а пользователю clientB – из каталога mysql. Создадим соответствующие правила:
{
"catalogs": [
{
"user": "clientA",
"catalog": "tpch",
"allow": "read-only"
},
{
"user": "clientB",
"catalog": "mysql",
"allow": "read-only"
}
]
}
В раздел catalogs мы записали правила, определяющие доступ к каталогам. После создания файла перезапустите кластер и запустите клиент Presto, подключившись к системе как пользователь clientA. Presto должен позво-лить вам выполнять запросы к каталогу tpch, но не к каталогу mysql. Если вы
подключитесь как пользователь clientB, то наоборот – сможете обращаться
к каталогу mysql, но не к каталогу tpch.
presto \
--server https://presto-coordinator:8443 \
1
2
Авторизация 145
--truststore-path /opt/presto/etc/presto-keystore.jks \
--truststore-password password \
--user clientA \
--password
Password:
presto> show catalogs;
Catalog
---------
system
tpch
(2 rows)
Авторизация и управление доступом
Система управления доступом в Presto регулирует доступ к ресурсам – каталогам, схемам, таблицам, столбцам и строкам. Вы можете настроить политики управления
доступом на любом из этих уровней. С другой стороны, авторизация в Presto – это
набор правил, которые Presto автоматически применяет к доступным ресурсам.
Пусть у нас есть таблица с тремя столбцами: пользователь, пароль и количество купленных товаров. Политика авторизации гарантирует, что только авторизованные со-трудники компании могут подключаться к кластеру и читать данные из таблицы. Система управления доступа позволяет выполнить более тонкую настройку, определив
более детальную политику. Например, команда инженеров может читать и изменять
значения всех столбцов, а команда аналитиков – только читать столбец с количеством
купленных товаров.
Авторизация при помощи Apache Ranger
Управлять доступом пользователей можно на уровне коннектора. Для этого
используются или возможности самой СУБД, с которой работает коннектор, или внешний сервис авторизации, например Apache Ranger, подключенный
через расширение для Hive. В этом разделе мы покажем, как настроить доступ на уровне коннектора при помощи Apache Ranger.
Apache Ranger1 – сервис авторизации с открытым исходным кодом, поддерживающий детальные настройки управления доступом и возможность
аудита. Сервис работает с разными платформами и обеспечивает централизованное управление. Открытая модель данных и система расширений
Apache Ranger позволяет управлять доступом к платформам экосистемы
Hadoop. Сервис активно используется ведущими облачными провайдерами, такими как AWS, Azure и Google Cloud Platform.
На рис. 7.5 показана архитектура системы, где интегрированы Presto и Ranger. Ranger использует базу данных для репозитория политик и предоставляет модуль управления аудитом. Чтобы подключить Ranger к Presto, требуется установить соответствующее расширение к Presto. Кроме того, надо настроить Presto, чтобы он записывал журнал аудита в базу данных.
1

146 Безопасность Presto
Apache Ranger
Репози
Сервис
Сервис аудита
торий
администрирования
Расширение Presto
для Ranger
Журнал
аудита
Presto
Рис. 7.5 Интеграция Presto и Ranger
Для Ranger существует расширение Presto-Hive, с помощью которого его
можно подключить к Presto. Это расширение можно использовать только
для каталогов, подключенных при помощи коннектора Hive, например Hive Metastore и AWS Glue. Коннектор Hive поддерживает Apache Ranger 2.1.0
и старше. Подробные инструкции по настройке расширения Ranger для Hive можно найти в соответствующем разделе документации Presto1.
Разработка функции аудита
Задача аудита – отслеживать действия пользователей хранилища данных
и сохранять их в журнале, чтобы убедиться, что пользователи работают
в соответствии с политиками, стандартами и нормативными актами. Прежде чем разрабатывать собственную функцию аудита, необходимо описать
требования, а именно список событий, которые должны попасть в журнал
(чтение и изменение данных, подключение и отключение пользователей
и т. д.), и уровень детализации журнальной информации. Следующий шаг –
определить местоположение журнала аудита. Это может быть, например, журнал Presto или журнал Ranger. Наконец, имея подробное техническое
задание, можно создавать функцию аудита – желательно с использованием
такого сервиса, как Apache Ranger. Примеры настроек и детальные инструкции можно найти в документации Ranger2.
Заключение
Из этой главы вы узнали, как обеспечить безопасность Presto. Для этого нужно сконфигурировать безопасный обмен данными, а также настроить аутентификацию и авторизацию.
1 .
2
Заключение 147
Безопасный обмен данными обеспечивается шифрованием данных, пере-
даваемых по сети, что предотвращает несанкционированный доступ к этим
данным. Для обеспечения безопасного обмена данных в Presto следует настроить HTPS/TLS.
Аутентификация позволяет предоставить доступ к кластеру Presto только
авторизованным пользователям. Presto дает возможность настроить аутентификацию, основанную на файлах, а также аутентификацию по протоколам LDAP и Kerberos. Вы можете разработать и собственное расширение для
аутен тификации.
Система авторизации предоставляет пользователям права на доступ к конкретным каталогам, схемам и таблицам, ограничивая возможность выполнения некоторых операций. Presto позволяет авторизовать пользователей
при помощи встроенной системы управления доступом, а также при помощи
внешних сервисов, например Apache Ranger.
Глава 8
Настройка
производительности
Настройка производительности заключается в постепенных мелких улучше-ниях, каждое из которых немного повышает скорость выполнения запросов
или уменьшает объем используемых ресурсов, тем самым повышая общую
производительность. Процесс начинается с анализа существующей системы
и определения узких мест, улучшение которых приведет к повышению производительности. После того как место приложения усилий определено, мы
можем вносить в систему изменения.
Эта глава разделена на пять частей. В первой части мы изложим общие
понятия, относящиеся к настройке производительности, включая описание
цикла настройки, а также побудительных мотивов для работы над производительностью кластера. Во второй части мы познакомимся с тем, как Presto выполняет запросы. Это знание позволит нам понять, где и как искать узкие
места. Затем мы познакомимся с традиционными подходами к настройке
производительности Presto, включая распределение ресурсов, настройку системы хранения и оптимизацию запросов. В четвертой части мы познакомимся с продуктом Aria Scan, который позволяет повысить эффективность
чтения данных в Presto. В пятой, последней, части мы рассмотрим пример, в котором будем решать практическую задачу настройки кластера и менять
его конфигурацию.
Введение в настройку
производительности
Как вы уже знаете, Presto – это распределенный движок SQL-запросов, который позволяет одновременно обрабатывать данные из множества разных
источников. Чем больше объем обрабатываемых данных, тем большее значение приобретает оптимизация, конечной целью которой является снижение
времени выполнения запросов до величины, приемлемой для пользователя.

Введение в настройку производительности 149
В процессе настройки производительности эксперт находит проблемные
места, которые замедляют систему, уменьшают ее общую пропускную способность или дестабилизируют ее.
Побудительные мотивы настройки
производительности
Настройка производительности – это циклический процесс, в течение которого администратор наблюдает за состоянием системы и изменяет ее конфигурацию, когда это необходимо. Для того чтобы запустить этот процесс, есть множество причин, а именно:
возможность обрабатывать больше данных. Вы сможете выполнять
сложные запросы к большому количеству разнородных данных за при-
емлемое время;
уменьшение времени ответа. Правильно настроенная система выдает
ответ быстрее. Это особенно важно в тех приложениях, где требуется
аналитика в режиме, близком к реальному времени;
экономия ресурсов. Правильно настроенная система обходится меньшим количеством ресурсов – серверов и сетевого оборудования.
Жизненный цикл настройки производительности
Цикл настройки производительности состоит из трех шагов, как показано
на рис. 8.1.
Нагрузочное
Анализ
Настройки
тестирование
Мониторинг системы
Оптимизация
по умолчанию
Измерение
(реактивный
Изменение настроек
производительности
и проактивный)
системы
Рис. 8.1 Цикл настройки производительности Presto
Базовая конфигурация
Кластер Presto запускается с настройками по умолчанию.
Тестирование производительности
Первый шаг цикла заключается в измерении производительности систе-
мы. Метриками могут служить время выполнения запросов, количество
узлов в кластере или объем ресурсов, используемых для выполнения запроса.
Анализ
Второй шаг – мониторинг системы в поисках узких мест. Мониторинг
включает в себя исследование планов запросов, в особенности тех, вы-

150 Настройка производительности
полнение которых занимает много времени, а также поиск запросов, по-требляющих много ресурсов. Есть два типа мониторинга – проактивный
и реактивный. Проактивный мониторинг заключается в постоянном сборе
метрик производительности, включая загрузку процессоров и утилизацию
оперативной памяти. Задача реактивного мониторинга – поиск узких мест
и причин их возникновения.
Оптимизация
Наконец, на третьем шаге администратор изменяет конфигурацию класте-ра, основываясь на выводах, сделанных на шаге анализа.
Модель выполнения запроса
Прежде чем говорить об увеличении производительности кластера, необходимо разобраться, как Presto выполняет запросы. Понимание принципов
работы Presto позволит вам понять возможные причины возникновения
проблем производительности и, используя это знание, влиять на планы запросов. Если эксперт обладает фундаментальными знаниями, эффективность любой оптимизации существенно увеличивается.
Механизм выполнения запросов в Presto показан на рис. 8.2.
Токенизация
Анализ
SQL
AST
Логический план
Оптимизация
Деление
на фрагменты
Физический план
Этапы выполнения
Рис. 8.2 Механизм выполнения запросов в Presto
Когда узел-координатор получает запрос от клиентского приложения, он
разбирает и анализирует текст запроса. В результате этого шага он создает
внутреннее представление, называемое абстрактным синтаксическим де-ревом (AST, Abstract Syntax Tree), из которого затем создается план запроса.
Построение плана состоит из двух шагов: сначала строится логический план, а затем – физический. Логический план – это высокоуровневая модель запроса, не учитывающая особенности источников данных (каталогов) и конкретного кластера. Главная задача построения логического плана – составить
последовательность операций, которые должны быть выполнены для полу-
Модель выполнения запроса 151
чения результата. Физический план – это уже более детальное представление, в котором учтены все особенности среды выполнения: количество узлов, доступные ресурсы, сетевая топология и т. д.
План выполнения состоит из этапов (stages), фрагментов (splits) и задач
(tasks):
этап – это часть плана, которая может быть выполнена параллельно.
Каждый этап состоит из одной или нескольких задач. Обычно коорди-
натор разбивает запрос на несколько этапов;
фрагмент – набор данных, который рабочий узел может обработать
независимо. Фрагмент – наименьшая единица обработки в Presto;
задача ( task) – объем работы, выполняемый одним рабочим узлом. Задача обрабатывает один или несколько фрагментов.
Координатор составляет план и распределяет задачи между рабочими
узлами, которые выполняют эти задачи и возвращают координатору результат. Все промежуточные данные и состояния хранятся в памяти рабочих узлов, что позволяет достичь наивысшей производительности. Как
только выполнение очередной задачи заканчивается, координатор обнов-ляет статус запроса. Получив результат запроса, координатор возвращает
его клиенту.
Рассмотрим запрос, вычисляющий сумму всех покупок каждого клиента
и выдающий 10 клиентов, потративших наибольшие суммы в порядке убывания суммы. Запрос выполняется над схемой tpch.tiny:
SELECT c.custkey, c.name, sum(o.totalprice)
FROM customer c
JOIN orders o ON c.custkey = o.custkey
GROUP BY c.custkey, c.name
ORDER BY sum(o.totalprice)
DESC LIMIT 10;
Запустите консоль Presto по инструкции из главы 6, запустите запрос, затем найдите его в списке запросов и перейдите в пункт live plan. Один из
возможных планов состоит из четырех этапов:
этап 3 – состоит из единственной задачи, которая читает таблицу customers;
этап 2 – состоит из пяти задач (см. рис. 8.3), которые читают таблицу
orders (ScanProject), соединяют ее с таблицей customer (LocalExchange) при помощи задачи InnerJoin и, наконец, выполняют проекцию (Project) и частичную агрегацию (Aggregate);
этап 1 – состоит из четырех задач, выполняющих окончательную агрегацию и частичную выборку 10 первых строк;
этап 0 – состоит из трех задач, вычисляющих окончательный результат
и возвращающих его пользователю.
Обратите внимание, что количество фрагментов и задач на каждом этапе
зависит от конфигурации кластера, распределения данных и настроек па-раллельного выполнения.

152 Настройка производительности
Рис. 8.3 Структура этапа 3 в консоли Presto
Подходы к настройке
производительности Presto
Существует три общепринятых метода настройки производительности: выделить больше ресурсов, оптимизировать хранение данных или оптимизировать запрос.
Первые два подхода помогают, когда узким местом системы является недостаток ресурсов, но скорость выполнения запросов они увеличивают лишь
в исключительных случаях. Третий подход не гарантирует успеха, поскольку
в некоторых случаях запросы генерируются автоматически и переписать
множество сгенерированных запросов просто физически невозможно. Поэтому лучшим решением будет комплексный подход, сочетающий управление ресурсами с оптимизацией запросов.
Выделение ресурсов
При управлении ресурсами необходимо учитывать текущий уровень утилизации и степень параллелизма. Выделение дополнительных ресурсов являет-
Подходы к настройке производительности Presto 153
ся самым быстрым, но отнюдь не всегда самым дешевым способом ускорить
выполнение запросов. Существует два подхода к увеличению мощности:
вертикальное масштабирование ( scale up) . Представляет собой увеличение мощности отдельного узла – добавление процессорных ядер, памяти, дисков. Этот подход следует использовать, когда единственный узел оптимальным образом справляется с текущей нагрузкой, но
мощности этого узла не хватает;
горизонтальное масштабирование ( scale out). Заключается в добавле-нии к кластеру новых узлов, чтобы координатор мог распределять на-
грузку по большему количеству серверов. Используйте этот подход, когда один сервер заведомо не справится с нагрузкой и увеличение его
мощности нецелесообразно.
Независимо от того, какой подход вы выберете, важно понимать, какие
именно ресурсы нужно добавлять. Это зависит от характера запросов, выполняемых в системе. В табл. 8.1 приведена сводная информация о том, каким
запросам какие ресурсы необходимы.
Таблица 8.1. Необходимые ресурсы в зависимости от типа запросов
Тип запроса
Примеры
Требуемые ресурсы
Запросы, требующие Сортировка или агрегация больших
Память (например,
хранения большого наборов, соединения больших таблиц
если вы работаете
объема данных
или большого количества таблиц
в облаке AWS,
в памяти
используйте узлы
типа R)
Запросы, требующие Вычисления сложных функций, запросы Процессор (например, интенсивных
с многочисленными преобразованиями если вы работаете
вычислений
данных, рекурсивные операции,
в облаке AWS,
множество подзапросов и вложенных
используйте узлы
запросов, соединения больших таблиц
типа C)
Итак, есть два типа ресурсов, которые мы можем наращивать, – процессорные ядра и память.
Память
Чтобы добавить памяти узлу Presto (либо координатору, либо рабочему
узлу), измените в файле jvm.config параметр -Xmx16G. Например, чтобы выделить узлу 60 Гбайт оперативной памяти, поменяйте его на -Xmx60G.
Не забудьте исправить параметр query.max-memory-per-node в файле config.
properties. Значение этого параметра по умолчанию – 10 % от максималь-ного объема памяти, выделенного виртуальной машине Java параметром
-Xmx. Высокой производительности можно добиться, установив значение
query.max-memory-per-node в половину максимально доступного объема. Но
разумеется, если в вашем кластере одновременно работает много запросов, то значение этого параметра должно быть меньше1.
1 Авторы не упоминают, что если узел Presto работает в контейнере Docker, то нужно
также увеличить объем памяти, выделяемой контейнеру. – Прим. перев.
154 Настройка производительности
Полную информацию о параметрах, управляющих выделением памяти
в Presto, можно найти в документации1.
Процессорные ядра
Если ваши запросы выполняют много вычислений, положительно повли-
ять на общую производительность кластера может уменьшение паралле-
лизма, т. е. количества одновременно исполняемых запросов. Параметр
task.concurrency в файле config.properties задает количество «виртуальных
процессоров» на узле. По умолчанию это значение равно 16, но если нагрузка на процессор велика, можно его уменьшить. Справочник по другим
параметрам с префиксом task., управляющим распределением вычисли-
тельных ресурсов, можно найти в документации2.
Уменьшение параллелизма снижает нагрузку на процессор, но, с другой
стороны, увеличивает время ожидания запросов в очереди. Вам предсто-ит найти разумный компромисс между загрузкой процессора и степенью
параллелизма.
Система хранения данных
Presto не управляет форматами данных, которые обрабатывает, и не имеет
собственных механизмов индексирования этих данных. Поэтому если проблема производительности заключается в медленном доступе к данным, со
стороны Presto мало что можно изменить.
Чтобы решить проблемы со скоростью доступа, вы должны настраивать
хранилище данных. Например, вы можете использовать форматы данных
со сжатием и с мощной системой метаданных, такие как ORC и Parquet. Выполняя запрос, Presto обращается к метаданным, и если по результатам этого
обращения очевидно, что данных, удовлетворяющих запросу, в файле нет, Presto не читает файл.
Для увеличения производительности полезно также настроить секционирование ( partitioning) на уровне хранилища. Секционированием называется
деление набора данных на подмножества, называемые секциями. Каждая
секция представляет собой отдельный объект хранения и может размещаться как на тех же узлах, что остальные секции этого же набора, так и на отдельных. В Presto вы можете создать секционированную копию несекцио-нированной таблицы с помощью команды CREATE TABLE AS (CTAS), добавив
к команде предложение partitioned_by. Подробные инструкции по созданию
секционированных таблиц можно найти в документации3.
Оптимизация запросов
Оптимизатор работает на узле-координаторе в момент запуска запроса. Для
построения наилучшего плана запроса используются как эвристический, 1 .
2
3
Подходы к настройке производительности Presto 155
так и стоимостной оптимизатор. В процессе поиска оптимального плана
оптимизатор перебирает множество вариантов. Он использует два основных
приема – проталкивание предикатов и стоимостную оптимизацию.
Проталкивание предикатов
Этот прием заключается в перемещении предикатов, т. е. условий, фильт-рующих данные, как можно ближе к операции чтения данных. За счет
раннего уменьшения количества обрабатываемых данных уменьшается
объем ввода-вывода и сетевого трафика, что ведет к ускорению выполнения запросов. Как правило, проталкивание предикатов помогает ускорить
соединение таблиц с условиями WHERE за счет того, что значительное количество данных просто не доходит до операции соединения.
Presto поддерживает проталкивание предикатов, но для того, чтобы оптимизатор мог использовать этот прием, его должен поддерживать коннектор. О том, как написать коннектор, поддерживающий проталкивание
предикатов, вы можете прочитать в статье «Совершенствование оптимизатора Presto для эффективной обработки данных» («Improving the Presto planner for better push down and data federation»)1. Например, чтобы включить проталкивание предикатов в коннекторе Hive, работающем с системой хранения с интерфейсом S3, в конфигурационный файл каталога (например, hive.properties) надо добавить следующую строку:
hive.s3select-pushdown.enabled=true
Подробности – как обычно, в документации Presto2.
Стоимостной оптимизатор
Стоимостной оптимизатор выбирает оптимальный план запроса, основываясь на оценках затрат ресурсов на выполнение того или иного плана.
Оптимизатор учитывает профиль данных, сохраненный в виде статистики.
Статистика включает такие показатели, как количество строк в таблице, средний размер столбца данных и др. Опираясь на статистику, оптимизатор выбирает оптимальный порядок соединения таблиц.
Presto поддерживает стоимостную оптимизацию, но чтобы она работала
с конкретным каталогом, ее должен поддерживать и коннектор. Детальная
информация о статистике и стоимостной оптимизации – в документации3.
Presto поддерживает такие стратегии стоимостной оптимизации, как пе-ребор соединений (join enumeration) и выбор распределения соединений
(join distribution selection). Перебор соединений перебирает все возможные последовательности выполнения соединений, чтобы найти оптимальный порядок. Выбор распределения соединений определяет, как вычис-
лять хеш-таблицы для соединения хешированием. Стратегии включаются
установкой параметров в файле config.properties или в соответствующих
настройках сеанса. В табл. 8.2 приведены поддерживаемые стратегии CBO, параметры config.properties, соответствующие настройки сеанса и допусти-мые значения.
1
2 .
3 .
156 Настройка производительности
Как всегда, в документации1 можно найти более подробное описание стои-мостного оптимизатора Presto.
Таблица 8.2. Стратегии стоимостной оптимизации и соответствующие
параметры
Стратегия
config.properties Настройки сеанса
Допустимые значения
Перебор
optimizer.join-
join_reordering_strategy AUTOMATIC (default),
соединений
reordering-strategy
ELIMINATE_CROSS_JOINS, NONE
Выбор
optimizer.join-
join_distribution_type
AUTOMATIC (default),
распределения distribution-type
BROADCAST, PARTITIONED
соединений
Aria Scan
В 2019 году Facebook запустил проект Aria2, который позволил достичь двух-трехкратного уменьшения нагрузки на процессор при выполнении запросов
Hive над таблицами в формате ORC. Авторы проекта сосредоточились на двух
направлениях – сканирование таблиц и перераспределение задач.
Сканирование таблицы
По умолчанию если в таблице не определены индексы, то операция скани-рования таблицы читает всю таблицу целиком. Независимо от того, нужна
строка или нет, она читается с диска. Из-за такого подхода сканирование
таблиц, особенно больших, оказывается медленным и ресурсоемким.
Aria уменьшает затраты процессорного времени на сканирование таблицы
за счет двух приемов – исключения столбцов и проталкивания фильтров.
Исключение столбцов
Оптимизатор определяет, какие столбцы выбираются в запросе, и читает
только их, пропуская неиспользуемые столбцы.
Чтобы включить исключение столбцов в Presto, следует задать в файле config.properties параметру experimental.pushdown-subfields-enabled значение
true. Можно также воспользоваться параметром pushdown_subfields_enabled в настройках сеанса.
Проталкивание фильтров
Этот прием позволяет фильтровать данные не в самом Presto, а в коннекторе Hive. Это снижает нагрузку на процессор за счет того, что ненужные
данные не передаются в Presto и не хранятся во временных структурах
в памяти.
1 .
2
Практическая настройка производительности 157
Чтобы включить проталкивание фильтров, установите в true параметр
<hive_catalog>.pushdown-filter-enabled в файле config.properties. Если, например, каталог называется hive, то полное название параметра будет
hive.pushdown-filter-enabled. Можно также воспользоваться параметром
<hive_catalog>.pushdown_filter_enabled в настройках сеанса.
Адаптивный порядок фильтрации эффективен для запросов, содержащих
несколько соединений. Он позволяет выбирать порядок соединений на
основе дополнительных критериев, тем самым сокращая общее время
выполнения запроса.
Перераспределение задач
Перераспределение заключается в изменении набора задач, выдаваемого
каждому рабочему узлу. Если расширение Aria не включено, Presto распределяет задачи в два шага – вычисление и сериализация. На шаге вычисления
рассчитывается хеш-функция от значения колонки, являющейся ключом
распределения, а затем на шаге сериализации страницы с данными записы-ваются в буфер вывода. Aria позволяет исключить фазу вычисления и сразу
записывать прочитанные данные в буфер1. Включение такой оптимизации
в некоторых случаях позволяет снизить нагрузку на процессор вдвое.
Чтобы включить оптимизацию распределения данных, установите в true значение параметра experimental.optimized-repartitioning в файле config.
properties или соответствующую ему настройку сеанса optimized_repartitio-ning.
Чтобы управлять размером буферов вывода, используйте параметр driver.
max-page-partitioning-buffer-size. Этот параметр устанавливает максимальный объем всех буферов, и по умолчанию его значение составляет 32 МБайта.
Размер каждого буфера не может превышать 1 МБайта.
Практическая настройка
производительности
В этом разделе мы разберем реальный сценарий настройки производительности. Мы возьмем систему, архитектура которой изображена на рис. 5.5, с параметрами конфигурации по умолчанию, затем проведем несколько
тестов и подберем значения некоторых параметров, описанных в этой главе, чтобы ускорить выполнение запроса. Поскольку Aria scan поддерживает
только файлы в формате ORC, мы создадим таблицу в этом формате ORC
1 Авторы не указывают, в каких случаях возможна такая оптимизация. Если, например, ключ распределения участвует в операции GROUP BY, то исключение фазы
вычисления, наоборот, замедлит выполнение запроса. – Прим. перев.
158 Настройка производительности
и загрузим ее в MinIO. Для наблюдения за кластером будем использовать
консоль Presto, описанную в главе 6.
Наш сценарий будет состоять из четырех шагов:
1) построение таблицы в формате CSV и ее загрузка в MinIO;
2) преобразование таблицы в формате CSV в ORC;
3) определение параметров, влияющих на производительность и подле-жащих модификации;
4) запуск тестов.
Создание CSV-таблицы и загрузка в MinIO
Для нашего эксперимента мы выгрузим одну из таблиц теста TPC-H и загрузим ее в хранилище MinIO. Чтобы сделать это, надо подключиться к каталогу tpch, прочитать таблицу customer из схемы sf1 и экспортировать ее
в CSV-файл. Если мощности не хватает, вы можете выбрать схему с меньшим
количеством данных. Затем мы загрузим полученный файл в MinIO, при помощи Spark выгрузим его в формате ORC и снова загрузим в MinIO файл – на
этот раз в оптимизированном формате.
Вы можете пропустить этот раздел и взять готовый CSV-файл в нашем
репозитории по адресу 08/data/customer.csv.
1. Запустите все компоненты хранилища данных командой ./deploy.sh
-d и подключитесь к контейнеру, в котором выполняется узел-коорди-
натор Presto. В папку /opt/presto-server/etc/catalog добавьте файл tpch.
properties, в котором содержится строка connector.name=tpch.
2. Перезапустите узел:
cd /opt/presto-server/bin
./launcher restart
После выполнения этой команды вы будете отключены от терминала,
и вам надо будет подключиться вновь.
3. Чтобы экспортировать таблицу customer в файл, запустите следующую
команду:
presto-cli \
--execute "SELECT * FROM tpch.sf1.customer;" \
--output-format CSV_HEADER > customer.csv
В таблице содержится примерно 150 тысяч строк, но если это слишком
большая нагрузка для вашего компьютера, замените sf1 на другое имя
схемы.
4. Чтобы скопировать файл customer.csv из контейнера на локальную машину, установите в контейнере пакет tar, воспользовавшись командой
yum install tar. Затем отключитесь от контейнера узла-координатора
и из командной строки своей локальной операционной системы запустите следующую команду:
Практическая настройка производительности 159
kubectl cp\
presto-coordinator-nm87g:/opt/presto-server/customer.csv\
./customer.csv
Замените presto-coordinator-nm87g на имя контейнера, в котором работает узел-координатор. Узнать это имя можно, выведя список запущенных подов командой kubectl get pods -n presto.
5. Подключитесь браузером к консоли MinIO по адресу http://localhost:9090/, введите логин и пароль для доступа и создайте в корзине
warehouse новый путь1 tpch. Загрузите по этому пути файл customer.csv.
Преобразование CSV-таблицы в ORC
Для преобразования таблицы из формата CSV в формат ORC используйте
Spark.
1. Подключитесь к контейнеру, где работает клиент Spark, и запустите
оболочку, как описано в главе 5.
2. Подключите требуемые библиотеки:
import org.apache.hudi.QuickstartUtils._
import scala.collection.JavaConversions._
import org.apache.spark.sql.SaveMode._
3. Загрузите таблицу customer в формате CSV:
val tableName = "customer"
val basePath = "s3a://warehouse/tables/customer"
val df = spark.read.options(Map("header" -> "true")).
csv("s3a://warehouse/tpch/customer.csv")
4. Преобразуйте таблицу в формат ORC и сохраните ее под именем orc_
customer:
df.write.format("orc").
options(getQuickstartWriteConfigs).
option(RECORDKEY_FIELD_OPT_KEY, "id").
option(PRECOMBINE_FIELD_OPT_KEY, "dob").
option(TABLE_NAME, tableName).
mode(Overwrite).
saveAsTable("orc_customer")
Теперь мы готовы к тому, чтобы определить, какие параметры будем изменять.
1 Авторы используют термин path вместо термина catalog (или folder, или directory), поскольку структура хранилища S3 не иерархическая, а плоская. – Прим. перев.
160 Настройка производительности
Определение параметров настройки
Ранее в этой главе мы разобрали некоторые наиболее популярные параметры настройки Presto. Их можно разделить на два типа – бинарные параметры, принимающие значение «вкл.» или «выкл.», и параметры с числовыми
значениями. Первые вы можете либо включить, либо выключить, а вот выбирать значения вторых можно из достаточно широкого диапазона. В табл. 8.3
приведены параметры конфигурации, разделенные на колонки по типам.
Жирным шрифтом выделены параметры, которые мы будем настраивать
в этом примере. Мы сосредоточимся только на некоторых важнейших параметрах, но вы можете использовать приведенный подход и для настройки
других параметров.
Таблица 8.3. Настройки производительности по типам
«Вкл.»/«Выкл»
Диапазон значений
hive.s3select-pushdown.enabled
query.max-memory-per-node
experimental.pushdown-subfields-enabled
task.concurrency
hive.pushdown-filter-enabled
optimizer.join-reordering-strategy
experimental.optimized-repartitioning
optimizer.join-distribution-type
Нагрузочное тестирование
Для нагрузочного тестирования будем использовать такой запрос:
SELECT count(*) as total, nationkey
FROM orc_customer
WHERE nationkey NOT IN ('23', '6')
GROUP KEY nationkey;
Мы протестируем несколько вариантов настройки. В каждом из тестов мы
зададим соответствующие значения параметров и затем выполним тестовый
запрос. Вот эти варианты:
настройки по умолчанию;
уменьшение нагрузки на процессор;
оптимизация запроса;
Aria scan.
Мы будем задавать значения параметров через настройки сеанса, и когда
найдем оптимальные настройки, распространим их на весь кластер через
файл config.properties.
Параметры по умолчанию
Запустите клиент Presto в контейнере с узлом-координатором и выполните
тестовый запрос. Затем откройте в браузере консоль Presto. Потом на вклад-ке Query Details нажмите Finished и найдите информацию о выполненном

Практическая настройка производительности 161
запросе. Вы увидите подробную информацию о запросе: общие сведения, детальный план выполнения, включая разбиение на фрагменты, и время
выполнения каждого этапа. На рис. 8.4 показана информация, относящаяся
к запросу, выполненному с настройками по умолчанию. Время выполнения
запроса составляет 8,14 секунды. Это базовое значение, которое мы поста-раемся уменьшить.
Рис. 8.4 Результат выполнения запроса
с настройками по умолчанию
Уменьшение нагрузки на процессор
Чтобы уменьшить время выполнения, мы можем уменьшить нагрузку на
процессор, уменьшив количество «виртуальных процессоров» на узле. Значение параметра task.consurrency по умолчанию – 16, а мы попытаемся уменьшить его до 8:
SET SESSION task_concurrency = 8;
Изменив значение параметра, запустим запрос еще раз. Информация из
консоли показана на рис. 8.5. Теперь время выполнения составляет 6,67 секунды, то есть мы уменьшили его почти на полторы секунды.
Оптимизация запроса
Для оптимизации запроса мы можем разрешить проталкивание предикатов, оставив при этом уровень параллелизма как в предыдущем запуске:
SET SESSION hudi.s3_select_pushdown_enabled=true; Снова запустим запрос. Результаты его выполнения можно увидеть на
рис. 8.6. Время выполнения теперь составляет 6,89 секунды. Это медленнее,


162 Настройка производительности
чем в предыдущем запуске, то есть для этого конкретного запроса проталкивание предикатов не помогло.
Рис. 8.5 Работа тестового запроса
после уменьшения нагрузки на процессор
Рис. 8.6 Результат выполнения запроса
с включенной оптимизацией
Aria scan
Теперь попробуем включить проталкивание фильтров, сохраняя установленный ранее уровень параллелизма:

Заключение 163
SET SESSION hudi.pushdown_filter_enabled = true; Еще раз запустим запрос и посмотрим на статистику выполнения на
рис. 8.7. Теперь время работы запроса составляет 6,20 секунды, что почти на
полсекунды меньше предыдущего значения.
Рис. 8.7 Результат выполнения запроса с включенным Aria scan По результатам нашего эксперимента мы выяснили, что для выполнения
этого запроса оптимальным набором настроек в config.properties будет такой: task.concurrency = 8;
hudi.pushdown-filter-enabled = true;
Заключение
В этой главе вы научились настраивать производительность Presto. Настройка производительности крайне важна для обеспечения максимальной эффективности и скорости работы вашего кластера Presto.
Мы обсудили основы настройки производительности и рассмотрели мо-
дель выполнения запросов Presto. Мы также рассмотрели некоторые методы
оптимизации производительности: распределение ресурсов, настройку хранилища, оптимизацию запросов, Aria scan.
В конце главы мы разобрали практический пример настройки некоторых
параметров конфигурации в Presto.
Глава 9
Масштабирование
Presto
Масштабирование заключается в возможности обрабатывать возрастающую
нагрузку без влияния на производительность: пользователи не должны заме-чать увеличения времени обработки запросов, несмотря на то что запросов
становится значительно больше.
Здесь у нас не будет практического задания, и поэтому в нашем репозитории нет папки для этой главы. Подход к масштабированию всегда инди-видуален и зависит от конкретной нагрузки на кластер. Вместо этого мы
обсудим общую стратегию масштабирования и методы, комбинация которых
позволит вам адаптировать кластер к растущей нагрузке.
Глава состоит из четырех частей. В первой мы поговорим об общих по-нятиях, относящихся к масштабированию, таких как причины, вызывающие
необходимость масштабирования, и проблемы, которые обычно возникают
в кластерах Presto. Во второй части мы рассмотрим некоторые принципы, положенные в основу Presto, которые следует учитывать при масштабировании кластера. К ним относятся доступность, управляемость, производительность, защита и простота настройки. Далее проанализируем популярные
подходы к масштабированию кластера Presto, включая использование нескольких координаторов, установку Presto на Spark и подкачку. Последняя
часть посвящена масштабированию Presto в облаках.
Введение в масштабирование
Реакцией на увеличение нагрузки в Presto является добавление ресурсов по
мере увеличения нагрузки. Между масштабированием и настройкой производительности, о которой мы говорили в главе 8, есть принципиальная
разница: настройка производительности – это уменьшение времени ответа
системы, а масштабирование – увеличение ее пропускной способности.

Введение в масштабирование 165
Когда требуется масштабирование?
Масштабирование необходимо, если произошло хотя бы одно из следующих
событий: возросло количество пользователей, увеличилось количество запросов или вырос объем данных (рис. 9.1).
Рост количества
Рост интенсивности
Рост
пользователей
запросов
объема данных
Кластер Presto
Кластер Presto
Кластер Presto
Рис. 9.1 Основные причины увеличения мощности Presto
Чтобы понять, что пришло время наращивать мощность системы, необходимо постоянно наблюдать за ее производительностью. Когда по одной из
указанных выше причин производительность падает ниже некоторой отметки, ресурсы кластера необходимо увеличивать. Лучше делать это чуть-чуть
заранее, пока падение производительности не стало проблемой.
Часто возникающие проблемы
Перед тем как переходить к описанию конкретных приемов масштабирования Presto, обозначим несколько наиболее часто возникающих проблем
и ограничений, которые надо учитывать при масштабировании. К таким
проблемам относят единые точки отказа (SPOF, Single Point Of Failure), некорректное состояние рабочего узла или бессмысленные запросы.
Единая точка отказа
Когда мы разворачивали учебный кластер, в нем был единственный узел-координатор. Координатор в Presto поддерживает связность кластера и ко-ординирует все коммуникации между узлами. Также он отвечает за разбор запросов и управление их выполнением. Узел-координатор является
единой точкой отказа, поскольку в нашем кластере есть функции, которые
выполняются только этим узлом. При отказе координатора весь кластер
будет недоступен.
166 Масштабирование Presto
Некорректное состояние рабочего узла
Если узел попытается загрузить данных больше, чем объем доступной
памяти, произойдет ошибка переполнения памяти (out-of-memory error).
Подобная ошибка может перевести рабочий узел в некорректное состояние. В этом состоянии все задачи, относящиеся к тому же запросу, что
и «упавшая» задача, завершаются с ошибкой. Однако Presto продолжает
выполнять другие задачи, относящиеся к тому же запросу, на других узлах, что ведет к бессмысленному расходу ресурсов.
Бессмысленные запросы
Бессмысленный запрос – тот, который не возвращает желаемого результата или выполняется слишком долго. Такие запросы расходуют вычис-
лительные ресурсы и отбирают их у других запросов. Запрос может выполняться долго по следующим причинам:
• неэффективность: запрос плохо написан и не оптимизирован;
• ресурсоемкость: для выполнения запроса нужно больше ресурсов, чем
физически есть в кластере;
• конкуренция за метаданные: слишком много запросов одновременно об-ращаются к хранилищу метаданных за информацией о схеме.
В табл. 9.1 собраны наиболее часто встречающиеся причины неэффективности, ресурсоемкости или высокой конкуренции.
Таблица 9.1. Некоторые часто встречающиеся проблемы запросов
Конкуренция
Неэффективность
Ресурсоемкость
за метаданные
Неверные соединения
Большие
Высокая интенсивность
несекционированные
запросов
таблицы
к system.information_schema
Неэффективные операции
Секции разного размера
сравнения
Пропущенные фильтры
Слишком много
обрабатываемых данных
Пропущенные предикаты
Неэффективные сортировки
(ORDER BY) или группировки
(GROUP BY)
Основные принципы
Создавая большие кластеры Presto, необходимо помнить о соблюдении правил и принципов, которые позволят обеспечить доступность, управляемость, высокую производительность, защиту информации и правильную настройку.

Основные принципы 167
Доступность
В некоторых случаях кластеру Presto требуется перезагрузка, например вам
необходимо обновить версию Presto, или возникла неустранимая проблема
с виртуальной машиной Java – нехватка памяти либо большие задержки при
сборке мусора. Во время перезапуска кластер недоступен. Отношение времени, когда кластер выполняет запросы пользователей, к общему времени
существования кластера называется доступностью. Для достижения высокой
доступности следует строить кластер таким образом, чтобы он переживал
потерю любого узла и продолжал выполнять пользовательские запросы.
Кластер Presto может не удовлетворять требованиям доступности, в особенности если на нем выполняются специфические запросы. Вы можете
параллельно использовать несколько кластеров Presto, осуществляющих различные задачи над одними и теми же данными, что гарантирует высокую
доступность.
Чтобы поддерживать несколько кластеров Presto, вы можете использовать
Presto Gateway. Это роутер запросов для Presto, позволяющий задавать разные политики роутинга. Если вам, например, нужно перезагрузить один из
кластеров, то вы можете запретить на роутере обращение к этому кластеру, а когда кластер снова будет готов принимать и обрабатывать запросы – снова разрешить. Этот процесс можно автоматизировать, добавляя или убирая
кластеры Presto в зависимости от текущей нагрузки.
Чтобы разобраться, как работает Presto Gateway, рассмотрим рис. 9.2. Система, изображенная на нем, состоит из двух кластеров Presto, 1 и 2, и одного
роутера. Вы тоже можете развернуть и запустить роутер, следуя инструкции
из официальной документации1. Затем разверните два кластера Presto в разных пространствах имен Kubernetes, например presto1 и presto2. Узлы-координаторы этих кластеров должны принимать соединения на разных портах, например на 9001 и 9002. Чтобы развернуть эти кластеры, воспользуйтесь
инструкциями из главы 5, а если мощности вашей машины не хватает для
развертывания хранилища-озера, то инструкциями из главы 2.
Кластер 1
Namespace: presto1
HTTP service: http://localhost:9001
Presto Gateway
Кластер 2
Namespace: presto2
HTTP service: http://localhost:9002
Рис. 9.2 Система, состоящая из двух кластеров Presto и роутера
1 .
168 Масштабирование Presto
Когда вы запустите эту систему, роутер будет ожидать соединений по адресу http://localhost:8080/. Откройте терминал и добавьте роутеру информацию
о двух кластерах Presto следующей командой:
curl -X POST http://localhost:8080/entity?entityType=GATEWAY_BACKEND \
-d '{ "name": "presto1", \
"proxyTo": "http://localhost:9001",\
"active": true, \
"routingGroup": "adhoc" \
}'
curl -X POST http://localhost:8080/entity?entityType=GATEWAY_BACKEND \
-d '{ "name": "presto2", \
"proxyTo": "http://localhost:9002",\
"active": true, \
"routingGroup": "adhoc" \
}'
Оба кластера Presto активны. Если по каким-то причинам вы ходите от-ключить кластер presto2, выполните следующую команду:
curl -X POST http://localhost:8080/gateway/backend/deactivate/presto2
Недавно в проекте Presto появился собственный роутер, который так и называется – Presto router. Это более совершенное программное обеспечение, чем Presto Gateway, но с той же функциональностью. Инструкции по развер-тыванию можно найти в документации Presto1.
Несмотря на то что Presto router уже выпущен, мы осознанно выбрали
Presto Gateway, чтобы показать, как легко интегрировать Presto с компонен-тами, созданными независимыми разработчиками.
Управляемость
Управляемость – это характеристика системы, показывающая, насколько легко и эффективно вы можете наблюдать за состоянием системы и сохранять ее
работоспособность, безопасность и производительность. Из главы 6 вы уже
узнали, как администрировать кластер Presto. В дополнение к описанным
способам и стратегиям управления Presto позволяет подключать расширения, обрабатывающие системные события. Для создания такого расширения
следует реализовать интерфейс EventListener, который перехватывает такие
события, как queryCreated, queryCompleted и splitCompleted:
package com.facebook.presto.spi.eventlistener;
public interface EventListener {
default void queryCreated(QueryCreatedEvent queryCreatedEvent) { }
default void queryCompleted(QueryCompletedEvent queryCompletedEvent) { }
default void splitCompleted(SplitCompletedEvent splitCompletedEvent) { }
}
1 .
Основные принципы 169
Если вы хотите создать свой листенер, ознакомьтесь с официальной доку-ментацией1 и статьей в блоге AWS2, либо можете использовать готовый пример из репозитория aws-samples 3. Собранный JAR-файл расширения нужно
скопировать в папку plugin узла-координатора. Чтобы сконфигурировать
лис тенер, создайте файл event-listener.properties в папке etc узла-координатора и добавьте в него следующую строку:
event-listener.name=event-listener
Перезапустите кластер Presto, а затем в любом клиентском приложении
выполните запрос:
SELECT * FROM tpch.tiny.customer LIMIT 10;
Теперь найдите файл log/queries-YYYY-MM-DDTHH:MM:SS.0.log на файловой
системе узла-координатора, чтобы увидеть вывод расширения-листенера.
Файлы журнала, созданные листенером, позволяют следить за работоспособностью и производительностью кластера Presto. Например, событие
QueryCompleted содержит не только тип ошибки (ошибка пользователя или
ошибка сервера), но и дамп состояния стека (stack trace) на момент сбоя
запроса. Кроме того, в журналах можно найти множество дополнительной
информации о запросах: указаны ли в запросах фильтры, отсекающие секции
таблиц, как таблицы участвуют в соединениях, сколько исходных данных
читает каждый запрос и т. д.
Производительность
В главе 8 вы узнали, как настраивать производительность кластера Presto.
Для настройки промышленного кластера требуется создать такой же кластер
и запустить на нем промышленную нагрузку. В главе 6 вы узнали, как настроить и использовать верификатор, чтобы проверить функционирование
нового кластера. Однако верификатор тестирует только функциональность, но не производительность. Для проведения нагрузочного теста необходимо
использовать утилиту Replayer, позволяющую воспроизвести нагрузку промышленного кластера в тестовой среде.
На рис. 9.3 показан типичный сценарий, используемый при настройке
производительности. В промышленном кластере листенер записывает запросы, выполняемые пользователями, и сохраняет их в EventDB – репозитории запросов. В тестовой среде утилита нагрузочного тестирования читает
из репозитория запросы, запускает несколько клиентских сеансов и с их помощью воспроизводит сохраненную нагрузку. На тестовом кластере также установлен листенер, сохраняющий журнал выполнения запросов. По
окончании тестирования специальная утилита Replay Analytics and Reporting 1
2 .
3 .

170 Масштабирование Presto
Service сравнивает журналы, полученные на разных кластерах, и показывает
в отчете разницу в производительности.
Промышленная среда
Среда тестирования
Кластер Presto
Листенер
Клиент Клиент Клиент
(тестовый)
(тестовый)
Presto
Presto
Presto
Воспроиз-
Кластер Presto
ведение
(промышленный)
Клиенты
Листенер
Сервис
Аналитика
воспроизведения
и отчетность
БД событий
Рис. 9.3 Тестовая среда с использованием утилиты Replayer Защита
Presto – распределенная система, и это делает ее уязвимой для злоумышлен-ников, которые могут использовать ее для проведения атак на базовую инфраструктуру. В главе 7 вы узнали, как обезопасить кластер Presto и защитить
его от подобных угроз. Кроме того, вы можете защитить свой кластер Presto от бесполезных запросов, сохранив тем самым ресурсы кластера. Для блоки-ровки или перезаписи таких запросов можно использовать Presto Gateway.
Реализовать блокировку нежелательных запросов можно не только при
помощи роутера, но и при помощи расширения-листенера. Блокировать запрос можно, выбросив исключение в методе queryCreated. Например, код, приведенный ниже, блокирует запросы к таблице system.information_schema.
columns, в которых нет ограничений на выбираемые данные, при помощи
фразы where:
public void queryCreated(QueryCreatedEvent queryCreatedEvent) {
String queryText =
queryCreatedEvent.getMetadata().getQuery().toLowerCase();
if (queryText.contains("system.information_schema.columns") &&
!queryText.contains("where")) {
logger.error("Rejected query from event listener: " + queryText); throw new PrestoException(StandardErrorCode.COMPILER_ERROR,
"Querying without predicates is not permitted.");
}
}
Основные принципы 171
Настройка
Из главы 6 вы узнали, как конфигурировать кластер Presto, используя конфигурационные файлы, настройки сеансов и настройки виртуальной машины
Java. Однако для больших промышленных кластеров этих трех инструментов
может быть недостаточно. Кроме этого, вы должны выбрать правильную
конфигурацию (или, если вы заказываете серверы в облаке, правильный тип
сервера) каждого узла – как координатора, так и рабочего узла.
Выбор оптимальной конфигурации узла – непростая задача, поскольку он
зависит от характера запросов, выполняемых в системе. Чтобы верно указать
типы узлов, следует определить долгосрочную стратегию выбора правиль-ных пропорций между объемом памяти и процессорной мощностью. Для
этого необходимо собрать метрики мониторинга виртуальной машины Java.
В идеале во время максимальной нагрузки на кластер память и процессор
узла-координатора должны быть загружены на 50–70 %. Процессоры рабочих
узлов должны быть загружены на 90 % или более, а память – минимум на
70 %. Если показатели использования процессора или памяти слишком высоки или слишком низки, то сервер не сбалансирован с точки зрения ресурсов.
Ниже перечислены параметры виртуальной машины Java, влияющие на
конфигурацию узла. Значения параметров вы можете установить в файле
jvm.config:
-server – устанавливает «серверный» режим работы виртуальной машины Java. Этот параметр активирует комплекс настроек, в основном
относящихся к сборке мусора, оптимальных для долгоживущих сер-верных приложений;
-Djdk.attach.allowAttachSelf=true – позволяет подключаться отладчи-ком к виртуальной машине Java;
-Xlog:gc:<path/to/log/dir>/gc.log – включает запись событий, связанных со сборкой мусора, в файл gc.log в папке <path/to/log/dir>;
-XX:+UseG1GC – включает модуль G1 для сборки мусора;
-XX:+AggressiveOpts – включает ряд агрессивных оптимизаций, способных в некоторых случаях существенно увеличить производительность;
-XX:ReservedCodeCacheSize=150M – устанавливает размер кеша JIT-ком пи-ля тора Java в 150 Мбайт;
-XX:ErrorFile={{ presto_log_dir }}/java_error.log – указывает имя файла, в который будет записана информация об ошибке в случае фаталь-
ной ошибки;
-Xms{{ 0.80 * available_node_memory_mb }}m – устанавливает начальный
размер кучи виртуальной машины равным 80 % доступной оператив-
ной памяти;
-Xmx{{ 0.80 * available_node_memory_mb }}m – устанавливает максимальный размер кучи виртуальной машины равным 80 % доступной опе-
ративной памяти;
-XX:MaxGCPauseMillis=400 – задает целевой показатель максимальной
задержки при сборке мусора. Виртуальная машина будет стараться, чтобы паузы при сборке мусора не превышали этого значения;
172 Масштабирование Presto
-XX:InitiatingHeapOccupancyPercent=5 – устанавливает пороговое значение заполненности кучи в процентах, после которого виртуальная
машина переходит в режим смешанной сборки мусора1;
XX:OnOutOfMemoryError="kill -9 %p" – указывает команду, которая будет
запущена при ошибке переполнения памяти (OutOfMemoryError ). В данном случае процесс с идентификатором %p будет немедленно завершен
командой kill -9;
-XX:G1NewSizePercent=1 – задает начальный объем кучи, отведенный для
вновь создаваемых объектов (new generation), равным 1 % от общего
объема кучи.
Чтобы включить возможность удаленной отладки виртуальной машины
Java, добавьте следующие параметры:
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
Как масштабировать Presto
Для поддержки высокой рабочей нагрузки, при которой запросы могут работать по нескольку часов, сканируя петабайты данных, необходимо масштабировать кластер. В этом разделе мы рассмотрим три метода масштабирования: использование нескольких координаторов, запуск Presto в Spark и вытеснение. Правильное применение этих методов позволит вашей компании эффективно обрабатывать большие объемы данных.
Несколько координаторов
Одновременный запуск нескольких узлов-координаторов позволит вам масштабировать кластер Presto. В такой конфигурации координаторы только
управляют жизненным циклом запроса, а новый компонент, называемый менеджером ресурсов ( resource manager), управляет кластером. В кластере Presto может быть один или несколько менеджеров ресурсов. Узлы-координаторы
работают независимо друг от друга. На рис. 9.4 изображен кластер Presto с несколькими узлами-координаторами и с несколькими менеджерами ресурсов. Новый запрос отправляется произвольному координатору, который
отправляет его менеджеру ресурсов, где запрос становится в очередь на выполнение. Периодически координатор запрашивает запросы из очереди менеджера ресурсов и принимает решение об их запуске. Также координаторы
получают от менеджера ресурсов информацию об активных рабочих узлах.
Подробнее работа кластера с несколькими координаторами описана в статье
«Распределенный координатор» («Disaggregated Coordinator»)2.
1 Подробное описание сборщика мусора G1 можно найти в статье «Дюк, вынеси мусор! – часть 3». Ссылка на с. – Прим. перев.
2 .

Как масштабировать Presto 173
Узлы
Менеджеры
координаторы
ресурсов
Рабочие узлы
Рис. 9.4 Кластер Presto
с несколькими координаторами
Чтобы сконфигурировать несколько координаторов, добавьте по одному
узлу для каждого менеджера ресурсов. Файл config.preperties для кластера
с единственным менеджером ресурсов будет выглядеть так:
resource-manager=true
resource-manager-enabled=true
coordinator=false
node-scheduler.include-coordinator=false
http-server.http.port=8080
thrift.server.port=8081
discovery-server.enabled=true
discovery.uri=http://<my_domain>:8080
thrift.server.ssl.enabled=true
Приведенные выше настройки выключают координатор (coordinator=false) и менеджер ресурсов (resource-manager=true). В параметре discovery.uri укажите адрес менеджера ресурсов.
Модифицируйте также config.properties узлов-координаторов и рабочих
узлов, добавив в них следующие настройки:
discovery.uri=http://<my_domain>:8080
resource-manager-enabled=true
Параметр discovery.uri должен содержать адрес ресурсной группы. Подробные инструкции по настройке кластера с несколькими координаторами, как обычно, можно найти в документации Presto1.
Presto на Spark
Apache Spark – это платформа для обработки больших данных с открытым исходным кодом, обладающая высокой масштабируемостью и отказоустойчи-востью. Вы можете развернуть ее как на отдельном сервере, так и на кластере
из нескольких сотен узлов. Поскольку Spark работает в виртуальной маши-1

174 Масштабирование Presto
не Java, вы можете легко интегрировать его с другими Java-приложениями, включая Presto.
Есть много веских причин запускать Presto на Spark. Во-первых, это может значительно повысить производительность Presto на пакетных задачах
(например, ETL-процессах) за счет увеличения степени параллелизма. Во-вторых, Spark – надежная и масштабируемая платформа, и Presto на Spark будет работать более стабильно. Наконец, такая интеграция позволит Presto пользоваться функциями экосистемы Apache Spark.
При запуске Presto на Spark используется только уровень Spark Resilient Distributed Dataset (RDD) и ниже, без применения Spark SQL engine. RDD позволяет Spark перепроверять данные в случае сбоя запроса.
На рис. 9.5 показана архитектура Presto на Spark. Драйвер Spark запускает
упрощенную версию узла-координатора Presto, которая анализирует и оптимизирует запросы, создает физические планы запросов и отправляет
задачи рабочим узлам. Рабочие узлы Presto совмещены с исполнителями
Spark (Spark executors). Функциональность узлов Presto, как координатора, так и рабочих узлов, доступна в виде библиотек, которые реализуют только
обработку запросов. Управление памятью, потоковая обработка и сетевое
взаимодействие реализуются кластером Spark. Архитектура Presto на Spark хорошо описана в статье «Масштабирование Presto с помощью Spark» («Scal-ing with Presto on Spark»)1.
Исполнитель Spark
Рабочий узел
Драйвер Spark
Библиотека
Координатор
Управление
Библиотека
кластером Spark
Исполнитель Spark
Рабочий узел
Библиотека
Рис. 9.5 Архитектура Presto на Spark
Чтобы запустить Presto на Spark, загрузите из Maven-репозитория пакет
Presto для Spark и jar-файл, запускающий Presto на Spark. Затем добавьте
следующие строки в config.properties:
task.concurrency=<N>
task.max-worker-threads=<N>
task.writer-count=<N>
1 .
Как масштабировать Presto 175
Значение N зависит от возможностей вашего оборудования. Обычно до-
статочно 8 или 16.
Также необходимо сконфигурировать каталоги Presto. Их конфигурация
ничем не отличается от обычных инсталляций.
Напишите запрос и сохраните его в файле query.sql:
SELECT * FROM tpch.tiny.customer;
Чтобы запустить запрос, используйте команду spark-submit. Аргумент N
должен быть установлен в то же значение, что и в файле config.properties выше:
/spark/bin/spark-submit \
--master spark://<url-to-spark-master>:7077 \
--executor-cores <N> \
--conf spark.task.cpus=<N> \
--class com.facebook.presto.spark.launcher.PrestoSparkLauncher \
/path/to/presto-spark-launcher-0.280.jar \
--package /path/to/presto-spark-package-0.280.tar.gz \
--config /path/to/config.properties \
--catalogs /path/to/presto/catalog/directory \
--file query.sql
В официальной документации1 развертывание Presto на Spark описано
более детально.
Подкачка2
В главе 6 мы уже писали, что подкачка – это перемещение части данных из
оперативной памяти на диск. Подкачка позволяет Presto продолжать работу, даже если памяти не хватает. Когда у узла заканчивается доступная оперативная память, он автоматически записывает часть данных на диск, осво-бождая место для дополнительной обработки. Благодаря подкачке кластер
может обрабатывать большие объемы данных и одновременно обслуживать
большее количество пользователей.
Подкачка повышает надежность кластера за счет уменьшения количества
ошибок, связанных с нехваткой памяти. Она также снижает риск перегрузки
узла и возникновения узких мест. Кроме того, подкачка помогает максимально эффективно использовать оборудование и позволяет снизить стоимость
кластера. Однако следует учитывать, что обычно подкачка значительно снижает производительность.
Чтобы включить подкачку в Presto, установите в true параметр experimental.spill-enabled.
1
2 Напомню, что вместо swapping в книге используется термин spilling. – Прим. перев.
176 Масштабирование Presto
Использование облачных сервисов
Самостоятельное развертывание кластера Presto с чистого листа требует
значительных усилий и компетенций в таких областях, как распределенные
системы, сети, настройка оборудования и программного обеспечения. Это
занятие:
долгое. Вам надо настроить узлы, установить программное обеспечение
и настроить сетевое взаимодействие;
дорогое. Вы должны приобрести оборудование и лицензии на программное обеспечение, а потом оплачивать сопровождение и техни-
ческую поддержку;
сложное. Вам потребуются основательные технические знания и опыт.
Существенно упросить развертывание Presto можно, развернув кластер
в облаке одного из провайдеров – AWS, Google Cloud Platform или Microsoft Azure.
Поставщики облачных услуг помогают сократить усилия и затраты на
развертывание кластера Presto, предоставляя готовые шаблоны и сценарии
автоматизации, которые упрощают и ускоряют настройку. Кроме того, об-лачные провайдеры предлагают такие функции, как мониторинг и автома-тическое масштабирование, а также дополнительные средства обеспечения
безопасности. Все это еще больше упрощает управление кластером Presto.
В облаке вы получите:
гибкость. Облачные сервисы позволяют легко добавлять или удалять
ресурсы. Это означает, что вы можете расширять и уменьшать свой
кластер в зависимости от текущей нагрузки без каких-либо предвари-
тельных инвестиций в инфраструктуру;
экономию. В облаке вы платите только за те ресурсы, которые реально
используете, что делает кластер экономически эффективным. Кроме
того, подбирая подходящую конфигурацию для узлов (оптимизиро-
ванную для вычислительных нагрузок или для больших объемов дан-
ных), вы можете использовать еще меньше ресурсов, снизив тем самым
стои мость услуг;
надежность. Облачные сервисы предоставляют готовые механизмы
отказоустойчивости, гарантирующие, что ваш кластер останется доступным при отказе любого сервера;
безопасность. Облачные сервисы поддерживают такие механизмы
обес печения безопасности, как шифрование и многофакторная аутен-
тификация. Применение этих механизмов поможет защитить ваши
данные и предотвратить несанкционированный доступ;
масштабирование. Облачные сервисы позволяют быстро и просто добавлять узлы в кластер или удалять их в зависимости от требований
к мощности кластера;
эластичность. Облачные сервисы могут автоматически увеличивать
или уменьшать объем выделенных ресурсов, что позволяет оптимизи-
ровать производительность и сократить расходы на поддержку клас-
тера.
Заключение 177
Заключение
Из этой главы вы узнали, как масштабировать кластер Presto. Масштабируемость показывает, насколько хорошо кластер может справляться с возрас-тающей нагрузкой, источником которой может быть увеличение количества
пользователей, объема данных или сложности запросов.
Мы обсудили основы масштабирования и описали некоторые архитектур-
ные особенности Presto, обеспечивающие масштабирование его кластеров.
Мы также рассмотрели наиболее распространенные подходы к масштабированию Presto, включая использование нескольких узлов-координаторов, развертывание Presto на Spark и подкачку. В последней части главы мы рассказали, как масштабировать кластер Presto с помощью облачных сервисов.
Вы только что закончили читать эту книгу. На всем ее протяжении вы
изучали основные концепции Presto: его архитектуру, способы добавления
источников данных, выполнение запросов с использованием различных клиентов. Вы узнали, как построить хранилище-озеро и включить в него Presto в связке с самыми современными технологиями, такими как Hive и Hudi.
В последней части книги вы узнали, как администрировать, защищать, настраивать и масштабировать кластер Presto.
Глава за главой вы проверяли все концепции, описанные в книге, на практике. Код для решения всех рассмотренных задач доступен в репозитории, который мы создали специально для этой книги. Надеемся, что это позво-лило вам получить не только теоретическое представление о платформе, но
и практические навыки работы с ней.
Мы верим, что, прочитав эту книгу, вы продолжите знакомство с Presto и с каждым днем будете узнавать об этой платформе все больше, читая другие статьи и книги и активно участвуя в работе онлайн-сообществ.
Предметный указатель
A
Docker Desktop,
Docker Hub,
ACID-транзакция,
Altair,
ANSI SQL,
E
apache-util,
ELT,
API
ETL,
мониторинг,
EXPLAIN, оператор,
Data Location,
Data Stream,
G
Metadata,
REST,
Google Cloud,
Aria,
Google Guice,
Aria Scan,
ARM,
H
AWS,
HDFS,
узлы,
Hive,
Azure,
Hive Metastore, ,
Blob Storage,
HMS (Hive Metastore),
Hudi,
B
bcrypt,
I
Benchto,
Impala,
IntelliJ IDEA,
C
ConfigMap,
J
JDK,
D
JMX,
JRE,
Data governance. См. Стратегическое
управление данными
Data lake. См. Озеро данных
K
Data lakehouse. См. Хранилище-озеро
Kerberos, ,
Docker,
Kubernetes,
Предметный указатель 179
L
Trino,
LDAP,
А
M
Абстрактное синтаксическое
дерево,
MapReduce,
Авторизация,
Maven,
Аудит,
MinIO, ,
Аутентификация, ,
взаимная,
N
основанная на файлах,
Kerberos,
Netflix,
LDAP,
O
Б
ORC,
База данных,
TPC-H,
P
Балансировщик нагрузки,
Pandas,
Parquet, ,
В
Pinot,
Ввод-вывод,
POJO,
Веб-интерфейс
Presto Gateway,
GCeasy,
Presto router,
MinIO,
Prometheus,
Presto,
Веб-приложение,
R
Верификатор,
Версионирование данных,
Ranger,
Внедрение зависимостей,
Replayer,
Д
S
Данные
S3,
неструктурированные,
Scheduler, См. Организатор
слабоструктурированные,
Secret,
структурированные,
Spark, ,
Доступность,
Spark SQL,
Драйвер,
SPI,
JDBC,
Spilling. См. Подкачка
ODBC,
Split. См. Фрагмент
Spark,
Stage. См. Этап
Streamlit,
Ж
T
Жизненный цикл
администрирования,
Task. См. Задача
запроса,
Thrift,
настройки производительности,
TLS,
узла,
180 Предметный указатель
З
М
Задача,
Масштабирование,
Запрос бессмысленный,
вертикальное,
горизонтальное,
И
Машинное обучение,
Идентификатор
Метаданные,
запроса,
каталог,
пользователя,
управление,
процесса,
Модуль разбора,
узел,
Мониторинг,
Интерфейс
метрики, ,
описание,
Монтирование
Connector,
локальной папки,
ConnectorFactory,
папки в Docker-контейнере,
ConnectorHandleResolver,
тома,
ConnectorRecordSetProvider,
файла,
ConnectorSplit,
ConnectorSplitManager,
О
MetaData,
Module,
Обмен данными,
Plugin,
Объектное хранилище,
RecordCursor,
Озеро данных,
RecordSet,
Оптимизатор,
RecordSetProvider,
стоимостной,
SplitManager,
эвристический,
Организатор,
К
Ошибка
аутентификации,
Каталог,
на этапе инициализации,
добавление,
переполнения памяти,
метаданных,
пространств имен,
синтаксическая,
службы обнаружения,
тайм-аут,
список,
фатальная,
LDAP,
system,
П
Квота,
Память,
Конкуренция,
максимальный общий объем,
Коннектор,
максимальный объем,
JMX,
максимальный объем
Координатор,
пользовательской области,
объем используемой,
Библиотека,
объем свободной,
несколько узлов,
Курсор,
пользовательская,
системная,
Л
управление,
Перераспределение данных,
Листенер,
Перераспределение задач,
Предметный указатель 181
Планировщик,
У
Подкачка,
Привилегии,
Узел, ,
Пространство имен
Управление доступом,
функций,
Kubernetes,
Ф
Проталкивание
Файловая система,
предикатов,
фильтров,
Федеративный запрос,
Процессор
Формат,
виртуальный,
колоночный,
нагрузка,
табличный,
Процессорное время, ,
файлов,
CSV,
Р
Delta Lake,
Hudi,
Расширение,
Iceberg,
Регулярные выражения,
Java Keystore File,
Ресурсные группы,
JSON,
Роли,
узла,
ORC,
Parquet,
С
Фрагмент,
Функция,
Сборка мусора,
аудита,
Сеанс,
изоляция,
Х
настройки, ,
Хранилище
Сертификат,
данных,
импорт,
ключей,
самоподписанный,
Хранилище-озеро,
удостоверяющих центров,
Стратегическое управление
Ш
данными,
Схема,
Шифрование,
на диске,
Т
трафика,
Тестирование
Э
нагрузочное,
регрессионное,
Эволюция схемы данных,
Тип данных,
Этап,
Анжелика Ло Дука, Тим Михан,
Вивек Бхаратан и Ин Су
Изучаем и используем Presto
Главный редактор Мовчан Д. А.
Перевод Комаров В. И.
Корректор Синяева Г. И.
Верстка Чаннова А. А.
Дизайн обложки Мовчан А. Г.
Гарнитура PT Serif. Печать цифровая.
Усл. печ. л. 14,79. Тираж 100 экз.
Веб-сайт издательства:
