Теперь мы представляем коллекцию экспертных правил, которые могут помочь проектировщику выбрать или изобрести метод доступа в зависимости от рабочей нагрузки и доступных ресурсов. Мы обнаружили, что наиболее важными являются три фактора: (1) наличие запросов на дальность, (2) важность характеристик модификации и (3) допустимость усиления пространства.
1.
Преобладающий диапазон: если
запросы с диапазоном преобладают, используйте сортировку, хеширование с сохранением порядка или организацию данных с разбиением на диапазоны/радиксы (раздел 3.1).
2.
Только точки: если в рабочей нагрузке присутствуют только точечные запросы, то хэш-партизация часто будет быстрее, чем сортированная или разбитая на диапазоны глобальная организация данных (раздел 3.1), а хэшированная локальная организация данных будет предпочтительнее сортированной.
3.
Выборочные запросы: если точечные или диапазонные запросы являются выборочными, то есть обращаются к небольшой части записей, то полезными будут индексирование (как описано в разделе 3.2) и глобальное разбиение данных (раздел 3.3).
- Малая оперативная память: если индекс не помещается целиком в оперативной памяти и используется индексирование, то для частей индекса, находящихся на дисковом носителе, необходимо обеспечить высокий fan-out. Поэтому B+-дерево или хэш-индекс предпочтительнее структуры с низким разветвлением на выходе, например двоичного дерева поиска (разделы 3.3.3 и 3.3.4).
4.
Время поиска, не зависящее от размера: когда размер данных увеличивается и мы хотим отвязать время поиска от размера данных, мы можем использовать прямую адресацию (раздел 3.2.3) в виде хэш-индексации (раздел 3.3.4), если наша рабочая нагрузка содержит только точечные запросы, или индексацию на основе радикса, когда базовые данные отсортированы и организованы на основе радиксного разбиения (раздел 3.3.5). Радиксные деревья (раздел 3.3.5) особенно полезны, когда индексируемые данные сильно перекошены, поскольку само дерево может занимать меньше места, чем в случае, когда ключи распределены по пространству ключей более равномерно.
5.
Частота модификаций: если вставки/удаления происходят часто, то мы различаем умеренный и интенсивный трафик модификаций. В частности, при интенсивном трафике модификаций мы можем захотеть использовать логирование, разделенное логирование или хэширование на глобальном уровне (раздел 3.1). Если трафик модификаций умеренный, то на глобальном уровне может быть больше гибкости (например, может подойти стиль B
+-дерева), а на локальном уровне может использоваться хэшированная или логированная организация локальных данных (раздел 3.4). Также отметим следующие положения:
- Пакетная обработка может заменить локальное логирование: проект, использующий локальную организацию данных с логированием для поддержки трафика модификаций, может вместо этого использовать высокоорганизованную (например, отсортированную) локальную организацию данных, а затем выполнять пакетную обработку модификаций либо глобально (раздел 3.7.2), либо локально (раздел 3.7.3).
- Перекрывающиеся разделы требуют фильтров: в проекте, использующем разделенное логирование в качестве глобальной организации данных, следует также использовать легкую индексацию фильтров (например, фильтры Блума), чтобы устранить по крайней мере некоторые ненужные обращения к страницам (раздел 3.3.2).
6.
Всплески запросов: если возникают всплески запросов на чтение, их объединение в пакет позволяет сократить избыточные обращения к данным. Выгода максимальна, когда запросы группируются вместе на основе локальности ключа, т. е. путем объединения запросов, направленных на один и тот же раздел (или группу разделов). Пакетирование запросов на чтение увеличивает общую пропускную способность, но также потенциально увеличивает время отклика отдельных операций (раздел 3.7), которые должны ждать, пока их пакет будет обработан.
7.
Всплески обновлений: если в отсортированных данных происходят всплески обновлений, то для снижения общих затрат на реорганизацию следует использовать пакетную обработку обновлений (раздел 3.7.2).
8.
Только сканирование: если запросы представляют собой полное сканирование или запросы с большим диапазоном, то индексирование не требуется (раздел 3.2.1 и раздел 6.1.2). Сканирование больших диапазонов можно оптимизировать с помощью зонных карт, фильтров Блума и других форм индексирования фильтров (раздел 3.3.2).
9.
Сканирование с логированием: если запросы представляют собой полное сканирование или запросы с большим диапазоном и основаны на времени вставки, то следует использовать логирование по времени (раздел 3.1). Также следует использовать индексирование фильтров (например, зонные карты в данном случае) (раздел 3.3.2).
10.
Локальность против гибкости: решение о хранении ключей-значений ортогонально другим размерностям. Если главной целью является локальность данных, ключи и значения должны храниться вместе. С другой стороны, при построении индекса, обычно вторичного, на уже организованном наборе записей, каждый ключ индекса должен быть сопряжен с указателем на запись или идентификатором строки, а не дублировать записи. Кроме того, если в рабочей нагрузке происходят обновления, которые могут сильно повлиять на размер значений, то ключи и значения следует хранить отдельно, чтобы избежать реорганизации движения данных, связанной с этими обновлениями.
В следующей главе мы обсудим, как использовать эти правила как для выбора между существующими проектами для заданной рабочей нагрузки, так и для предложения новых проектов, если ни один из существующих проектов не удовлетворяет всем требованиям.