zergon321

Patchouli design

Oct 4th, 2020
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.58 KB | None | 0 0
  1. # Распределенный децентрализованный кэш Patchouli
  2.  
  3. ## Проблемы, которые решает данная система
  4.  
  5. Высоконагруженные системы, обрабатывающие и сохраняющие большие объемы данных, нуждаются в отказоустойчивости и распределении нагрузки. Для выполнения этих требований используются такие техники, как шардирование и репликация. Этим процедурам подвергаются микросервисы, системы управления базами данных, брокеры сообщений и системы кэширования. Среди систем кэширования есть 2 основные: **Redis** и **memcached**.
  6.  
  7. К достоинствам **Redis** можно отнести поддержку встроенных структур данных для хранения информации (списков, множеств, словарей и т.д.) и поддержку *TTL* для ключей. К сожалению, у нее также есть и существенные недостатки. К ним относятся однопоточность и сложность шардирования и репликации, нестабильность работы в кластере.
  8.  
  9. К достонствам **memcached** можно отнести то, что она легко кластеризуется и является многопоточной. Но при этом её функционал ограничен, она не поддерживает структур данных, транзакций и **TTL**. При этом решение о том, на какой ноде должно находиться значение конкретного ключа, принимает клиентская программа (или клиентская библиотека, используемая в этой программе).
  10.  
  11. При этом ни в одной из вышеупомянутых систем не поодерживается разделение данных, содержащихся под одним ключом, на несколько нод. Кроме того, ни одна из этих систем не поддерживает *пространств имен*. Таким образом, все ключи для всех клиентов хранятся в одной единой области и всегда присутствует риск того, что данные одного клиента могут быть изменены другим.
  12.  
  13. ## Фичи, которые следует реализовать
  14.  
  15. - централизованное (*master-slave*) и децентрализованное (*master-master*) шардирование;
  16.  
  17. - репликация;
  18.  
  19. - пространства имен (*namespaces*);
  20.  
  21. - *TTL* на ключи и элементы структур данных;
  22.  
  23. - структуры данных: словарь, множество, которые можно шардировать (разделить на части, которые расположить на разных нодах);
  24.  
  25. - транзакции, которые исполняют серии интсрукцию как одну атомарную инструкцию.
  26.  
  27. ## Методы доступа и протоколы
  28.  
  29. - **gRPC** API;
  30.  
  31. - **HTTP** API;
  32.  
  33. - **TCP** API.
  34.  
  35. ## Сохранение данных на диск
  36.  
  37. Система должна уметь делать дамп данных в постоянное хранилище. Он должен осуществляться по запросу или по заданному расписанию. Должна быть возможность сделать/настроить дамп для каждого отдельного ключа, содержащегося в кэше.
  38.  
  39. При дампе должен происходить либо снимок данных, содержащихся в ключе, либо его блокировка для записи. Каждому ключу кэша на диске соответствует группа файлов с определенным названием. Каждый новый дамп помещается в новый файл.
  40.  
  41. ## Реализация хранилища данных
  42.  
  43. Хранилище данных в оперативной памяти состоит из 3-ёх областей:
  44.  
  45. - область хранения ключей словарей;
  46.  
  47. - область хранения ключей неупорядоченных множеств;
  48.  
  49. - область хранения ключей, под которыми хранятся обычные атомарные значения (последовательности байтов);
  50.  
  51. Каждый ключ структуры данных и словаря имеет TTL - time to live, время жизни. Если оно отрицательно, ключ и сохранённое по нему значение никогда не удаляются из кэша. Иначе ключ находится в кэше ровно столько времени, сколько прописано в TTL.
  52.  
  53. Каждый словарь или множество состоит из определённого количества хэш-таблиц (bucket'ов), в каждой из которых хранятся ключи или элементы определённого диапазона хэшей. Каждый bucket имеет мьютекс и блокируется при конкурентном доступе к нему. Таким образом, не нужно блокировать весь словарь или множество полностью - только его часть. В какой именно bucket вставлять ключ, определяется остатком от деления хэша ключа на количество bucket'ов.
  54.  
  55. Ключ, под которым записано атомарное значение (простая байтовая последовательность), также имеет TTL и мьютекс.
  56.  
  57. ## Реализация TTL
  58.  
  59. TTL реализуется с помощью таких средств языка программирования **Go**, как функция `time.AfterFunc` и объект типа `*time.Timer`, возвращаемый ей. Как только таймер срабатывает, ключ и данные, сохранённые под ним, удаляются из кэша. Сохранение таймера необходимо, чтобы можно было отменить TTL.
  60.  
  61. Таймеры для ключей словарей и множеств хранятся в bucket'ах. Таймеры для ключей с атомарными данными хранятся в отдельной области. Элемент, для которого сработал таймер TTL, блокируется и удаляется.
  62.  
  63. ## Реализация пространств имён
  64.  
  65. Пространства имён необходимы, чтобы не возникало конфликтов при обращений к данным. К примеру, 2 разных микросервиса могут иметь одинаковые ключи и записывать данные в один и тот же кластер **Patchouli**. Чтобы не испортить данные друг друга, они должны создавать ключи в разных пространствах имён.
  66.  
  67. Внутренне пространства имён реализованы как префиксы перед ключами. Например, `"customers.counter"` - это ключ `"counter"`, находящийся в пространстве имён `"customers"`. Пользователю необязательно указывать пространство имён при каждом доступе к ключу - достаточно просто выбрать его в начале работы.
  68.  
  69. ## Реализация шардирования и репликации
  70.  
  71. Шард - это группа узлов **Patchouli**, отвечающая за хранение определённой части данных единой коллекции. Так, например, если у нас есть множество элементов `{1, 2, 3, 4, 5, 6, 7, 8}` и кластер **Patchouli** из двух шардов, то на одном шарде могут располагаться элементы `{1, 3, 6, 8}`, а на другом - `{2, 4, 5, 7}`. Шарды необходимы для распределения нагрузки в кластере.
  72.  
  73. Шарды состоят из реплик. Реплика - это узел **Patchouli**, входящий в шард. У каждой реплики в рамках одного шарда данные одинаковые. Репликация необходима для обеспечения отказоустойчивости. Если шард состоит из нескольких реплик, то падение одной реплики не приведёт к отказу всего шарда.
  74.  
  75. Если части коллекции (множества или словаря) распределены по шардам, то необходимо каким-то образом обращаться к этим частям как к единой коллекции. Для этого вводятся т.н. мастер-серверы. Мастер-сервер отличается от других тем, что в нём записана конфигурация кластера и адреса подчинённых узлов. Как только на мастер-сервер приходит запрос, связанный с шардированной коллекций, мастер-сервер находит именно тот шард, на котором находятся нужные данные, и осуществляет работу с ними.
  76.  
  77. Конфигурирование мастер-сервера происходит либо через конфигурационный **XML**-файл, либо через переменные окружения.
  78.  
  79. ## Реализация транзакций
  80.  
  81. Транзакция - это серия инструкций, которая исполняется как одна единая, атомарная (в рамках паралеллизма) инструкция. Транзакции позволяют изменять данные в нескольких областях с сохранением целостности.
  82.  
  83. Как только пользователь инициирует транзакцию, все ключи и bucket'ы, затрагиваемые ей, блокируются, и после выполнения инструкций происходит разблокировка. Транзакцию можно выполнять как на мастер-сервере, так и на подчинённом сервере, но в последнем случае не будут учитываться данные на других шардах.
Add Comment
Please, Sign In to add comment