Статьи

Полевая токенизация: анализ проблемы

Недавно меня спросили о проблеме повторения имен свойств в RavenDB, поэтому давайте рассмотрим следующий документ:

 { 

     "FirstName":"John",

     "LastName":"Doe",

     "BirthDay": "1980-01-01",

     "LastLoggedInAt": "2013-08-10"

 }

В этом документе содержится 40 символов, которые посвящены исключительно именам полей, и только 28 символов, предназначенных для фактических данных. Теперь, 40 против 28 означает, что если у нас есть 1 миллион таких документов, мы теряем много места без веской причины. Конечно, база данных может добиться большего успеха, верно? Он может маркировать эти вещи, поэтому вместо хранения 40 байтов для полей он будет хранить только 16 (при условии int32 для идентификаторов токенов). А для больших документов вы бы сэкономили много места.

Я думаю, что я говорил об этом раньше, о да, вот оно. Я описал ситуацию, в которой вы вручную (или через конфигурацию на стороне клиента) изменяете документ так, чтобы он выглядел следующим образом:

{ 

  "FN":"John",

  "LN":"Doe",

  "BD": "1980-01-01",

  "LLD": "2013-08-10"

 }

Это довольно ужасно, и я объясняю, почему это плохо в посте блоба выше. Теперь для 1 миллиона документов эти 40 символов представляют около 38 МБ дискового пространства. Я не думаю, что это серьезная проблема.

Актуальный вопрос меня спросили (Джастин):

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

https://jira.mongodb.org/browse/SERVER-863

Лично я считаю, что чтение и запись 2х или более данных, тогда необходимых, не глупая проблема, она затрагивает все аспекты производительности и управления базами данных. Диск, конечно, дешевый, но опять же, если ваша база данных меньше, вы могли бы поместить ее на ssd или более быстрый, но меньший диск, или просто перенести меньше байтов с этого дешевого диска, чтобы выполнить то же самое. Вы действительно хотите сказать мне, что каждый байт не имеет значения после изучения этих баз кода c, которые делают все возможное, чтобы сохранить каждый байт по соображениям производительности?

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

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

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

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

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

И это даже не принимает во внимание, что на самом деле довольно часто иметь динамические имена свойств; например, даты и время. У вас есть 1 миллион документов, каждый из которых имеет свойство с такими именами, как: «2013-08-03T19: 57: 32.7060000Z» 

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

Продолжение на следующей странице >>

{

   "2013-08-03T19:57:32.7060000Z":{

      "Project":"RavenDB",

      "RavenDB-1248":"Fixed",

      "RavenDB-1212":"Progress made"

   }

 }

Как видите, у нас на самом деле есть несколько свойств с динамическими именами. Было бы напрасно пытаться их интернировать. Фактически, это, вероятно, заставит нас упасть и умереть. Если вам нужно подождать, пока это произойдет, когда вам придется передать это клиенту … тогда все вокруг весело и радостно.

Но вы знаете, что? Я все еще могу, вероятно, пойти и выяснить, каковы наиболее 10000 общих имен полей, и иметь фиксированную таблицу из них на месте. Это дало бы мне возможность обрабатывать такие вещи, как имя, имя, дата и т. Д. Поскольку это фиксированный список, это означает, что многие из перечисленных выше проблем не имеют значения. Это будет означать, что если вы хотите вызвать ваше свойство FirstNames, вам может потребоваться назвать его Names, потому что первый будет в списке и не будет интернирован.

Это приводит к множеству интересных потенциальных проблем с обратной и прямой совместимостью. Как добавить больше терминов в список? Старые клиенты не смогут понять токены, и это приводит к дополнительным сложностям при управлении историей версий. И я могу заверить вас, что многие люди будут требовать и добавлять свои уникальные свойства в этот список. («Что вы имеете в виду, что« PensionFundTarget »нет в списке стажеров? Это действительно распространенный термин, и мы могли бы действительно использовать повышение производительности этого!»).

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

{ 

  "231":"John",

  "432":"Doe",

  "127": "1980-01-01",

  "5841": "2013-08-10"

 }

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

Конечно, вы можете уменьшить объем данных, которые вы читаете с диска. Но эти данные уже находятся в одном месте. И нет абсолютно никакой разницы между чтением 30-байтового значения и 200-байтового значения (чтение / запись всегда выполняется с минимальным объемом 512 байт). Таким образом, чтение дополнительных байтов ничего не значит для нас. Нам не нужно к этому стремиться, оно уже есть. Кроме того, у нас есть кэши, чтобы облегчить эту проблему для нас.

Чтение с диска не является нашей главной проблемой. Это относительно дешево по сравнению с отправкой данных пользователю по сети. Но подождите, мы действительно нашли решение для этого: мы используем сжатие gzip на проводе, поддерживаемый и хорошо известный метод, который вы можете легко увидеть с помощью таких инструментов, как Fiddler. Это обычно дает вам лучшее из всех миров. Вы получаете небольшой размер ответа, вы все еще используете читаемый формат, вам не нужно решать все проблемы, упомянутые выше. Счастливой счастливой радости, радости!

Хм … мы не можем применить тот же метод внутренне на стороне сервера? Мы можем точно. Мы можем сжимать и распаковывать данные на лету, когда мы записываем / читаем на диск, еще больше сокращая объем записываемых нами данных.

В RavenDB мы делаем оба. Данные сжимаются по проводам, а также могут быть сжаты на диск.

Что касается вопроса MongoDB, то за него могут проголосовать высоко, он может быть помечен как основной, но он находится в системе отслеживания проблем в течение последних 3+ лет. Я бы сказал, что они не торопятся решать эту проблему. Вероятно, по тем же причинам, которые я изложил здесь. Дешево проголосовать за проблему, и обычно люди, определяющие серьезность проблемы, — это люди, которые ее создают. Я бы не стал ставить слишком много на что-нибудь подобное.

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

Я думаю, вы можете догадаться, что я выберу.