LINUX.ORG.RU

B+-Tree: как там эффективнее всего решают задачу «дай число ключей от A до B»?

 


0

1

Как мировые лидеры-имплементаторы B+-Tree (авторы всяких там движков InnoDB или Postgres) успешнее всего решали задачи «дай число ключей в дереве между A и B»?

Могу сходу напридумывать способов облегчать такие запросы, простейший: в каждом PIVOT хранить число ключей во всём поддереве, на которое он показывает. Тогда находим просто 2 итератора на lower_bound(A) и upper_bound(B) и суммируем все такие чиселки на всех PIVOT между ними. Учитывая, что не обязательно ходить по PIVOT-ам в блоках уровня 1 (0 означает LEAF-блок), а можно часто пройтись и по блокам выше, суммируя числа крупнее (критерий: блок верхнего уровня покрывает весь диапазон блоков уровня ниже, по которым мы собирались пойти), то всё можно сделать быстро. Физически это будет выглядеть примерно так: сходили вглубь до LEAF для A, сходили вглубь до LEAF до B, потом поднялись-собрали, собрали по крыше, опустились-собрали. Трапеция такая. Ну вы поняли. Но недостаток тут в том, что тебе надо на каждый инсерт ключа инкрементить чиселки на всем пути выше, по которому ты пришёл в LEAF и в целом больше блоков в дереве будет грязными из-за этого, хотя наверное пофиг.

Это к «задаче про лайки». Допустим я решил хранить число лайков в комменту в какой-то наивной реализации B+-Tree. Коммент - это uint64 (id сущности, которую лайкнули - docid), лайк - тоже uint64 (кто лайкнул - userid). Инсертим в дерево ключ K = uint128 (конкатенация двух ID выше - (docid << 64 + userid)). Такой ключ может быть только один. Всё хорошо. Медленно - это достать число лайков к нашему docid. Для этого надо достать число ключей в подинтервале [ID:0, ID+1:0), что при паре лямов лайков к какому-то посту не-больно только при каком-то ненаивном подходе к этому, типа описанного выше.

Вопрос: чё там в постгресах-мускулях понаверчено физически в реальности для оптимизации такого?

Как хранить лайки иначе спрашивать не буду, и так знаю - во всяких фейсбуках просто пишут свою микро-субд, представляющую собой логически std::map< uint64_t, std::set<uint64_t> > и кайфуют. Вопрос был именно про кейс, когда Вася решил докопаться именно до B+-Tree для такой задачи. Мало ли где ещё и для чего кто-то хочется число ключей в подинтервале.



Последнее исправление: kilokolyan (всего исправлений: 4)

Почему комментов нет?

anonymous
()

а какая практическая цель знать это? То есть разве дерево как-то упорядочено растёт у тебя, что будет практическая польза от знания расстояния этого?

xmikex ★★★★
()
Последнее исправление: xmikex (всего исправлений: 1)
Ответ на: комментарий от xmikex

То есть разве дерево как-то упорядочено растёт у тебя, что будет практическая польза от знания расстояния этого?

TC мудровато запихал в б-дерево отношения много к одному… и теперь не знает как это оттуда достать.

…да пусть пихнет не в б дерево, а еще куда.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

успешнее всего решали задачи «дай число ключей в дереве между A и B»?

Как, как. Там тупо упорядоченный список есть отдельно от дерева. Находим A по дереву, дальше по списку до B.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от no-such-file

У ТС оказывается чат есть.
Но там какой-то бред табуна гнедых жеребцов и сивых кобыл …

anonymous
()
Ответ на: комментарий от no-such-file

Тупо упорядоченный список - он есть в виде списка листовых блоков. По нему действительно можно обойти все ключи по порядку и быстро. Но если у меня лайков пара лямов, а размер блока килобайт 64, то я буду дофига блоков на каждый запрос перемалывать. Особенно учитывая, что в каждом разное число записей и надо туда заглядуть спросить.

kilokolyan
() автор топика
Ответ на: комментарий от xmikex

а какая практическая цель знать это? То есть разве дерево как-то упорядочено растёт у тебя, что будет практическая польза от знания расстояния этого?

Практическая польза - ответить на запрос get_likes_count() чтобы циферку у сердечка нарисовать. Ну да, оно упорядоченно растёт.

kilokolyan
() автор топика
Последнее исправление: kilokolyan (всего исправлений: 1)
Ответ на: комментарий от kilokolyan

Но если у меня лайков пара лямов, а размер блока килобайт 64, то я буду дофига блоков на каждый запрос перемалывать.

перемалывать то зачем? список со счетчиком элементов.

alysnix ★★★
()
Ответ на: комментарий от alysnix

перемалывать то зачем? список со счетчиком элементов.

Ну это уже не B+-Tree. Или как запихать список со счётчиком элементов туда?

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

Или как запихать список со счётчиком элементов туда?

первый или последний элемент списка - счетчик. как удобней.

ааа… или как запихать список в btree? ну уж не знаю что за хранилища там у вас. они списки умеют хранить?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от kilokolyan

Ну это уже не B+-Tree

Как бы уже наличие списка намекает, что это не совсем дерево. Но если тебе нужен счётчик, ты же можешь заранее считать, а данные A:B=>count это уже таки дерево. БД не парятся таким, подразумевается, что ты не идиот, и не будешь на каждый запрос делать выборку миллиона лайков только чтобы посчитать, а будешь хранить сам счётчик.

no-such-file ★★★★★
()
Ответ на: комментарий от kilokolyan

Тупо упорядоченный список - он есть в виде списка листовых блоков

Я не про тот список, а про тот который список всех элементов индекса. Или ты думал индекс через дерево перебирается?

no-such-file ★★★★★
()
Ответ на: комментарий от kilokolyan

ну а какая польза от хранения лайков в виде дерева такого? вот допустим тебе надо будет отобразить отлайканные конкретным пользователем посты - тебе такое дерево поможет быстро их найти?

xmikex ★★★★
()
Ответ на: комментарий от xmikex

ну а какая польза от хранения лайков в виде дерева такого?

Тут задача решалась от телеги впереди лошади: B+-Tree уже было, захотелось хранить лайки. Каждый лайк - это ключ, егг вполне себе годно хранить в дереве (ключ, т.к. мы постоянно хотим проверять лайкнут ли данный пост уже). Вот осталось понять как быстро доставать чичло ключей вообще и как для этого надо поменять дерево.

kilokolyan
() автор топика
Ответ на: комментарий от no-such-file

Я не про тот список, а про тот который список всех элементов индекса. Или ты думал индекс через дерево перебирается?

Я наверное потерял нить, но вообще ты можешь перебрать все ключи в дереве тупо проходом по дереву…

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

а для чего оно уже было?
Я думал обычно под задачу выбирают тип данных, а у тебя идея сделать наоборот.

xmikex ★★★★
()
Ответ на: комментарий от kilokolyan

вообще ты можешь перебрать все ключи в дереве тупо проходом по дереву

Можно ещё чесать правое ухо левой пяткой. Но неудобно. Да и в чём тогда твой вопрос-то? Иди перебирай ключи и считай, раз нет проблем.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от kilokolyan

а зачем вообще хранить пару пост-юзер? посту не все ли равно, кто лайкнул?

иначе один условный пост условного илона маска типа - «Cool!», соберет 10 млн лайков, и с 10 млн пар пост-юзер, это будет занимать под сотню мегабайт в хранилище.

alysnix ★★★
()
Ответ на: комментарий от xmikex

а для чего оно уже было? Я думал обычно под задачу выбирают тип данных, а у тебя идея сделать наоборот.

В нём много чё хранилось, ибо оно мегауниверсально и дружественно к диску.

kilokolyan
() автор топика
Ответ на: комментарий от alysnix

а зачем вообще хранить пару пост-юзер? посту не все ли равно, кто лайкнул?

Не всё равно, ибо лайкаемый пост не должен позволять одному и тому же телу лайкнуть себя дважды. Да, оно и должно занимать под сотню мегабайт в хранилище. Ну, точнее 10М * sizeof(uint64) в оптимальнейшем случае, ну да, сотня почти - 80 мегов.

kilokolyan
() автор топика
Ответ на: комментарий от no-such-file

Можно ещё чесать правое ухо левой пяткой. Но неудобно.

Удобно. Нижний слой блоков (листовых блоков) B+-Tree обычно линкуют в список, поэтому встав на ключ «AAA» ты удобно и с максимально возможной в природе скоростью доходишь последовательно до ZZZ в порядке сортировки. С этим проблем как раз нет. Но только хочется быстрее понять, сколько ключей между AAA и ZZZ и это был вопрос треда.

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

ну скажи как ты будешь искать все лайки одного пользователя при том, что у тебя ключ - это соединение id документа с id пользователя? Оно у тебя будет упорядочено по id документов всё, как будешь искать все лайки одного пользователя?

xmikex ★★★★
()
Ответ на: комментарий от xmikex

ну скажи как ты будешь искать все лайки одного пользователя при том, что у тебя ключ - это соединение id документа с id пользователя? Оно у тебя будет упорядочено по id документов всё, как будешь искать все лайки одного пользователя?

Мне не нужны «все лайки одного пользователя» нигде практически. Если встанет задача «дай все посты, которые он лайкал», то это будет отдельный какой-то индекс вида uid:set_of_posts

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

тогда почему не использовать также
docid:set_of_users?

xmikex ★★★★
()
Ответ на: комментарий от kilokolyan

поэтому встав на ключ «AAA» ты удобно и с максимально возможной в природе скоростью доходишь последовательно до ZZZ

«С максимально возможной» - ой-ой-ой. Там referential locality на нуле практически наверняка… Могу только пожелать удачи ;)

bugfixer ★★★★★
()
Ответ на: комментарий от kilokolyan

хочется быстрее понять, сколько ключей между AAA и ZZZ и это был вопрос треда

Это никак не понять быстрее чем O(n), что за тупой вопрос?

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Это никак не понять быстрее чем O(n), что за тупой вопрос?

Задача однозначно O(1), «K» может быть разным. Это после одного look-up стоимостью O(1) если hash хороший очевидно. Но я устал, и как здесь принято говорить «баиньки хоца». Out.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Это после одного look-up стоимостью O(1) если hash хороший очевидно

Дядя, а где ты возьмёшь значение которое нужно поместить в хэш? ТС не хочет хранить счётчик, он хочет каждый раз считать.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Дядя, а где ты возьмёшь значение которое нужно поместить в хэш? ТС не хочет хранить счётчик, он хочет каждый раз считать.

«Сынок», выше он упоминал что в 128 бит он уже упаковался, а то и в 64 (ломает конкретику искать).

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от no-such-file

ТС не хочет хранить счётчик

Хочет / не хочет… А всё равно придётся. Ну или O(N). Кактус отобрать не могу.

bugfixer ★★★★★
()
Ответ на: комментарий от no-such-file

Иди проспись, откуда ты это взял?

Спасибо за заботу. Ухожу. Взял из описания проблемы:

Как хранить лайки иначе спрашивать не буду, и так знаю - во всяких фейсбуках просто пишут свою микро-субд, представляющую собой логически std::map< uint64_t, std::set<uint64_t> >

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Взял из описания проблемы: std::map< uint64_t, std::set<uint64_t> >

Коммент - это uint64 (id сущности, которую лайкнули - docid), лайк - тоже uint64 (кто лайкнул - userid)

Каким боком это к подсчёту ключей вообще и где тут O(1) для подсчёта ключей (или значений) в частности? Протупил, так и скажи.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от no-such-file

Каким боком это к подсчёту ключей вообще и где тут O(1) для подсчёта ключей (или значений) в частности?

Один из моих преподавателей в своё время имел привычку говорить «да Вы батенька - дурачёк, ду-ра-чёк»

Протупил, так и скажи.

Да куда уж нам, притуплённым то…

bugfixer ★★★★★
()
Последнее исправление: bugfixer (всего исправлений: 2)
Ответ на: комментарий от no-such-file

Каким боком это к подсчёту ключей вообще и где тут O(1) для подсчёта ключей (или значений) в частности? Протупил, так и скажи.

Я не понял твой вопрос.

kilokolyan
() автор топика
Ответ на: комментарий от bugfixer

«Сынок», выше он упоминал что в 128 бит он уже упаковался, а то и в 64 (ломает конкретику искать).

Не упаковался. Данные о лайках к посту всё ещё представляют собой пару лямов ключей docid:uid

kilokolyan
() автор топика
Ответ на: комментарий от bugfixer

«С максимально возможной» - ой-ой-ой. Там referential locality на нуле практически наверняка… Могу только пожелать удачи ;)

оптимизация: блоки имеют рядом идущие id и все влезли в озу, т.е. впределе это непрерывный кусок памяти

kilokolyan
() автор топика
Ответ на: комментарий от no-such-file

Это никак не понять быстрее чем O(n), что за тупой вопрос?

Ну, понять. Первый же вариант решения (про трапецию) в самом первопосте работает быстрее. Давай ты уже уйдёшь.

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

Первый же вариант решения (про трапецию) в самом первопосте работает быстрее

Быстрее чем что? Тупо хранить счётчик максимально быстро и просто. А твоя «трапеция» это говно которое имеет все недостатки счётчика и перебора одновременно. Давай ты уже перестанешь умничать.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Тупо хранить счётчик максимально быстро и просто.

Ну вперёд, херачь решение как в B+-Tree засунуть подобный счётчик. Пойду пока чайку пару цистерн набуровлю.

kilokolyan
() автор топика
Последнее исправление: kilokolyan (всего исправлений: 2)
Ответ на: комментарий от kilokolyan

оптимизация: блоки имеют рядом идущие id и все влезли в озу, т.е. впределе это непрерывный кусок памяти

Не получится. Гулять по памяти будете взад-вперед по любому. Кмк гораздо правильнее hash_map с ключём кого лайкают в поинтеры на set’ы (или hash_set’ы) кто лайкает. И будет Вам счастье.

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

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

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

В B+-Tree листья в списке именно для последовательного их обхода с целью достать большой диапазон клбчей в порядке сортировки.

kilokolyan
() автор топика
Ответ на: комментарий от kilokolyan

В B+-Tree листья в списке

А, ну да, я и забыл что Вы любитель всё свое писать и думал о структуре как std::map. Только всё равно использовать compound docid:uid keys в Вашей задаче неэффективно, рано или поздно Вы к этому придёте.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Только всё равно использовать compound docid:uid keys в Вашей задаче неэффективно, рано или поздно Вы к этому придёте.

Более производителтная альтернатива в треде всё ещё не предложена.

kilokolyan
() автор топика
Последнее исправление: kilokolyan (всего исправлений: 3)
Ответ на: комментарий от bugfixer

любитель всё свое писать

list из блоков нижнего уровня в b+tree - явление почти обязательное в мире b+-tree, кто бы эту структуру не писал. Не понял при чём тут что-то своё.

kilokolyan
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.