Короткое замыкание HDFS читать подробно

HDFS Эксплуатация и обслуживание
В этой статье представлена ​​эволюция считывания короткого замыкания HDFS, безопасного считывания короткого замыкания и оптимизации Xiaomi безопасного считывания короткого замыкания.
Обзор предыдущей статьи:Анализ проблем вывода из эксплуатации HDFS

задний план

Важная идея Hadoop — перемещать вычисления, а не данные. Мы предпочитаем перемещать вычисления в узел, где находятся данные, насколько это возможно. Поэтому в HDFS часто видно, что клиент и данные находятся на узле, и когда клиент читает блок данных, происходит локальное чтение. Например, в сценарии HBase данные, записанные ResionServer, обычно будут хранить три резервные копии в HDFS и обязательно запишут резервную копию на локальный узел.Когда ResionServer считывает данные, он также предпочтительно выбирает данные того же узел для чтения.

Эволюция чтения с коротким замыканием

1. Сетевое чтение

Первоначально локальное чтение в HDFS обрабатывается так же, как удаленное чтение, и также реализуется через чтение по сети. Клиент подключается к DataNode через сокет TCP и передает данные по протоколу DataTransferProtocol (как показано на следующем рисунке):

Этот способ прост, но имеет очевидные проблемы:

DataNode должен зарезервировать один поток и один TCP-сокет для каждого клиента, считывающего блок данных. В ядре будут служебные данные протокола TCP и служебные данные протокола DataTransferProtocol, поэтому есть много возможностей для оптимизации.

2. Чтение небезопасного короткого замыкания HDFS-2246

Ключевая идея состоит в том, что чтение замыкается накоротко, потому что клиент и блок данных находятся на одном узле, а DataNode не обязательно должен присутствовать на пути чтения данных. Сам клиент может читать данные прямо с локального диска. Было бы значительно улучшена производительность чтения. Замыкание чтения HDFS-2246 реализовано во всех блоках данных DataNode с разрешениями на открытые пути к клиенту, клиент считывает данные напрямую через локальный дисковый путь, как показано ниже:


Но этот подход приносит много проблем:

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

(2) Эти изменения разрешений создадут дыру в безопасности.Пользователи с разрешением на чтение файла блока данных на узле DataNode могут произвольно читать все блоки данных на пути, а не только блоки данных, к которым им нужен доступ, что, по-видимому, делает Пользователь становится суперпользователем, что может быть приемлемо для небольшого числа пользователей, таких как пользователи HBase. Но в целом это принесет большие риски для безопасности.

3. Безопасное чтение короткого замыкания HDFS-347

Основная проблема с HDFS-2246 заключается в том, что каталог данных DataNode открыт для клиента, и все, что нам действительно нужно, — это прочитать часть файла блока данных. В Unix есть механизм для этого, называемый передачей дескриптора файла. HDFS-347 использует этот механизм для безопасного чтения с коротким замыканием. Вместо передачи каталога клиенту DataNode открывает файлы блоков и метаданных и передает их файловые дескрипторы клиенту через сокет домена (рис. 3):

Основываясь на следующих двух аспектах, безопасное чтение короткого замыкания решает проблемы безопасности HDFS-2246.

(1) Файловый дескриптор доступен только для чтения, поэтому клиент не может изменить файл, передающий дескриптор.

(2) Клиент не может получить доступ к самому блочному каталогу, поэтому он не может читать любые другие блочные файлы, к которым у него не должно быть доступа.

Безопасное чтение короткого замыкания HDFS

1. Короткое замыкание чтения общей памяти

Поняв эволюцию считывания короткого замыкания HDFS, давайте посмотрим, как HDFS реализует безопасное считывание короткого замыкания. DataNode передает дескриптор файла реплики чтения с коротким замыканием в DFSClient, а DFSClient кэширует дескриптор файла реплики. Поскольку состояние реплики может измениться в любое время, DFSClient и DataNode должны синхронизировать состояние реплики в режиме реального времени. В то же время DFSClient и DataNode находятся на одном компьютере.Общая память может отображать файлы в память через интерфейс mmap, предоставляемый POSIX, а отображаемые данные синхронизируются в реальном времени (как показано на рисунке 4), поэтому общая память может поддерживать все реплики чтения с коротким замыканием.Состояние позволяет DFSClient и DataNode синхронизировать информацию о репликах в режиме реального времени за счет совместного использования памяти.

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

Размер слота слота составляет 64 байта, формат данных слота, первые 4 байта — это биты флага слота, от 5 до 8 байтов — биты счетчика привязки, а оставшиеся байты зарезервированы для будущего использования, например, для статистической информации.

Два флага:

(1) VALID_FLAG: указывает, действителен ли слот.

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

(2) ANCHORABLE_FLAG: указывает, была ли кэширована копия, соответствующая слоту.

Этот флаг устанавливается, когда DataNode кэширует реплику, соответствующую слоту, через интерфейс mlock, предоставляемый POSIX. Когда флаг установлен, DFSClient больше не нужно выполнять проверку при замыкании копии, потому что операция проверки уже была выполнена при кэшировании копии, и эта копия также поддерживает чтение без копирования. Когда DFSClient считывает такую ​​копию, ему необходимо добавить к соответствующему счетчику привязок слота 1. Только когда счетчик привязок слота равен 0, DataNode может удалить эту копию из кэша.

Максимальный размер сегмента общей памяти – 8 192 Б. Когда DFSClient выполняет большое количество операций чтения с коротким замыканием, между DFSClient и DataNode может быть несколько сегментов общей памяти. DFSClient в HDFS определяет класс DFSClientShm для абстрагирования раздела общей памяти на стороне DFSClient, класс DFSClientShmManager управляет всем DFSClientShm, а класс DataNode определяет класс RegisteredShm для абстрагирования раздела общей памяти на стороне DataNode и класс ShortCircuitRegistry. управляет общей памятью на всех концах DataNode, как показано на следующем рисунке:

При безопасном чтении с коротким замыканием DFSClient и DataNode синхронизируют информацию о слоте общей памяти через сокет домена.

  • DFSClient применяет общую память для сохранения состояния реплики чтения с коротким замыканием. DataNode создаст общую память, сопоставит файл общей памяти с памятью DataNode и создаст RegisteredShm для управления этой общей памятью, а затем вернет файловый дескриптор файла общей памяти в DFSClient через сокет домена.

  • DFSClient открывает файл общей памяти в соответствии с дескриптором файла, сопоставляет файл с памятью DFSClient и создает объект DfsClientShm для управления этой общей памятью.

  • DFSClient обращается к DataNode за файловыми дескрипторами файлов блоков данных и файлов метаданных через сокет домена и синхронизирует состояние слотов в общей памяти. DFSClient подаст заявку на слот для блока данных в общей памяти, управляемой DfsClientShm, а затем синхронизирует информацию с DataNode через доменный сокет.DataNode создаст соответствующий слот в общей памяти, управляемой RegisteredShm, а затем получит файл блока данных и метаданные Файловый дескриптор файла данных, отправленный в DFSClient через сокет домена, как показано на следующем рисунке:

2. Процесс чтения с коротким замыканием

Когда клиент выполняет ускоренное чтение копий блоков данных, процесс взаимодействия между DFSClient и DataNode выглядит следующим образом:

(1) DFSClient запрашивает DataNode создать разделяемую память через интерфейс requestShortCircuitShm(), а DataNode создает файл разделяемой памяти и возвращает дескриптор файла разделяемой памяти в DFSClient.

(2) DFSClient запрашивает слот в общей памяти через интерфейс allocShmSlot() и запрашивает у DataNode чтение дескриптора файла реплики через интерфейс requestShortCircuitFds(). DataNode открывает файл реплики и сохраняет блок данных. файл и файл метаданных.Описатель возвращается в DFSClient.

(3) После того, как DFSClient прочитает копию, он асинхронно запрашивает DataNode для освобождения дескриптора файла и соответствующего слота через интерфейс releaseShortCircuitFds().

Xiaomi оптимизирует безопасное чтение короткого замыкания HDFS

1. Слоты освобождаются медленно

В нескольких напряженных сценариях мы обнаружили, что несколько коротких потоков чтения Hbase ResionServer часто блокировали чтение и запись сокетов домена. В дампе DataNode обнаружено большое количество ShortCircuitShm для чтения с коротким замыканием. Таким образом, мы смоделировали онлайн-ситуацию через YCSB и обнаружили, что при большом количестве запросов на чтение с коротким замыканием количество запросов в секунду, выделяемое BlockReaderLocal, очень велико, а выделение BlockReaderLocal зависит от распределения ShortCircuitShm и слота блока синхронного чтения. .

Подсчитав QPS, выделенные и выпущенные слотом, мы обнаружили, что QPS, выделенные слотом, могут достигать 3000+, в то время как выпущенные QPS могут достигать только 1000+, и примерно через 1 час теста YCSB DataNode появился ПОЛНЫЙ GC . Отсюда видно, что скопившиеся в DataNode слоты, которые вовремя не освобождаются, являются основными причинами GC.

В текущей реализации чтения с коротким замыканием новое соединение сокета домена создается каждый раз, когда освобождается слот. Для каждого вновь установленного подключения к сокету домена DataNode повторно инициализирует DataXceiver для обработки этого запроса. Через профиль DataNode поток SlotReleaser тратит много времени на установление и очистку этих соединений.

Поэтому мы повторно использовали подключение к доменному сокету SlotReleaser. При мультиплексировании сокетов домена в одном тестовом наборе число запросов в секунду, высвобождаемое слотом, может соответствовать выделенному количеству запросов в секунду. Это устраняет вытеснение просроченных слотов в DataNode. В то же время из-за сокращения DataNode Young GC QPS GET YCSB также увеличился примерно на 20%.

2. Низкая эффективность распределения общей памяти

В процессе чтения короткого замыкания профиля HBase мы также обнаружили еще одну проблему, то есть время от времени на пакет чтений будет задержка около 200 мс, и эти задержки появляются практически одновременно. Сначала мы подозревали, что это вызвано Minor GC Hbase ResionServer. Однако при сравнении журнала сборщика мусора ResionServer обнаруживается, что время точно не совпадает. Частично это совпадает со временем DataNode Minor GC. Реальная операция чтения копии блока данных вообще не проходит через DataNode.Если на нее влияет DataNode, проблема может быть вызвана только взаимодействием между ResionServer и DataNode при установлении короткого замыкания чтения. Добавив журналы трассировки в процессе чтения с коротким замыканием, мы обнаружили, что эти задержки были вызваны блокировкой запроса на выделение ShortCircuitShm из-за GC DataNode Minor. При выделении ShortCircuitShm это вызовет большую блокировку выделения слотов. Задержка выделения слота вызовет задержку BlockReaderLocal, что приведет к задержке чтения при коротком замыкании. Вот почему было обнаружено, что раньше была партия чтений, которые всегда сообщали об одинаковых задержках в одно и то же время. Чтобы решить эту проблему, мы предварительно выделяем ShortCircuitShm, чтобы уменьшить влияние DataNode Minor GC на степень короткого замыкания и сделать задержку более плавной.

3. Short запрещает чтение строительного блока методом короткого замыкания.

Чтение по короткому замыканию строительного блока запрещено, то есть чтение последнего блока по короткому замыканию запрещено. Из-за режима чтения-записи HBase эта проблема не оказывает на него большого влияния, но сильно влияет на потоковые сервисы на основе HDFS. Работа по оптимизации, которую мы выполняем, в основном заключается в обеспечении того, чтобы чтение с коротким замыканием происходило только после операции сброса, и в то же время проверка достоверности блока в процессе чтения, обработка исключений чтения и, если это не удается, переключиться на дистанционное чтение.

приложение

(1) Дескриптор файла

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

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

(2) Сокет домена Unix

Сокет домена Unix — это конечная точка связи для обмена данными между процессами, выполняющимися в одной и той же операционной системе хоста. Допустимыми типами сокетов домена Unix являются SOCK_STREAM (для сокетов, ориентированных на потоки) и SOCK_DGRAM (для сокетов, ориентированных на дейтаграммы, которые сохраняют границы сообщений) Как и в большинстве реализаций Unix, сокеты дейтаграмм домена Unix всегда надежны, а дейтаграммы не переупорядочиваются. Сокеты домена Unix являются стандартными компонентами операционных систем POSIX.

API сокета домена Unix аналогичен сетевому сокету, но не использует лежащий в основе сетевой протокол, и вся связь полностью осуществляется в ядре операционной системы. Сокеты домена Unix используют файловую систему в качестве адресного пространства имен. Процесс ссылается на сокет домена Unix как на индексный дескриптор файловой системы, поэтому два процесса могут взаимодействовать, открывая один и тот же сокет. В дополнение к отправке данных процессы также могут отправлять файловые дескрипторы через соединения сокетов домена Unix, используя системные вызовы sendmsg() и recvmsg(). И только если процесс-отправитель авторизован для процесса-получателя, процесс-получатель может получить доступ к разрешениям файлового дескриптора.

(3) Общая память

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

POSIX предоставляет стандартизированный POSIX API для использования разделяемой памяти. Используйте функцию shm_open в sys/mman.h. Межпроцессное взаимодействие POSIX состоит из общих функций shmat, shmctl, shmdt и shmget. Общая память, созданная shm_open, является постоянной. Он остается в системе до тех пор, пока не будет явно удален процессом. Недостатком этого является то, что если процесс дает сбой и не может очистить общую память, она останется до завершения работы системы. POSIX также предоставляет API-интерфейс mmap для отображения файлов в память, и отображение может быть общим, что позволяет использовать содержимое файла в качестве общей памяти.

Цитировать

1. https://en.wikipedia.org/wiki/File_descriptor

2. https://en.wikipedia.org/wiki/Unix_domain_socket

3. https://en.wikipedia.org/wiki/Shared_memory

4.https://www.tutorialspoint.com/inter_process_communication/inter_process_communication_shared_memory.htm

5. https://blog.cloudera.com/blog/2013/08/how-improved-short-circuit-local-reads-bring-better-performance-and-security-to-hadoop/

Эта статья была впервые опубликована в разделе «Облачные технологии Xiaomi».Нажмите, чтобы просмотреть исходный текст.