LINUX.ORG.RU

Структура данных «список» для блочных хранилищ. Страницы. DBMS.

 


1

2

Дратути, меня зовут модест...

Есть хранилище: большой файл, разделённый на страницы (4...16Кб).

В хранилище лежит 100500 объектов переменной длины: в каждой странице 100...300 штук, в зависимости от их длины. Объекты хранятся в порядке их добавления в хранилище. Кончается текущая страница, открывается новая (в старой ставится ссылка на новую) и т.п. Страница пишется атомарно благодаря double-write-buffer как в InnoDB.

Разыскивается алгоритм и структура данных, которая бы позволяла сохранить информацию о порядке следования (списке) объектов. От структуры данных требуются операции:

1. Выдать N объектов с заданного смещения в списке
2. Атомарно переставить любой объект из его текущей позиции в начало списка (типа «поднятие топика»)
3. Операцию 2 выполнить в минимальное количество записей страниц, в деале за 1. У меня есть решение для 4 записей страниц на такую операцию, при этом между записями каждой страницы можно упасть и хранилище останется консистентным, но с учётом double write buffer реальных записей уже 8, а это 128 КБ записи на диск: дохрена для примитивной операции )

B-tree знаю как работает, но оно мне не нужно, я не хочу полноценное удаление-добавление-по-ключу, хочется проще и дешевле, не хочется полу-заполненных страниц, сплитов, мёрджей... Допустимо хранение всего индекса в памяти (но не самих объектов).

Варианты:
1. В каждый объект запилить поле «timestamp» (свежесть). Когда объект надо поднять в начало списка, то делаем touch этому timestamp. Это позволит на каждое поднятие объекта перезаписывать только ту страницу, где лежит этот объект. Недостаток: при холодном старте хранилища придётся прочитать ВСЕ объекты, чтобы построить индекс в памяти (дерево) по ключу timestamp. Хотя можно хранить массив timestamp объектов отдельно от самих объектов, в параллельном «потоке» страниц. Тогда при подъёме нужно читать меньше страниц.

2. Заюзать журнал и ссылки в объектах (двусвязный интрузивный список объектов). При поднятии объекта в начало списка, кидать в журнал команду, которая меняет ссылки между объектами. Вместо операции на реальных страницах просто кидать команду в журнал, таким образом пишется только 1 страница (страница журнала). Когда журнал набирает критическую массу, он накатывается на реальные страницы (но тут наступает необходимость перезаписать много разных страниц, хотя если список сделать не интрузивный, то есть prev-next (указатели) не хранить внутри самих объектов, а в отдельном «потоке страниц», то многие модификации указателей будут попадать на одни и те же страницы, в которых лежат эти указатели, поэтому количество страниц с указателями надо будет записать сильно меньше, чем самих указателей...).

Кастую DBMS-девелоперов и коммитчиков патчей в мускуль.

★☆

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

его текущей позиции в начало списка

список(в этом месте) глобальный(a) или в пределах страницы(b)?

если

a: то «в деале за 1» возможно для топиков которые и так в головной странице.

самое простое это «удаление»(элемента из текущего положение) и создание новой головной - правда у тя получается страница лишь с одной записью :)

b:

пусть у тебя на каждой странице в голове индекс-набор смещений(массив указателей) а элементы(с указателями на предыдущий логически) на страницу кладутся в обратном порядке индексу. (т.е навстречу) - тогда перестановка в пределах страницы на верх это обмен указателей в индексе и перед этим правка в списки поля «pred»

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

Глобальный список, содержащий все 100500 объектов.

kiverattes ★☆
() автор топика

Это позволит на каждое поднятие объекта перезаписывать только ту страницу, где лежит этот объект.

Таймстамп можно хранить прямо в индексе. Но т.к. индекс в виде какого-то дерева будет в памяти, то на каждый апдейт придётся писать записть в лог наката + периодически сбрасывать индекс на диск. В принципе это не более 2х записей на диск.

И таки это дерево всё равно будет B+ tree.

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

B-tree знаю как работает, но оно мне не нужно, я не хочу полноценное удаление-добавление-по-ключу, хочется проще и дешевле

распиши какие операции тебе от b-tree излишни

и какие aveO для нужных тебе операций тебе приемлемы.

у тя ща вырисовывается эдакая персистентная(те которые версионные)файловая система

qulinxao ★★☆
()

взять обычный ext2/ext3 с inodesize=размер_вашей_страницы, добавить fcntl «inode_move»..profit!!

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

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

Мне не нужна, например, операция вставки ключа/удаления ключа — это просто избыточно (в ходе чего происходят всякие там сплиты страниц и т.п.)

Сделать желаемое на B-Tree: ключом бы был timestamp изменения объекта. Поднятие из списка наверх: удаление старого ключа-timestamp и вставка этого же объекта с новым ключом-timestamp. Хотя пришлось бы прикручивать что-нибудь, что позволило бы быстро пропускать N первых элементов, а выгребать список я бы стал пробеганием по индексам от больших к меньшим. Но избыточно всё это ради только того, чтобы просто переставить элемент из списка наверх.

B-Tree хорошо, когда весь индекс в память положить нельзя и когда у тебя есть вставки рандомных ключей, да и удаления из B-Tree не особо приветствуются, дыры появляются... В моём случае типичной операцией будет удаление старых ключей и добавление ключа, который больше всех остальных. Это какой-то однобокий паттерн использования B-Tree будет... Он приведёт к тому, что будет много дыр в страницах и постоянно будут выделяться новые страницы. В результате будет такое разряженое-дырявое дерево постоянно растущее на диске...

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

Если я могу держать всё дерево в памяти, мне не нужно, чтобы оно было «B».

Оно должно быть B+, а не B. Нужно это чтобы можно было писать только изменённую часть дерева на диск. Конечно, если объектов будет всего 100500, т.е. само дерево займёт не более пары десятков MiB, то можно какое угодно дерево использовать и дампить его всегда целиком.

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

Хотелось бы не просто задачу решить, а поковыряться в алгоритмах: мне тогда надо ext2 изучать изнутри )

прям даже не знаю что и сказать.. :)

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

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

Как я могу поковыряться в алгоритмах «не изучая чего-либо»?

не знаю, может в фантазиях?

вы-же шарахаетесь от ext2/3..тут предлагают решение на B-Tree - дык это dtrfs и устроен он сходным образом и исходник у него там-же :) тоже видимо не подходит..

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

Ну B+, да. Удобно записывать только изменившиеся страницы, да. Но для моей операции мне понадобится выдернуть ключ и вставить новый ключ, причём возможно с выделенеим новых страниц. Это будет запись минимум 2 страниц — ту, из которой ключ убили + ту, куда добавили. А ещё может быть, что нужно выделить ещё одну.

Неприятно в этих B(+)-деревьях то, что у меня паттерн их использования специфический — идут пары «удалить+вставить». Дерево будет дырявое и потребляющее новые страницы (если ключом будет timestamp изменения объекта). Хотя казалось бы, что задача звучит просто: список, выдернуть, положить наверх. Хочется поискать что-нибудь проще B+-tree.

kiverattes ★☆
() автор топика
Ответ на: комментарий от MKuznetsov

Я не шарахаюсь, я просто не знаю как она устроена (ext2), только в общих чертах... Мне наверняка многое оттуда не нужно, но понять всё равно интересно...

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

Но для моей операции мне понадобится выдернуть ключ и вставить новый ключ, причём возможно с выделенеим новых страниц. Это будет запись минимум 2 страниц — ту, из которой ключ убили + ту, куда добавили. А ещё может быть, что нужно выделить ещё одну.

Это всё

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

Дерево будет дырявое и потребляющее новые страницы (если ключом будет timestamp изменения объекта).

Дырявость всегда ограничена сверху, оно не может вырасти за вполне определённые размеры. У тебя же дырявость и так имеет мето быть из-за вытаскивания некоторых объектов и эта дырявость будет отбирать значительно больше места. Вроде эта проблема более важная чем организация индекса.

Хотя казалось бы, что задача звучит просто: список, выдернуть, положить наверх. Хочется поискать что-нибудь проще B+-tree.

Отдельный список проще. Если не нужно чтобы первый пункт отрабатывал быстрее чем за O(skip + N), то это самое простое решение. Его тоже можно сделать на страницах и дампить только грязные.

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

Мне наверняка многое оттуда не нужно,

тебе оттуда (из ФС) нужно практически всё - ты же по факту и хочешь её сделать. Там в общем-то ничего сложного нет, и документации/описаний/практики на простора Интернета много.

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

Одно мысль: у меня дыр не будет, ибо я могу только ссылки друг на друга перезаписывать. Условно говоря, в объекте 4 байта ворочать и ничего не двигать никуда. Это про дырки. Про остальную часть твоего каммента ещё думать буду.

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

только если у тя интенсивно будут тачаться разные обьекты у тя будет настолько рэндом обращения при последовательном_логически чтение что :(

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

Одно мысль: у меня дыр не будет, ибо я могу только ссылки друг на друга перезаписывать. Условно говоря, в объекте 4 байта ворочать и ничего не двигать никуда. Это про дырки.

Есть страница с записями которых может быть до 300 на каждой. В какой-то момент времени решаешь пару записей из этой страницы подвинуть вперёд, а чуть позже другие записи на странице экспайрятся. Итого, получается недозаполненная страница с всего несколькими записями. Это и есть дырка. Дальше нужно как-то хранить список недозаполненных страниц, в общем делать специальную логику для доутилизации места на них.

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

Записи в страницах не двигаются, не удаляются. ID последовательные.

Есть отдельная «ссылочная страница», разбитая на 4-байтные ячейки. Ячейка номер 717 содержит номер записи, на которую ссылается запись 717. Если надо поменять порядок следования записей, меняется только виртуальный порядок путём записи в соответствующую ячейку на «ссылочной странице новой ссылки. Сами записи никто никогда не трогает.

При подъёме базы, нужно прочитать только эти ссылочные страницы, чтобы построить представление о том, какая запись на какую ссылается. Ссылочных страниц в 100500 раз меньше, чем страниц с записями, поэтому подъём дешёвый, а изменение кучи ссылок — это перезапись всего нескольких страниц.

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

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

Ты же уже понимаешь, что закончится всё «нулевым объектом» (он же «каталог», «оглавление», «корневой каталог» и куча других названий) в котором хранится список остальных объектов с метаданными - таймстамп, расположение первого блока и т.п.?

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