Распределенная система хранения KV с открытым исходным кодом Xiaomi Pegasus

Архитектура HBase ZooKeeper модульный тест
Автор|Сун Вэйцзе Редактор | Сяочжи Xiaomi недавно открыла исходный код распределенной системы хранения KV Pegasus.Какие концепции дизайна и технические детали стоят за этим колесом, сделанным Xiaomi?написать впереди

На этот раз я предлагаю вам тему «Распределенная реализация этих вещей — история Pegasus». В этом выступлении я сосредоточился на некоторых ловушках, с которыми мы столкнулись в архитектуре и реализации Pegasus.

Я из Xiaomi и в основном занимаюсь исследованиями и разработками Pegasus. Pegasus — это распределенное колесо хранения KV, созданное самой Xiaomi. Я не знаю, что вы, ребята, думаете о создании колес Лично я хочу избежать создания колес. Если есть лучшее решение, которое может решить проблему, сначала следует использовать другие решения; если нет хорошего решения, из которого можно было бы выбирать по мере развития бизнеса, мы можем только создать его сами.

Процесс изготовления Пегаса таков.

Рождение Пегаса

До создания проекта Pegasus распределенное хранилище Xiaomi в основном использовало HBase. Сам HBase после более чем 10-летней разработки относительно стабилен, да и функциональный интерфейс стал более удобным. Однако из-за некоторых проблем с дизайном и реализацией у нас все еще используются некоторые ямы.

Помните аварию HBase

Я считаю, что архитектура HBase должна быть знакома каждому. Как показано на рисунке выше, HBase использует главный узел для управления состоянием всего кластера. Под общим управлением Мастер-узла узел Региональный сервер отвечает за запросы клиента на чтение и запись; данные Регионального сервера существуют во внешней распределенной файловой системе HDFS. Обнаружение пульса регионального сервера и высокая доступность главного сервера выполняются Zookeeper.

Когда мы используем его на практике, Zookeeper вызывает много проблем. В начале разработки Zookeeper представлял собой систему, требующую относительно низкой нагрузки, поэтому со стороны Xiaomi Zookeeper в основном используется несколькими предприятиями. Помимо HBase, многие другие компании полагаются на Zookeeper для обнаружения узлов и регистрации служб. Однако, когда некоторые компании используют Zookeeper, они оказывают чрезмерное давление на Zookeeper, что приводит к нестабильности Zookeeper. Кроме того, некоторые неправомерные использования Zookeeper, такие как отсутствие общего доступа при доступе к Соединение через сокет также является источником нестабильности в службе Zookeeper.

Наличие различных факторов неправильного использования приводит к частым сбоям Zookeeper. Для HBase сбой Zookeeper означает, что сервер регионов также выходит из строя. Многие предприятия Xiaomi полагаются на HBase, но после того, как HBase рухнет, многие предприятия Xiaomi не смогут предоставлять услуги.

Недостатки HBase

Рассматривая ряд проблем с HBase, мы считаем, что стоит упомянуть несколько моментов:

1. Для HBase можно передать важную задачу «обнаружения узла» Zookeeper. Потому что, если эксплуатация и обслуживание недостаточно детализированы, Zookeeper станет ямой, которая повлияет на стабильность HBase.

В HBase региональный сервер обрабатывает «время ожидания сеанса Zookeeper» как «самоубийство». Реализация «нескольких регионов, записывающих один WAL в HDFS» на сервере регионов сделает стоимость «самоубийства» относительно высокой, потому что WAL будет разделен и воспроизведен при перезапуске сервера после самоубийства. Это означает, что если весь кластер HBase зависнет, потребуется много времени, чтобы снова поднять HBase.

2. Даже если мы можем гарантировать стабильность Zookeeper, функция "обнаружения узла" не может работать очень стабильно. Поскольку HBase реализован на Java. Существование GC приведет к тому, что Zookeeper неправильно расценит нормально работающий сервер региона как мертвый, что, в свою очередь, приведет к самоубийству сервера региона; регионы выше него потребуют от других серверов загрузки и воспроизведения WAL из HDFS для предоставления услуг. И этот процесс также занимает много времени. В этот период ключи, обслуживаемые Регионом не доступны для чтения и записи.

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

3. Еще одна проблема с GC заключается в том, что HBase имеет проблемы с задержкой чтения и записи. Мы надеемся максимально избежать подобных глюков в рекламно-рекомендательных сервисах, то есть иметь относительно стабильную задержку.

Подводя итог вышеизложенным трем пунктам, мы считаем, что HBase все еще имеет некоторые недостатки с точки зрения доступности и задержки производительности. Эти проблемы можно решить, переделав и настроив параметры. Но решить ее в корне непросто.

Позиционирование Пегаса

Поскольку мы не можем разобраться с HBase, нам нужно пересобрать его самостоятельно. Поскольку уже существует эталон, такой как HBase, мы также очень четко позиционируем: учитесь на сильных сторонах HBase и дополняйте его слабые стороны. Конкретно:

  • Непротиворечивое представление HBase и динамическое масштабирование — две очень хорошие характеристики системы хранения, и мы надеемся сохранить их;

  • Задержку производительности и доступность HBase мы должны компенсировать архитектурой и реализацией, чтобы покрыть систему для большего использования в бизнесе.

Благодаря этим позициям наша архитектура стала более четкой:

Краткий обзор архитектуры Pegasus

В целом архитектура многое позаимствовала у HBase:

  • В общем, Pegasus — это тоже централизованно управляемая распределенная система хранения. MetaServer отвечает за управление глобальным состоянием кластера, подобно HMaster в HBase; ReplicaServer отвечает за чтение и запись данных, как и RegionServer в HBase.

  • Из соображений масштабируемости мы также разделяем ключ на разные разделы.

Он отличается от HBase в следующих моментах:

  • Сердцебиение не зависит от Zookeeper, а извлекается отдельно и управляется непосредственно MetaServer.

  • Данные не записываются в стороннюю DFS, а попадают напрямую в ReplicaServer.

  • Для борьбы с отказом одного ReplicaServer,Каждый раздел имеет три копии, разбросаны по разным на Репликасервере.

В настоящее время высокая доступность MetaServer зависит от Zookeeper, что является преимуществом для простоты разработки проекта. Raft может быть введен позже, чтобы полностью исключить зависимость от Zookeeper.

Единый протокол для нескольких копий

Как упоминалось ранее, каждый из наших разделов имеет несколько копий. Чтобы обеспечить строгую согласованность в случае нескольких копий, мы должны использовать алгоритм консенсусного протокола. Мы используем PacificA, опубликованный MSRA. Сравнение PacificA и Raft можно найти в нашей документации в проекте github, которая здесь подробно не рассматривается. В целом, мы считаем, что реализация пригодной для использования системы хранения в соответствии с документом PacificA менее сложна, чем Raft.

Процесс записи запроса Pegasus

В протоколе PacificA тот, который отвечает за прием запросов на чтение и запись, называется первичным, что эквивалентно лидеру в Raft; два других, принимающих запросы на репликацию, являются вторичными, что эквивалентно ведомому в Raft. При такой архитектуре относительно просто написать запрос в раздел: если у клиента есть запрос на запись, ему сначала нужно запросить у Метасервера расположение ключа, а затем инициировать запрос на запись на сервер-реплику, где находится ключ. расположен первичный, а затем первичный Синхронизируйте его с вторичным, оба из которых выполнены успешно, а затем верните успешный ответ на запрос клиента на запись.

Блок-схема запроса чтения Pegasus

Операция запроса на чтение проще.Клиент напрямую инициирует запрос на чтение с основным, а основной отвечает напрямую, потому что у него есть все данные.

Эти ямы в реализации

Выше приведен общий обзор особенностей дизайна и общей архитектуры Pegasus. Теперь перейдем к нашей основной теме, посмотрим, какие проблемы будут у такой архитектуры в реализации?

Расширяемость

Сначала посмотрите на масштабируемость. Расширяемость делится на два пункта:

  • Проблема схемы разделов: полное ключевое пространство, как разделить его на разные разделы;

  • Проблема балансировки нагрузки: если схема раздела определена, как разделить эти осколки на разные машины с лучшим алгоритмом.

Выбор схемы раздела

Для схемы разделов в отрасли обычно есть два решения: первое — это хеширование, а второе — сортировка.

Для хеширования это разделить все ключи на множество ведер, одно ведро - это раздел, а затем принять решение о выделении в определенное ведро в соответствии с хэш-значением ключа; для сортировки все ключи в начале находятся в одном ведре, Затем динамически разделяйте и непрерывно объединяйте в соответствии с емкостью корзины.

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

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

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

Реализация хеш-схемы

При реализации HashSchema первой проблемой, с которой мы столкнулись, было «как хранить данные». «Положить ключ в корзину в соответствии с хеш-значением», это просто идеализированная абстракция. В реальной реализации системы вы также должны предоставить слой концепции «таблицы» для разделения различных служб. С концепцией стола мы начали расходиться во мнениях по реализации. Конкретно у нас есть две схемы «многотабличное смешанное хранилище» и «раздельное хранилище» в хранилище.

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

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

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

В таблице ниже приведено сравнение двух вариантов:

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

Балансировка нагрузки хэш-схемы

После разговора о хеш-схеме следующим шагом будет проблема балансировки нагрузки. Ниже перечислены цели общего распределенного хранилища KV при балансировке нагрузки:

  • Перегрев запросов на один ключ: Любое решение для хранения решить непросто. Если он читает, вы можете рассмотреть возможность добавления кеша слоя только для чтения; если он записывает, вы можете только разрешить бизнесу дальнейшее разделение ключа.

  • Запрос одного Partition перегрет: нет необходимости рассматривать его по хеш-схеме, т.к. ключ хэш-рассеян.

  • Распределение емкости Partition неравномерно: нет необходимости рассматривать под хеш-схемой, потому что ключ разбросан по хешу.

  • Количество Partitions неравномерно распределено по разным машинам: с этим нужно разобраться, а после того, как с этим разберутся, запросы на чтение и запись на разных ReplicaServer станут более сбалансированными.

Балансировка нагрузки — цель

В частности, балансировка нагрузки преследует две цели:

A. первичный и вторичный не могут совместно использовать ReplicaServer

B. Для каждой таблицы первичная и вторичная могут быть равномерно распределены по разным серверам реплик.

Приведенный выше рисунок представляет собой краткое описание цели B: если таблица имеет четыре раздела и всего три сервера реплик, мы надеемся, что распределение 12 реплик будет (1, 3), (2, 2), (1, 3) .

Балансировка нагрузки — алгоритмы

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

Для объяснения этого момента вы можете обратиться к двум случаям на рисунке выше:

  1. На левом рисунке 4 основных сервера распределены по серверам ReplicaServer2 и ReplicaServer3, а сервер ReplicaServer1 не имеет основного сервера. В настоящее время, поменяв местами роли первичного A на ReplicaServer3 и вторичного A на ReplicaServer1, можно удовлетворить первичный баланс.

  2. На рисунке справа распределение 4 первичных серверов на четырех серверах-репликах равно (2, 1, 1, 0). Если вы хотите сбалансировать первичный сервер, вам необходимо перенести его с ReplicaServer1 на ReplicaServer4, но для прямого переноса основного сервера требуется копирование данных. На этом этапе, если мы введем промежуточный узел ReplicaServer2, сначала поместим первичный узел A для ReplicaServer1 и ReplicaServer2. Роли вторичного A сервера ReplicaServer2 меняются местами, а затем меняются основные роли D сервера ReplicaServer2 и вторичного сервера D сервера ReplicaServer4 для достижения баланса основных ролей.

Чтобы справиться с этими ситуациями, мы строим ориентированный граф возможных направлений потоков Первича в кластере, а затем используем метод Форда-Фалкерсона для выполнения миграции Первича. Конкретный алгоритм больше не расширяется, вы можете обратиться к нашему коду проекта с открытым исходным кодом.

При реальной балансировке нагрузки необходимо учитывать еще много ситуаций:

  • Для повышения отказоустойчивости не следует размещать несколько копий раздела на одной стойке.

  • Разные ReplicaServers могут иметь разную емкость хранилища, и абсолютный баланс количества реплик может быть не очень разумным.

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

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

Согласованность и доступность

Масштабируемость была введена ранее, за ней последовали согласованность и доступность. Наши соображения по дизайну были представлены ранее:

  • Уменьшить зависимость от зоопарка

  • Данные не записываются в DFS

  • Несколько копий с использованием алгоритма PacificA для обеспечения строгой согласованности

Когда мы внедрили систему в соответствии с этими проектными целями и были готовы найти компанию для тестирования ножа, компания оттолкнула нас одним предложением: «У вас есть двухкомпьютерные залы для горячего резервирования?»

Зачем говорить это в одиночестве? Потому что для распределенной системы хранения с сильной консистентностью отказоустойчивость в компьютерных залах — более хлопотная вещь:

  • С одной стороны, если несколько копий Раздела развернуты в двух компьютерных залах, у одного компьютерного зала должно быть большинство. И общее время простоя этого машинного зала означает, что кластер неработоспособен (потому что гарантируется сильная согласованность).

  • С другой стороны, если несколько копий Partition развернуты в трех компьютерных залах, безопасность в этих компьютерных залах действительно гарантирована. Но это означает, что каждый запрос на запись должен иметь репликацию по компьютерному залу, что влияет на производительность.

Размышляя над этим вопросом, мы думаем, что на самом деле создали «идеальную систему». С точки зрения бизнеса, как полноценная система хранения, действительно необходимо справляться со всеми видами нештатных ситуаций. Однако по мере снижения вероятности нештатных ситуаций бизнес-требования к согласованности постепенно смягчаются:

Поняв потребности бизнеса, мы разработали многоуровневую стратегию резервирования для Pegasus, чтобы справиться с различными рисками:

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

  • Репликация между машинными комнатами: асинхронно реплицирует журнал каждого раздела, обе комнаты могут быть записаны на основе возможной согласованности временных меток NTP.

  • Регулярное холодное резервное копирование снэпшотов: межрегиональная репликация, итоговая стратегия в случае серьезного сбоя, некоторые данные могут быть потеряны

Несколько замечаний по стратегии резервирования выше:

  • Временные метки NTP могут быть недостаточно точными. Два компьютерных зала записывают один и тот же ключ один за другим, и конечным результатом может быть то, что первый перезапишет второй. В настоящее время у нас нет другого способа обойти это, потому что наша цель — «согласованность в конечном итоге», то есть «со временем конечный результат двух компьютерных залов будет одинаковым», поэтому даже если он будет записан первым после перезаписи , конечное состояние производительности в двух компьютерных залах одинаково.

  • Что касается нашего опыта поддержки HBase, очень маловероятно, что два компьютерных зала предприятия будут писать ключ одновременно. Таким образом, проблема «сначала записать, а затем перезаписать», которую создает NTP, требует внимания со стороны бизнеса.

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

  • Функции репликации между машинными комнатами и холодного резервного копирования моментальных снимков все еще находятся на стадии внутреннего тестирования и не были выпущены в версии с открытым исходным кодом.

Гарантия задержки

Наконец, вводится проблема гарантирования характеристик задержки. В этом вопросе следует подчеркнуть два момента:

  • Какой язык реализации выбрать

  • Как эффективно реализовать согласованный протокол

В качестве языка реализации мы выбрали C++. Причина была упомянута ранее, чтобы гарантировать производительность, мы должны использовать язык без сборщика мусора во время выполнения. Другим вариантом может быть Rust. Наши соображения о том, почему лучше выбрать C++, а не Rust, следующие:

  • Rust не был очень популярен, когда проект только создавался, а сторонние библиотеки были далеки от совершенства C++.

  • Пока соблюдаются некоторые соглашения по программированию, ориентироваться в C++ не особенно сложно.

  • Нанимать проще с C++

Конечно, этот выбор консервативен, и на этом дискуссия на уровне языка заканчивается. Основное внимание по-прежнему уделяется реализации нашего согласованного протокола.

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

На следующем рисунке показана упрощенная блок-схема полного запроса на запись:

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

Итак, как бороться с этой проблемой? Что касается нашего опыта, то это изменение организации кода с «борьбы за критические секции» на «безблокировочную сериализацию очередей». Позвольте мне сначала использовать картинку, чтобы проиллюстрировать нашу структуру кода:

В частности, он основан на «непротиворечивом состоянии» как на ядре, и все события, связанные с изменением состояния, выстраиваются в очередь, а события очереди выполняются через отдельный поток. Так называемое выполнение заключается в изменении состояния согласованности. Если ввод-вывод запускается во время выполнения, он будет выполняться чисто асинхронно, а события ответа после завершения ввода-вывода будут помещены в очередь. Как показано ниже:

Кроме того, чтобы не создавать слишком много потоков, мы используем подход пула потоков. Структура данных «непротиворечивого состояния» может быть изменена только одним потоком, но несколько структур могут совместно использовать поток; реплики и потоки имеют отношение «многие к одному».

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

Детерминированный тест

Далее мы сосредоточимся на том, как проводится тест.

Стабильность распределенных систем

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

Каковы основные аспекты этой проблемы? В заключение, есть три момента:

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

  2. Трудно воспроизвести. Поскольку ошибки появляются вероятностно, даже если проблема обнаруживается в ходе тестирования, ее нелегко воспроизвести, что еще больше затрудняет отладку.

  3. Трудно вернуться. Если вы найдете суть проблемы, просматривая журнал, наблюдая за явлением и анализируя код. Неубедительно, работает ваше исправление или нет. Этот фикс решит проблему? Не вызовет ли это новых проблем? Поскольку не существует стабильного и воспроизводимого метода, на эти два вопроса трудно ответить.

Корень: Неуверенность

Так в чем же источник вышеуказанных трудностей? Подводя итог, мы думаем, что это неопределенность самой программы. Эта неопределенность проявляется в двух аспектах:

  • Случайность самой программы: системные API, такие как планирование, таймеры, использование случайных чисел и параллелизм между несколькими узлами.

  • IO: Программа может включать IO периферийных устройств. Вероятностный сбой, тайм-аут, потеря пакетов и беспорядок ввода-вывода также приводят к неопределенности программы.

Обобщите это с помощью формулы:

Малая вероятность ошибок ввода-вывода + случайные пути выполнения = аномальные условия, которые непросто воспроизвести

Так как же нам решить эту проблему?

Поскольку воспроизвести проблему онлайн сложно, можем ли мы построить смоделированный сценарий: в этом смоделированном сценарии мы можем смоделировать вероятность ошибок ввода-вывода и контролировать порядок выполнения программы. Затем запускаем код в таком смоделированном сценарии.Если есть проблема с логикой, мы можем воспроизвести проблему в том же порядке выполнения. После изменения логики ее можно превратить в модульный тест, чтобы решить проблему сложной регрессии.

Конечно, это описание проблемы все еще очень абстрактно. Здесь я привожу простой пример для иллюстрации:

Предположим, что есть такая система учетных записей, есть два человека Алиса и Боб, а остатки на двух учетных записях составляют по 100 юаней каждая, которые хранятся на двух машинах соответственно, и обе могут инициировать транзакции перевода на другую. Теперь Алиса хочет перевести Бобу 5 юаней Простейшая реализация состоит в том, что Алиса списывает 5 юаней с баланса своего счета, а затем отправляет запрос на добавление 5 юаней на машину Боба. После того, как счет Боба повысится на 5 юаней, Алиса может быть уведомлена об успешном переводе.

Если машина не выйдет из строя, диск не выйдет из строя и сеть не выйдет из строя, тогда эта простая реализация подойдет. Но такое предположение никогда не будет верным, поэтому для решения этих проблем мы можем добавить ряд средств, чтобы сделать систему учетных записей более надежной, таких как добавление журналов транзакций, резервное копирование информации о транзакциях и учетных записях на нескольких машинах. Технология распределенных транзакций. Введение этих средств усложнит нашу систему.

Столкнувшись с такой сложной системой, любое ненормальное состояние базы данных заставит нас почесать голову, например, следующие изменения в информации об учетной записи Алисы и Боба:

(Alice_100, Bob_100) -> (Alice_105, Bob_105)

Когда в программе нет багов, можно считать, что сумма балансов счетов Алисы и Боба равна 200. Теперь сумма баланса стала 210, должно быть проблема в каком-то звене. Может быть, когда два человека переводят деньги друг другу одновременно, то срабатывает какой-то баг, может пакет данных отправляется дважды. Существует множество возможных ситуаций входа в недопустимое состояние, но вероятностный отказ оборудования и сложная (возможно, параллельная) передача записей между Алисой и Бобом затрудняют нам обнаружение и воспроизведение причины недопустимого состояния.

А для такой задачи наше оружие аналоговое и псевдослучайное. С помощью этих двух средств мы надеемся, что программа будет работать в воспроизводимой последовательности событий. Таким образом, если программа вылетает из-за того, что входит в недопустимое состояние, мы можем воспроизвести, отладить и вернуться в это состояние.

Также возьмите транзакцию между Алисой и Бобом в качестве примера. Одновременный процесс передачи между двумя сторонами может выполняться следующим образом в смоделированной среде: Алиса инициирует передачу -> Боб инициирует передачу -> Алиса инициирует запись на диск -> Алиса инициирует RPC -> Алиса выполняет RPC успешно -> Боб инициирует запись на диск -> Алиса не удалась записать на диск

другими словами:

  • Два набора логики, которые изначально выполнялись параллельно, могут стать параллельными и чередоваться в симуляторе.

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

  • Сбои ввода-вывода также вводятся с определенной псевдослучайной вероятностью.

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

контрольная неопределенность

Итак, конкретный контроль не уверен, как это сделать?

  • Интерфейсы, которые создают неопределенность, обеспечивают уровень абстракции. Текущая система предоставляет в общей сложности 5 абстрактных интерфейсов пула потоков, RPC, дискового ввода-вывода, синхронизации потоков и работы среды. Каждый интерфейс имеет фиктивную реализацию (тест) и не фиктивную реализацию (развертывание и эксплуатация), а код распределенной бизнес-логики (логика приложения) вызывает только абстрактный интерфейс (время выполнения) системы

  • Система предоставляет возможность моделирования нескольких сервисных узлов в одном процессе, чтобы добиться эффекта моделирования распределенной системы.

Имитационная реализация слоя Runtime является сложным моментом, и ключевые моменты включают в себя:

  • Предоставляется только чисто асинхронный программный интерфейс, а режим работы узла — это процесс выполнения потоком задач в очереди событий.

  • Когда бизнес-логика вызывается в модуле среды выполнения через API среды выполнения, среда выполнения приостанавливает текущий узел и выбирает новый узел для выполнения псевдослучайным образом. Весь процесс аналогичен одноядерной многозадачной операционной системе: среда выполнения похожа на режим ядра, узел распределенной логики похож на процесс пользовательского режима, а API среды выполнения похож на системный вызов. Как показано ниже:

  • Для событий ввода-вывода, отправленных в очередь событий, мы вводим ошибки псевдослучайно с определенной вероятностью. Кроме того, введены некоторые модули определения глобального состояния, чтобы определять, является ли глобальное состояние законным каждый раз, когда состояние изменяется (например, проверять, меньше ли сумма остатков на счетах Алисы и Боба 200); в то же время, код Application Logic также добавил много точек утверждения, чтобы проверить, является ли состояние узла допустимым. После сбоя программы ее можно воспроизвести и отладить с тем же начальным числом случайных чисел. Как показано ниже:

Модульное тестирование Pegasus также выполняется с использованием этой среды тестирования. В модульном тестировании мы будем использовать сценарий для описания действия, применяемого к сцене, и ожидаемого состояния.Функция средства проверки логики приложения на рисунке выше состоит в том, чтобы загрузить сценарий, а затем проверить, соответствует ли состояние программы ожиданиям в соответствии с к сценарию.

Концепция и реализация тестовой среды, используемой Pegasus, заимствованы из платформы Microsoft с открытым исходным кодом rDSN. Весь фреймворк сложнее для понимания, и вот лишь краткое описание его принципов. Если вам интересно, вы можете напрямую просмотреть исходный код.

Состояние и планы

Сейчас Pegasus стабильно работает в Xiaomi уже около года, обслуживая почти десять предприятий. Для получения дополнительной информации о механизме хранения, производительности и дизайне Pegasus вы можете перейти к другому разделу на Arch Summit 2016 (ссылки после статьи) или обратиться к нашим соответствующим документам на github.

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

напиши в конце

Наконец, резюмируйте содержание всей статьи.

Когда мы исследуем или реализуем проект, необходимо обратить внимание на три момента:

  • Сосредоточьтесь на бизнесе: отвечает ли проект потребностям бизнеса?

  • Сосредоточьтесь на архитектуре: разумна ли архитектура проекта?Для распределенных систем хранения это согласованность, доступность, масштабируемость и производительность.

  • Сосредоточьтесь на разработке программного обеспечения: завершено ли тестирование проекта, очень ли хороша ремонтопригодность кода и можно ли отслеживать проект.

Ссылки по теме
  • Адрес проекта Пегас:

    https://github.com/XiaoMi/pegasus

  • Что Pegasus поделился на Arch Summit 2016:

    http://bj2016.archsummit.com/presentation/3023

  • Адрес проекта rDSN:

    https://github.com/Microsoft/rDSN

  • Форк rDSN, поддерживаемый Pegasus:

    https://github.com/XiaoMi/rdsn

  • Ссылки на документы PacificA:

    https://www.microsoft.com/en-us/research/wp-content/uploads/2008/02/tr-2008-25.pdf

насчет нас

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

Системы, разработанные и поддерживаемые нашей командой, включают: Zookeeper, HDFS, HBase, Pegasus и такие сервисы, как FDS, SDS и EMQ, основанные на этих системах.

Если у вас есть какие-либо вопросы (не ограничиваясь техническими проблемами), пожалуйста, свяжитесь с нами. В то же время мы также приветствуем людей с высокими идеалами, которые могут присоединиться к нам в любое время.

  • Электронная почта: pegasus-help [at] xiaomi.com

  • Weibo: облачная технология Xiaomi


Привет, мы сделали приложение "Geek Time". Это продукт службы знаний в области ИТ, который включает в себя различные формы услуг знаний, такие как подписка на колонки, новости Q, горячие темы, прямые трансляции, видео и аудио.

Время гика, восстановите дух гика, улучшите техническое познание, исследуйте мир с любопытством и создайте будущее. Теперь вы вошли в магазин приложений iOS, добро пожаловать на загрузку. Версия для Android находится в разработке и скоро будет запущена, так что следите за обновлениями!