Использование Двоичного Поиска Для Оптимизации Запроса На Получение Данных



Введение В настоящее время очень популярна тема оптимизации работы с различными СУБД.

На многочисленных форумах идут дискуссии о «лучшей СУБД в мире», но зачастую все это превращается в голословные вопли о том, что «я познал смысл жизни и понял, что лучшее хранилище данных — это Х».

Да, конечно, сейчас мы видим активное развитие NoSQL-решений, которые позволяют нам многое.

Но эта статья не о них.

Так получилось, что я сменил работу и получил один очень интересный проект на связке php+MySQL. В нем много хороших решений, но он не был написан для большой аудитории.

За несколько лет существования количество активных пользователей стало приближаться к цифрам с 7 нулями.

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

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

Сейчас все стабильно.

Но недавно мне прислали по электронной почте новое задание.

Целью было собрать статистику.

Проанализировав требования, я понял, что для его реализации достаточно написать один-единственный запрос, выполняющий 3 INNER JOIN над таблицами, размеры которых были впечатляющими.

Каждая таблица содержала в среднем 40 миллионов записей.

Получается, что временная таблица будет состоять из 4*4*4*10^21 = 64*10^21 записей.

Это колоссальная цифра.

А загружать СУБД таким запросом для сбора статистики – непозволительная роскошь.

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

(в проекте используется СУБД MySQL, но особенностей алгоритм не имеет)



Что такое двоичный поиск

Я думаю, что многие из вас писали лабораторные работы по поиску элемента в массиве с помощью бинарного алгоритма.

Попробую кратко изложить ее суть.

Пусть у нас есть отсортированный массив н элементы: Первый элемент массива = 1 Последний элемент массива = n Нам нужно найти индекс элемента со значением ж .

Каждый шаг заключается в том, что мы вычисляем середину массива: Середина массива = раунд (первый элемент + последний элемент)/2 Затем вычисляем значение этого элемента и проверяем, больше или меньше полученное значение желаемого.

ж .

Диапазон поиска сокращается в 2 раза: Если > е, тогда Последний элемент массива = среднее значение В противном случае Первый элемент массива = среднее значение Эти шаги повторяются до тех пор, пока не произойдет одно из следующих условий:

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

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

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



Перейдем к практике

Допустим, нам нужно выполнить INNER JOIN для трех таблиц, а затем установить условие «столбец Икс в диапазоне от 10 до 20".

При этом столбец Икс не имеет индекса.

Это займет очень много времени.

Вот тут-то и приходит на помощь этот простой метод. Берем таблицу с этим самым столбцом и с помощью бинарного поиска находим по ней диапазон первичных ключей, удовлетворяющих условию 10<= Икс <=20. Considering that for such a sample we will use only indices, we will very soon get the desired pair of values. Стоит сказать, что двоичный поиск используется для поиска одного элемента, а не диапазона, но никто не мешает нам найти первый элемент со значением 10 и последний элемент со значением 20. Их первичными ключами будут ограничения по диапазону.

С этим диапазоном мы возвращаемся к нашему запросу, но теперь вместо условия ГДЕ x > = 10 И x <= 20 мы пишем ГДЕ id_x МЕЖДУ min_id_x И max_id_x , Где min_id_x И max_id_x — значение нижней и верхней границ диапазона, удовлетворяющее условию.

Что получаем: теперь делаем выборку не по определенному столбцу Икс , но по первичному ключу.

Время, потраченное на обход одной таблицы, экономится.

Аналогичную процедуру можно провести и с другими условиями в запросе.

Я не вижу смысла приводить здесь код, потому что.

код двоичного поиска можно найти в Википедии, но в самом запросе нет ничего сверхъестественного.



выводы

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

Но метод нельзя считать панацеей.

Во-первых , сложно подготовить универсальное решение для всех запросов.

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

Во-вторых , этот метод подходит не для всех решений, так как строки в таблице должны быть отсортированы в каком-то порядке.

Теги: #MySQL #базы данных #бинарные деревья #оптимизация #Алгоритмы #MySQL

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.