Введение

Протокол для распределенной хеш-таблицы

В вычислительной технике Chord — это протокол и алгоритм для одноранговой распределенной хеш-таблицы. Распределенная хеш-таблица хранит пары «ключ-значение», присваивая ключи различным компьютерам (известным как «узлы»); узел хранит значения для всех ключей, за которые он отвечает. Chord определяет, как ключи присваиваются узлам, и как узел может найти значение для заданного ключа, сначала определив узел, ответственный за этот ключ. Chord является одним из четырех оригинальных протоколов распределенных хеш-таблиц, наряду с CAN, Tapestry и Pastry. Он был представлен в 2001 году Ионом Стойкой, Робертом Моррисом, Дэвидом Каргером, Франсом Каашоком и Хари Балакришнаном и разработан в MIT. В 2001 году Памела Заве провела исследование, которое показало, что оригинальный алгоритм Chord (как описано в статье SIGCOMM 2001 года, статье PODC 2002 года и статье TON 2003 года) может неправильно упорядочить кольцо, создать несколько колец и нарушить целостность кольца.

Обзор

Узлы и ключи получают битовый идентификатор с использованием консистентного хэширования. Алгоритм SHA 1 является базовой хэш-функцией для консистентного хэширования. Консистентное хэширование является неотъемлемой частью надежности и производительности Chord, поскольку как ключи, так и узлы (фактически, их IP-адреса) равномерно распределены в одном и том же идентификационном пространстве с пренебрежимо малой вероятностью коллизии. Таким образом, это также позволяет узлам присоединяться и покидать сеть без прерываний. В протоколе термин "узел" используется для обозначения как самого узла, так и его идентификатора (ID) без двусмысленности. То же самое относится и к термину "ключ". Используя протокол поиска Chord, узлы и ключи располагаются в идентификационном круге, содержащем не более узлов, в диапазоне от до ( должен быть достаточно большим, чтобы избежать коллизий). Некоторые из этих узлов будут соответствовать машинам или ключам, а другие (большинство) будут пустыми. Каждый узел имеет преемника и предшественника. Преемником узла является следующий узел в идентификационном круге по часовой стрелке, а предшественник – против часовой стрелки. Если для каждого возможного ID существует узел, то преемником узла 0 будет узел 1, а предшественником узла 0 – узел ; однако обычно в последовательности есть "пропуски". Например, преемником узла 153 может быть узел 167 (а узлов с 154 по 166 не существует); в этом случае предшественником узла 167 будет узел 153. Концепция преемника применима и к ключам. Преемником ключа является первый узел, чей ID равен или следует за в идентификационном круге, обозначаемый как . Каждый ключ назначается (хранится) своему преемнику, поэтому поиск ключа – это запрос к . Поскольку преемник (или предшественник) узла может исчезнуть из сети (из-за сбоя или выхода из сети), каждый узел хранит информацию об участке из узлов, в середине которого он находится, то есть список из узлов, предшествующих ему, и узлов, следующих за ним. Этот список обеспечивает высокую вероятность того, что узел сможет правильно определить местоположение своего преемника или предшественника, даже если сеть подвержена высокой частоте отказов.

Основной запрос

Основное применение протокола Chord — запрос ключа от клиента (как правило, также узла), то есть поиск значения, соответствующего этому ключу. Основной подход заключается в передаче запроса преемнику узла, если ключ не найден локально. Это приводит к времени запроса, пропорциональному log(N), где N — количество узлов в кольце.

Стол с пальцами

Чтобы избежать линейного поиска, описанного выше, Chord реализует более быстрый метод поиска, требуя от каждого узла хранить таблицу пальцев, содержащую до *m* записей, где *m* – количество бит в хэш-ключе. Первая запись в таблице пальцев фактически является непосредственным преемником узла (поэтому дополнительное поле для преемника не требуется). Каждый раз, когда узел хочет найти ключ *k*, он передает запрос ближайшему преемнику или предшественнику (в зависимости от таблицы пальцев) узла *k* в своей таблице пальцев (то есть узлу с наибольшим ID, меньшим, чем ID ключа *k*), пока узел не выяснит, что ключ хранится у его непосредственного преемника. Благодаря такой таблице пальцев, количество узлов, с которыми необходимо связаться для поиска преемника в сети из *N* узлов, составляет (см. доказательство ниже).