Сообщение Re[97]: MS забило на дотнет. Питону - да, сишарпу - нет? от 14.10.2021 7:16
Изменено 14.10.2021 7:23 Sinclair
Re[97]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Будем разгребать:
V>Синклер:
V>- Неадекватное решение — реализация набора видов блокировок в СУБД через самописанный семафор, поддерживающий отрицательные значения счётчиков.
V>Я:
V>- твоё утверждение ложно, бо в СУБД широко используют именно самописные семафоры.
Ну разве можно так бессовестно передёргивать? Все ходы записаны.
Я вас просил привести решение задачи — вы начали с описания некоего решения на основе семафора; затем оговорились, что это решение — плохое, поэтому нужно добавить ещё один семафор, впрочем это решение тоже плохое, поэтому нужно использовать пользовательские очереди задач.
При этом изо всего этого ряда решений код вы привели только для первого, и то неполный, а в стиле "вот вам int main(){}, остальное впишите между фигурными скобками".
Я не утверждаю, что вообще применение каких-либо "самописанных семафоров" плохо. Я говорю, что конкретно ваше решение, предложенное в этой ветке — плохое.
V>Синклер:
V>- нет, блокировка строки и блокировка страницы делается через разные ресурсы, а не через один семафор.
V>Я:
V>- а как соотносятся твои "разный" (в плане экземпляров) и "самописный"?
Очень просто. Я пытаюсь вернуться к мифической "одной interlocked операции", которой было достаточно для сканирования таблицы.
(Уже начинаю подозревать, что речь шла о глобальном семафоре, который, по-вашему, является единственным средством достижения истинного ACID. Нет, такая СУБД будет катастрофически неэффективна на практике, независимо от того, компилируются ли планы запросов статически.)
Пока что вам никак не удаётся показать эту операцию. В основном — потому, что вы бегаете от вопросов про код, отделываясь общими рассуждениями про последовательность решения задач и отсылками к теории СМО.
Интересует не теория, а практика.
V>- и почему "блокировка" делается через "ресурсы"? ))
Нужно же как-то называть то, что мы захватываем и отпускаем. Общепринятое название — просто lock, но у вас какая-то беда с пониманием термина "блокировка".
V>В общем, просьба не применять пока мест просто термин "блокировка", бо ты используешь его неверно уже десяток постов подряд.
Ещё раз умоляю — откройте ссылку на википедию, которую я вам дал, и прекратите кривляться.
V>Если нет — вот одна из первых ссылок в гугле, просто прочти первый абзац, там сходу понятно, что принято называть "ресурсом" в СМО:
V>https://cyberleninka.ru/viewer_images/16416395/f/1.png
Ну прикольно. Вы даже не обратили внимания, что определения термина "ресурс" в вашем источнике нет. Просто нечто, что необходимо для выполнения заявки.
Ок, можно отбросить этот термин и пользоваться только общепринятым термином "блокировка" из мира СУБД. Ссылку на его определение я вам дал.
Если же мы вернёмся в мир СУБД, то в нём "ресурсом" называется некоторый элемент базы данных — строка, страница, таблица, индекс, key range, обьект схемы, вся база данных и т.п.
V>Чтобы дойти до операции блокировки строки, ты должен сначала пройти уровень блокировки страницы.
В общем случае нет. Просто альтернатива настолько неэффективна, что в большинстве реализаций перед захватом блокировки на "кусочек" композитного ресурса делается транзитивный захват intent lock-ов на все вышестоящие ресурсы в иерархии.
V>Т.е. не заблокировать страницу, но "отметиться" на каком-нить примитиве сигнализации уровня страницы, и, отметившись удачно, запретить конкурентным задачам блокировать страницу. Т.е., shared-владение ресурсом запрещает блокировку ресурса (т.е. запрещает монопольное овладение ресурсом, а не производит это овладение, как может показаться из твоей ошибочной терминологии), т.е., чтобы заблокировать страницу, небходимо будет дождаться освобождения всех shared-владений.
Ещё раз порекомендую вам прочитать общепринятое определение термина "блокировка" применительно к СУБД. Оно гораздо удобнее, потому что без него придётся мучиться с многословными формулировками.
Вот к примеру, то, что вы написали выше "пройти уровень блокировки страницы" — не имеет чёткого смысла. Вам приходится выкручиваться при помощи конструкции "попытаться запретить конкурентным задачам блокировать страницу".
А в мире СУБД просто говорят "транзакция захватывает intent exclusive lock на страницу", и всем всё понятно.
V>Хотя ни о каком прикладном отношении RO-RW речь не идёт, речь идёт только о shared, либо монопольном владении, просто при shared-владении безопасны конкурентные чтения, вот и всё кино. Но всё это уже уровень прикладной семантики, т.е. интерпретирования состояний неких переменных, изменяемых атомарно.
Совершенно верно. Поэтому в мире СУБД про читателей и писателей говорить не принято (в отличие, скажем, от мира параллельного программирования, где всё называют именно так (тыц, тыц, тыдыц). А принято так и называть — shared lock и exclusive lock.
V>В данном случае у некоей простейшей СМО (или простейшей такой подсистемы) есть всего два состояния — shared и exclusive.
V>Как обыграть эту пару состояний — я рассказал более чем доступно, ИМХО.
Ну, для начала, там, конечно же, не пара состояний . Состояний там примерно 2^32- в зависимости от того, ограничиваем ли мы максимальное количество одновременных читателей.
Вы привели неполную и неудачную реализацию одного из способов обыграть эту "пару состояний". Даже в википедии приведено ещё два.
В реальных СУБД помимо shared и exclusive применяют update lock, который уже совсем сложно описать одной фразой вроде "запретить конкурентным задачам блокировать страницу".
Кроме того, нужна ещё и реентерабельность, которую ваше решение не обеспечивает и близко, а также умение "апгрейда" блокировки с shared в exclusive. Этого у вас тоже нет.
V>Если состояний будет более одного, их, например, можно будет закодировать в старших битах счётчика, и тогда код условия под if потребует наложения маски битового тела счётчика или еще каких проверок, согласно выбранной семантики закодированного значения.
Вы, наверное, имеете в виду не количество состояний, а количество переменных, описывающих состояние.
V>Не зря я в самом начале предложил абстрагироваться от идентификаторов блокировок, а просто пронумеровать их, связав в иерархию, чтобы не засорять моск ненужной информацией.
Ну так я уже неделю жду, пока вы "пронумеруете" идентификаторы блокировок. Хотя бы для ровно одной гранулярности, и простой системы shared/update/exclusive, с общепринятой матрицей совместимости.
V>Стадия анализа задач, про которую я уже совсем не скромно намекаю который пост подряд, должна дать тебе отображение прикладных требований на выбранную схему СМО, адекватную поставленной задаче, включая графы переходов, если состояний более одного.
Эта стадия пройдена задолго до меня. А вам, прежде чем бросаться закидывать шапками хорошо известную проблематику, стоило бы всё же почитать какие-то основы. Даже учебника пользователя СУБД достаточно для того, чтобы понять, как именно обеспечиваются различные уровни изоляции, включая serializable, при помощи различных протоколов синхронизации.
V>И да, просьба заканчивать буксовать на "единственном семафоре", бо в подробном описании различных алгоритмов RO-RW блокировки ключевой семафор я называл целевым, но никогда не было речи о том, что один семафор может обслужить разные уровни иерархии, если есть такая потребность в разных уровнях. Один он будет на своём уровне.
Речь была о том, что table scan должна обслужить единственная interlocked переменная. Булшит про СМО появился позже, в попытках отвлечь рассуждение от кода.
V>К тому же, достаточно того, что упоминался "турникет" — а это семафор со счётчиком 1, где "турникетом" его делает способ оперирования этим семафором. Т.е. уже минимум два семафора. И еще требуется сериализация писателей, если есть желание решить проблему голодания писателей в случае, когда мы не управляем задачами, а пользуем семафоры уровня ОС — уже 3 семафора. И это только для разруливания проблематики совместного или монопольного доступа к одному ресурсу.
Вот видите, как здорово. Вижу, у вас начинает появляться понимание, что одной interlocked инструкцией мы никак не обойдёмся.
V>А ресурсы у тебя иерархические, как матрёшки, и в точности такая же задача возникает на каждом слое матрёшки — об этом было сказано вообще сразу и шло как контекст.
V>(при переходе от таблицы к странице, до этого при переходе от базы к таблице, до этого на уровне схемы)
Отож. Поэтому для того, чтобы прочитать десяток строк из таблицы по индексу, придётся побегать по нескольким десяткам "семафоров".
V>И я уже напоминал, что различные уровни изоляции достигаются не только и не столько за счёт блокировок, а чаще за счёт алгоритмов, растущих из банального COW.
V>Т.е., на некоторых уровнях изоляции конкурентные чтения и даже транзакционная запись может быть, но совершенно без эксклюзивных блокировок "ресурсов", как тебе такое?
Такое — плохо. Недаром в стандарте ANSI нет такого уровня изоляции, при котором можно обойтись без эксклюзивных блокировок.
И, кстати, версионников, которые бы при записи обходились без эксклюзивных блокировок, в природе тоже не существует.
V>А недефолтные алгоритмы — сплошное ноу-хау, увы.
V>И почему так — именно тебе не так давно рассказывал уже.
А, вот и секретность пожаловала в студию.
V>Пфф, очередной культурный шок.
V>Коду вообще требуется, чтобы его кто-то писал.
А то.
V>Выглядит как когнитивный диссонанс по причине "само не работает".
V>Т.е., на уровне мозжечка привык рассуждать в духе "с неба упало и само работает", вот до чего дотнет доводит. ))
Вижу, я плохо объяснил вам тему обсуждения, т.к. вы неверно интерпретируете мои вопросы. Попробую ещё раз объяснить: у меня нет проблемы с пониманием того, как устроена механика управления блокировками в СУБД.
И сомнений в том, что она поддаётся реализации, тоже нету. В конце концов — у нас же есть готовые примеры, многие из которых опубликованы в исходниках.
Я просто пытаюсь оценить затраты на эту механику при исполнении реальных запросов. Чтобы эти затраты оценить, нужно хотя бы примерно себе представлять, как всё это устроено внутри.
Когда я говорю "там сложнее, чем вы пишете", то сложно не означает "непонятно". Сложно означает "вычислительно сложно", т.е. то, что выглядит с т.з. диаграммы на доске как "захват блокировки на строку", внутри реализуется как множество инструкций, часть из которых требуют синхронизации (хотя бы через interlocked). А вы делаете совершенно бредовые утверждения про вот эту вычислительную сложность, и ещё и ухитряетесь с менторским видом высказывать не относящиеся к делу банальности.
V>Только в твоём случае этот феномен доходит до крайности: "обычные люди это сделать не смогли бы, такие как я или любые мои собеседники в интернете".
С чего вы взяли? Вы берётесь показывать решение — и сразу сливаетесь, как только речь заходит о коде. Как, впрочем, и более-менее всегда. Начиная от вычисления 2d-фильтров, и заканчивая планами запросов.
Про способности "обчных людей" я ничего не предполагаю — я всего лишь хочу увидеть код, о котором вы так смело рассуждаете.
V>Но ты же не мог всерьёз просить меня ответить тебе работающими исходниками полноценной СУБД? ))
Достаточно умозрительного кода, иллюстрирующего основные тезисы.
V>С херна ли эффективнее существующих?
V>Кто тебе такое обещал?
Вот это поворот! Мы же как раз начали с построения сервера приложений, совмещённого с СУБД, для максимизации эффективности: https://rsdn.org/forum/flame.comp/8103984.1
V>Обсуждение возникло по причине того, что ты злостно путаешь уровни изоляции и "блокировки".
Я один раз с недосыпа оговорился. А вот у вас пока что затруднения и с пониманием того, что такое ACID, и с уровнями изоляции. С иерархией блокировок, я вижу, наступает понимание.
V>А будучи реализованным через ручное управление заявками, можно подключать наработки из СМО — они туда сам просятся и так, вообще-то, и поступают при разработке подобных систем сложных диспетчеризаций заявок.
Ну, про ручное управление тема пока что не раскрыта.
V>Твоё зацикливание на СУБД говорит лишь о том, что ты не понимаешь сам этот класс задач.
IP-телефония, на фоне СУБД, с точки зрения разделяемых ресурсов — так, детский лепет на лужайке.
V>Еще тебе говорилось, что вместо "различных видов блокировок" (как ты рассуждал в начале этой беседы), стоит оперировать не их именами собственными, а достаточно рассматривать их нумерованную иерархию, бо на каждом уровне иерархии в плане отношений с низлежащим слоем в СУБД соблюдается строгое подобие от уровня выше.
Да не сможете вы уложить update/exclusive/shared ни в какую нумерованную иерархию. Вы по прежнему путаете гранулярность блокировок и их виды.
То, что вы описываете — и есть те самые intent блокировки, которых по-вашему не существует. И (сюрприз-сюрприз), можно обходиться и без них! Вот только тогда код захвата крупногранулярной блокировки становится громоздким и дорогостоящим.
V>СМО — это не абстрактные рассуждения, а вполне конкретные, и сразу же было сказано почему — так УДОБНЕЙ.
Удобней чем что? Чем принятая в мире СУБД классификация блокировок? Ну-ну.
V>Да, просимулировать происходящее можно и на обычных семафорах, в т.ч. используя код того полусамописного семафора, который я дал, т.к. при симуляции обычно не интересует быстродействие эмулятора, интересует происходящее в количественном выражении, т.е., где, какие, сколько операций, какова вероятность/статистика развития тех или иных сценариев из большого комбинаторного их сочетания и т.д. и т.п.
Что именно вы предлагаете просимулировать и зачем? Убедиться, что ваша реализация падает на простейших сценариях вроде update table1 set field1 = 0 where field2 = 0?
V>И?
V>Одна на таблицу.
V>До этого одна на базу.
V>До этого одна на схему.
V>До этого минимум одна на входной механизм самописных очередей задач.
Ага. И после этого — ещё и постраничные блокировки по мере сканирования. Т.к. захватывать shared table lock на всё время транзакции — свинство даже на уровне serializable.
V>До этого на пул запросов, на ресурсы, требуемые для работы хотя бы парсера и валидации SQL, составления плана запроса согласно текущей схемы базы и т.д. и т.п.
Вот этого ничего не надо, т.к. ресурсы парсера и валидатора — не уникальные, и доступ к ним в синхронизации не нуждается. А блокировка на схему, от которой зависит валидность плана (и возможность построить план, если это нужно) уже упомянута.
V>Но в дефолтном алгоритме RO-RW блокировки аж три семафора, три недешёвых вызова АПИ ОС только на реализацию всего двух состояний СМО всего одного уровня из иерархии.
V>Разве не ого? ))
V>А если состояний "ячейки" СМО более одного (как ты там упоминал еще виды блокировок) — то и вовсе по-дефолту обычно делают на условной переменной, со всеми её suspicion wake up и чудовищной вероятности столкновения потоков на единственном мьютексе, обслуживающем эту условную переменную.
Воот. Постепенно вырисовывается понимание, почему у меня скепсис относительно больших выигрышей за счёт компиляции запроса. При "интерпретации" план строится из относительно крупных кубиков — ну типа "просканировать индекс вот с таким предикатом". Накладные расходы на состыковывание кубиков сопоставимы (на первый взгляд) с накладными расходами на "проверить, нужно ли захватить row lock для выполнения bookmark lookup, или уже захвачен более крупногранулярный lock".
S>> Показываю на пальцах: Допустим, у нас уровень параллельности = 8. Разделяемые блокировки на ресурсе реализуются как acquire(1), эксклюзивные — как acquire(8).
S>>Блокировки строки — опять же, acquire(1), страницы — acquire(8).
V>8 будет если на данном уровне иерархии ровно 8 фактических shared-владений.
V>А если одно?
V>А если ни одного?
Вот поэтому-то я и хотел, чтобы вы привели код. Какой смысл обсуждать свойства "моей" реализации "вашей" идеи?
Покажите мне код, который делает то, что вы написали.
V>Ты там цикл не видел разве в псевдокоде обновления счётчика?
А цикл-то тут при чём? Он же не от количества "владений" зависит, а только от наличия конфликтующих обновлений состояния самого семафора.
S>>Транзакция A продолжает работу, захватывает shared lock на записи номер 4, 5, 6, 7, 8, 9, 10 при помощи Page(42).acquire(1) — успешно.
V>Зачем?
V>Ты ведь уже shared-владешь страницей, т.е. можешь читать данные этой страницы "просто так".
Эмм, а откуда транзакция "знает", что уже shared-владеет страницей? В коде вашего семафора нет никакой информации о том, кто чем владеет. Если вы подразумеваете, что транзакция (или менеджер блокировок) ведёт таблицы владения, то стоимость операций над этими таблицами тоже нужно учитывать. То есть опять — даже в простейшем случае одноуровневой иерархии ресурсов у нас никак не хватает "единственной interlocked операции".
V>Это тогда надо заходить на страницу под другой дисциплиной блокировки, например, "я могу блокировать с разными дисциплинами произвольные твои строки конкуретно", и тогда страница не пустит shread-RO "клиентов" к себе.
V>И тогда acquire ты будешь запрашивать у конкретной строки, а не страницы, бо на страницу под нужной тебе прикладной дисциплиной ты уже вошёл.
Совершенно верно тогда надо помимо захвата "разделяемой блокировки" на уровне страницы дополнительно захватывать нужный вид блокировки на уровне строки. Я взял "разделямую" в кавычки потому, что на самом деле intent lock не во всех отношениях ведёт себя как просто shared lock, хотя в упрощённой модели их можно использовать взаимозаменяемо.
Как мы уже выяснили, нельзя просто захватить row lock — надо сначала захватить intent-блокировки на базу, таблицу, страницу.
V>Или, при реализации COW, если страница захвачена читателями и для тех уровни изоляции позволяют, твоя транзакция может поступить иначе — сделать копию страницы, обновить строки в этой копии, а потом обновит цепочку страниц (про COW тебе тоже говорилось сразу).
И я про COW сразу отвечал, что он убивает производительность даже при чтениях. Потому, что механика определения того, какая версия страницы является актуальной для данной транзакции, небесплатна.
V>А текущая страница для shared RO-заявок переходит в режим фантома и будет жить до тех пор, пока ею shared-владеют, потом тихо умрёт.
Вот вы же опять манипулируете какой-то магией. Вот это вот всё — "режим фантома" и прочее — это же всё не божьим промыслом происходит. Кто-то должен отслеживать состояния транзакций, которые видят фантомные страницы, и своевременно эти страницы помечать как свободные. Вы уверены, что понимаете, как это повлияет на производительность обработки запросов?
V>Здесь опять путаешь уровни иерархий.
V>Говорил же тебе — тупо пронумеруй, меньше вероятность ошибиться. ))
Нет, просто вы плохо объясняете свои мысли. Именно поэтому я хотел от вас кода. Потребовалось всего-то 20 постов туда-сюда, чтобы мы сошлись на недостаточности одной interlocked операции для сканирования таблицы.
V>Но тогда привет турникетам и база быстро встанет колом, при таком-то кол-ве уровней иерархии.
Нет, если мы чётко понимаем, чем отличаются блокировки строк от блокировок страниц, то никаких вставаний колом не происходит. Просто для модификации одной строки нужно захватить две блокировки — строки и страницы (в предположении, что блокировка таблицы уже захвачена).
V>Надеюсь, на следующей итерации у тебя будет получше с пониманием целевого — а зачем вообще требуется нетривиальная возня с режимами СМО.
V>Про уровни параллелизма когда речь, то ресурсом является вычислительный блок, (процессор, ядро, поток в ядре и т.д., смотря как железка устроена).
Ага. Именно поэтому в примере выше было взято число 8. А не, скажем, 2000 — что больше похоже на количество записей на странице.
V>Например, транзакция, выполняющая COW, делает обновления многошагово, т.е. через последовательность заявок.
V>Первая заявка иницировала транзакцию, произвела первое чтение.
V>Если были конкуретные чтения или даже чтения-записи, то в рамках транзакции могут создаваться копии данных, как упомянул выше. Одна операция чтения — один уровень параллелизма (допустим для простоты). В системах с отказами и последействиями, например, нужной страницы нет в памяти, — будет отказ и новая постановка в очередь с новым состоянием заявки (см эту категоризацию СМО, сама эта категоризация даёт много подсказок).
Я по-прежнему не вижу, чем эта категоризация СМО может помочь при проектировании СУБД.
V>Освободили "ресурс", т.е. процессор-ядро-поток, исполняем следующую заявку. Вполне может быть, что следующей будет заявка из той же самой транзакции, она ведь при инициализации могла накидать сразу пачку заявок, т.е. вероятность последовательного расположения заявок от одной транзакции будет высокой.
Ну вот с этой стороны я пока не очень понимаю, как это всё строить с точки зрения реализации. Что такое "заявка"? Task<T>?
V>В общем, тебе придётся найти подтверждение этой (уже не первой) инсинуации или прилюдно извиниться.
Какой?
V>Ага, т.е. я прав — ты теперь пытаешься замылить саму суть обсуждения, как обычно ища виноватых в своем непонимании на стороне. ))
Нет, я по-прежнему пытаюсь вернуть вас к сути. Обратите внимание — уже 20 постов я задаю одни и те же вопросы.
V>"На коленке" я жду от тебя куда как более примитивную часть этого кода — простое управление shared-exclusive доступом ко всего одному ресурсу, т.е. очень простой СМО из двух состояний.
Давайте лучше вы напишете эту часть кода. Которая хотя бы будет реентерабельной
Когда сделаете, можно буд
При наличии конфликтов всё становится гораздо сложнее смоделировать, т.к. нужно откуда-то брать параметры типа "времени удержания блокировок", "вероятность конфликтов" и прочие данные, которые невозможно получить умозрительно. Поэтому мы обсуждаем только happy path.
А речь изначально шла о том, что при исполнении запроса не бывает так, что нужен доступ только к одному ресурсу.
V>И жду понимание того, как с помощью аналогичных механизмов, раскиданных по разным уровням иерархий, реализовать комбинаторику возникающих сценариев при организации различных уровней изоляции.
вся "комбинаторика" при реализации различных уровней изоляции хорошо известна. Для неё не нужна никакая теория СМО. И даже гранулярность блокировок уровню изоляции ортогональна.
Уровни изоляции описываются тем, в какой момент захватываются и отпускаются shared lock, независимо от их гранулярности. Ну, кроме serializable, где важными становятся table и range блокировки.
А вы опять умничаете в области, с которой плохо знакомы.
V>И тогда ты будешь готов согласиться с самыми первыми моими заявлениями, насчёт простой нумерации уровней иерархий.
V>Ну конечно же да.
V>Потому что в какой-нить транзакции я могу по некоему условию менять даже схему таблиц, т.е. заранее неизвестно.
V>И тогда ACID на всех уровнях, кроме сериализованного не будет соблюдаться от момента начала такого участка транзакции, т.е. базе придётся войти в сериализованный режим.
)
Господи, чушь-то какая. Изменения схемы таблиц совершенно не требуют никакого глобального мутекса. Вы, наверное, не видели взрослых СУБД.
Изменения схемы прекрасно живут на блокировках схемы. Я могу стартовать транзакцию, выполнить в ней create table и пойти покурить, не делая commit — и никакие другие транзакции этого не заметят.
Я могу сделать alter table drop column — и он дождётся, пока последняя транзакция, использующая эту таблицу завершится, и дропнет колонку.
И если параллельно со мной никакие транзакции не работают конкретно с этой таблицей, то они прекрасно продолжат работать, как ни в чём не бывало.
Я не знаю, что вы называаете "сериализованным режимом", но если речь про захват глобального мьютекса — то нет, вы не угадали, для изменений схемы он не нужен, и настоящие СУБД работают не так.
S>>Почитайте любой учебник по СУБД.
V>Опять мимо, тут нужен учебник не для пользователя СУБД, а для разработчика СУБД (угу, не разработчика прикладной базы данных, а самой СУБД).
Из тех четырёх, которые я привёл, два — для разработчиков СУБД.
V>СМО — неотъемлимая часть такого учебника.
Давайте так: дайте ссылку на главу по СМО в любом учебнике по разработке СУБД на ваш вкус.
S>>Глобальная блокировка является достаточным, но вовсе не необходимым условием для serializability.
V>Опять та же ошибка — зависит от уровня иерархии оперируемой сущности.
Что именно зависит? Значение термина "глобальная блокировка"? Очень странно. Я полагал, что глобальность — она на то и глобальность, что одна на всех.
V>СУБД тут ни разу не уникальны, т.е. до смешного — сама попытка отослать к учебнику по некоей СУБД демонстрирует не просто непонимание предметной области, а отсутствие простой "записи" в эрудиции о наличии самой предметной области.
Не по "некоей СУБД", к учебнику по разработке СУБД.
V>В английском различают термины lock и block, в русском языке возникает путаница.
V>Поэтому, стоит использовать или английскую терминологию, или длинную русскую, навроде "блокировка для совместного доступа по чтению".
V>В русскоязычном IT "просто блокировка" означает blocking lock на английском.
Именно поэтому я ни разу не использовал термин "просто блокировка".
И в большинстве мест я использовал английскую терминологию. Очень странно с вашей стороны делать вид, что вы двадцать постов подряд не понимали, что мы обсуждаем СУБД.
V>Простой пример:
V>вот у тебя запрос с агрегацией только по чтению.
V>Какие могут быть уровни изоляции:
V>- никакой, т.е. код исполнения запроса читает и те данные, что уже есть и те, что были изменены; есть вероятность, что результатом агрегата станет такое значение, которое не могло быть ни в одном из "конечных" состояний базы, т.е. между транзакциями.
V>- ACID-изоляция; конкурентные транзакции не могут изменить твои данные; это может быть достигнуто исключительным доступом к таблице (блокировкой), либо же, транзакция, выполняющая агрегирующее чтение, будет работать с копиями данных, на момент начала чтения; вот тебе пример того, что блокировки и уровни изоляции — совсем не одно и то же;
V>- откаты/обработка конфликтов; например, самостоятельный перезапуск базой запроса, если уже просмотренные данные были изменены конкурирующими транзакциями;
V>Первый случай еще обычно разделяют на два-три, по степени допущения несогласованности, но пример с агрегатом был взят именно затем, чтобы отличаться от "простой выборки".
V>Именно в этом месте "учебники по СУБД" нехило улыбают, бо я привел пример одной из самых распространнёных ошибок подсчёта агрегата при неверно выбранном уровне изоляции.
Ок, хорошо, что вы привели конкретный пример. Его хотя бы можно обсуждать. Вот для банального агрегатного запроса типа select sum(salary) from employees; какой нужен уровень изоляции? Внезапно — repeatable read.
Если мы пошли по пути блокировок, внезапно оказывается, что совершенно необязательно захватывать исключительный доступ к таблице.
Достаточно удерживать разделяемую блокировку до конца транзакции.
Более того — даже делать её уровня таблицы совершенно необязательно. Можно захватывать shared page locks по мере сканирования данных — и всё ещё никаких проблем с сериализуемостью не будет.
Понятно, почему так происходит, или надо подробно разжевать?
S>>Например, рассмотрите классическую задачу атомарного перевода денег между счетами, и убедитесь, что repeatable read достаточно для обеспечения согласованности данных в этой задаче.
V>Но недостаточно для моей задачи.
V>Если в рамках конкурирующей транзакции были изменены две строки данных, одну из них уже прочитали и подсчитали в агрегате, другую прочитали уже после фиксации конкурентной транзакции — агрегат выдаст фуфел на выходе.
Именно поэтому для данного запроса нужен repeatable read. А вот serializable — избыточен.
V>Ну да, вместо конструктива, вместо попыток понять конкретно обсуждаемый сабж, занимаешься пусканием "дымовой завесы".
Ну, ок. Не хотите читать учебники — не читайте. Но не обижайтесь, когда я буду вас тыкать носом в непонимание вещей, которые там изложены простым языком.
V>Какого именно утверждения?
Про то, что ACID достижим только при помощи одного глобального мьюьекса.
V>Системы резервирования работают, внезапно, через различные допущения с целью того самого ускорения обслуживания.
V>Например, работавшая (не знаю как сейчас) еще с 90-х система резервирования ЖД-билетов работала через пулы, раскиданные по звездообразной схеме.
V>При исчерпании пула у локального и вышестоящего узла, резервирование порой выполняется ооочень долго.
V>Потому что чудес не бывает.
V>Ты бы эта хотя бы свои собственные примеры проверял, прежде чем приводить их якобы в пример якобы своей правоты.
Печаль, печаль. Подумайте ещё раз: если бы ACID был недостижим без одного глобального mutex, то резервирование бы было медленным всегда, а не при "исчерпании пула".
V>А с современными скоростями интернета и быстродействием серверов и этого уже не требуется, ведь "один мьютекс" нужен не на всю базу ЖД-трафика, а на конкретный рейс, т.е., подозреваю, с пулами сегодня никто не возится, достаточно обеспечить fault tolerance, задача поиска свободных мест из десятков-сотен значений сегодня занимает микросекунды.
Хм, опять началась какая-то каша в голове. Что такое "глобальный мьютекс на конкретный рейс"? И чем он отличается от "локального мьютекса"?
Вы совершенно напрасно изобретаете какую-то самодеятельную терминологию.
V>И да, при попытке фактического резервирования repetable read уже не катит, упс?
С чего вы это взяли? Смотря что входит в "фактическое резервирование" — может хватить даже read committed. Попробуйте продемонстрировать контрпример
V>Зато прекрасно катит автоматический откат и повторное резервирование, скажем, на рейсы с пересадкой, когда при последовательном резервировании одно из них вышло неудачным.
V>(третий упомянутый алгоритм обеспечения согласованности)
Да, расскажите мне про optimistic locking. Но лучше — в другой раз. Там всё сложнее, чем кажется на первый взгляд, а вы ещё с pessimistic locking не разобрались.
V>Будем разгребать:
V>Синклер:
V>- Неадекватное решение — реализация набора видов блокировок в СУБД через самописанный семафор, поддерживающий отрицательные значения счётчиков.
V>Я:
V>- твоё утверждение ложно, бо в СУБД широко используют именно самописные семафоры.
Ну разве можно так бессовестно передёргивать? Все ходы записаны.
Я вас просил привести решение задачи — вы начали с описания некоего решения на основе семафора; затем оговорились, что это решение — плохое, поэтому нужно добавить ещё один семафор, впрочем это решение тоже плохое, поэтому нужно использовать пользовательские очереди задач.
При этом изо всего этого ряда решений код вы привели только для первого, и то неполный, а в стиле "вот вам int main(){}, остальное впишите между фигурными скобками".
Я не утверждаю, что вообще применение каких-либо "самописанных семафоров" плохо. Я говорю, что конкретно ваше решение, предложенное в этой ветке — плохое.
V>Синклер:
V>- нет, блокировка строки и блокировка страницы делается через разные ресурсы, а не через один семафор.
V>Я:
V>- а как соотносятся твои "разный" (в плане экземпляров) и "самописный"?
Очень просто. Я пытаюсь вернуться к мифической "одной interlocked операции", которой было достаточно для сканирования таблицы.
(Уже начинаю подозревать, что речь шла о глобальном семафоре, который, по-вашему, является единственным средством достижения истинного ACID. Нет, такая СУБД будет катастрофически неэффективна на практике, независимо от того, компилируются ли планы запросов статически.)
Пока что вам никак не удаётся показать эту операцию. В основном — потому, что вы бегаете от вопросов про код, отделываясь общими рассуждениями про последовательность решения задач и отсылками к теории СМО.
Интересует не теория, а практика.
V>- и почему "блокировка" делается через "ресурсы"? ))
Нужно же как-то называть то, что мы захватываем и отпускаем. Общепринятое название — просто lock, но у вас какая-то беда с пониманием термина "блокировка".
V>В общем, просьба не применять пока мест просто термин "блокировка", бо ты используешь его неверно уже десяток постов подряд.
Ещё раз умоляю — откройте ссылку на википедию, которую я вам дал, и прекратите кривляться.
V>Если нет — вот одна из первых ссылок в гугле, просто прочти первый абзац, там сходу понятно, что принято называть "ресурсом" в СМО:
V>https://cyberleninka.ru/viewer_images/16416395/f/1.png
Ну прикольно. Вы даже не обратили внимания, что определения термина "ресурс" в вашем источнике нет. Просто нечто, что необходимо для выполнения заявки.
Ок, можно отбросить этот термин и пользоваться только общепринятым термином "блокировка" из мира СУБД. Ссылку на его определение я вам дал.
Если же мы вернёмся в мир СУБД, то в нём "ресурсом" называется некоторый элемент базы данных — строка, страница, таблица, индекс, key range, обьект схемы, вся база данных и т.п.
V>Чтобы дойти до операции блокировки строки, ты должен сначала пройти уровень блокировки страницы.
В общем случае нет. Просто альтернатива настолько неэффективна, что в большинстве реализаций перед захватом блокировки на "кусочек" композитного ресурса делается транзитивный захват intent lock-ов на все вышестоящие ресурсы в иерархии.
V>Т.е. не заблокировать страницу, но "отметиться" на каком-нить примитиве сигнализации уровня страницы, и, отметившись удачно, запретить конкурентным задачам блокировать страницу. Т.е., shared-владение ресурсом запрещает блокировку ресурса (т.е. запрещает монопольное овладение ресурсом, а не производит это овладение, как может показаться из твоей ошибочной терминологии), т.е., чтобы заблокировать страницу, небходимо будет дождаться освобождения всех shared-владений.
Ещё раз порекомендую вам прочитать общепринятое определение термина "блокировка" применительно к СУБД. Оно гораздо удобнее, потому что без него придётся мучиться с многословными формулировками.
Вот к примеру, то, что вы написали выше "пройти уровень блокировки страницы" — не имеет чёткого смысла. Вам приходится выкручиваться при помощи конструкции "попытаться запретить конкурентным задачам блокировать страницу".
А в мире СУБД просто говорят "транзакция захватывает intent exclusive lock на страницу", и всем всё понятно.
V>Хотя ни о каком прикладном отношении RO-RW речь не идёт, речь идёт только о shared, либо монопольном владении, просто при shared-владении безопасны конкурентные чтения, вот и всё кино. Но всё это уже уровень прикладной семантики, т.е. интерпретирования состояний неких переменных, изменяемых атомарно.
Совершенно верно. Поэтому в мире СУБД про читателей и писателей говорить не принято (в отличие, скажем, от мира параллельного программирования, где всё называют именно так (тыц, тыц, тыдыц). А принято так и называть — shared lock и exclusive lock.
V>В данном случае у некоей простейшей СМО (или простейшей такой подсистемы) есть всего два состояния — shared и exclusive.
V>Как обыграть эту пару состояний — я рассказал более чем доступно, ИМХО.
Ну, для начала, там, конечно же, не пара состояний . Состояний там примерно 2^32- в зависимости от того, ограничиваем ли мы максимальное количество одновременных читателей.
Вы привели неполную и неудачную реализацию одного из способов обыграть эту "пару состояний". Даже в википедии приведено ещё два.
В реальных СУБД помимо shared и exclusive применяют update lock, который уже совсем сложно описать одной фразой вроде "запретить конкурентным задачам блокировать страницу".
Кроме того, нужна ещё и реентерабельность, которую ваше решение не обеспечивает и близко, а также умение "апгрейда" блокировки с shared в exclusive. Этого у вас тоже нет.
V>Если состояний будет более одного, их, например, можно будет закодировать в старших битах счётчика, и тогда код условия под if потребует наложения маски битового тела счётчика или еще каких проверок, согласно выбранной семантики закодированного значения.
Вы, наверное, имеете в виду не количество состояний, а количество переменных, описывающих состояние.
V>Не зря я в самом начале предложил абстрагироваться от идентификаторов блокировок, а просто пронумеровать их, связав в иерархию, чтобы не засорять моск ненужной информацией.
Ну так я уже неделю жду, пока вы "пронумеруете" идентификаторы блокировок. Хотя бы для ровно одной гранулярности, и простой системы shared/update/exclusive, с общепринятой матрицей совместимости.
V>Стадия анализа задач, про которую я уже совсем не скромно намекаю который пост подряд, должна дать тебе отображение прикладных требований на выбранную схему СМО, адекватную поставленной задаче, включая графы переходов, если состояний более одного.
Эта стадия пройдена задолго до меня. А вам, прежде чем бросаться закидывать шапками хорошо известную проблематику, стоило бы всё же почитать какие-то основы. Даже учебника пользователя СУБД достаточно для того, чтобы понять, как именно обеспечиваются различные уровни изоляции, включая serializable, при помощи различных протоколов синхронизации.
V>И да, просьба заканчивать буксовать на "единственном семафоре", бо в подробном описании различных алгоритмов RO-RW блокировки ключевой семафор я называл целевым, но никогда не было речи о том, что один семафор может обслужить разные уровни иерархии, если есть такая потребность в разных уровнях. Один он будет на своём уровне.
Речь была о том, что table scan должна обслужить единственная interlocked переменная. Булшит про СМО появился позже, в попытках отвлечь рассуждение от кода.
V>К тому же, достаточно того, что упоминался "турникет" — а это семафор со счётчиком 1, где "турникетом" его делает способ оперирования этим семафором. Т.е. уже минимум два семафора. И еще требуется сериализация писателей, если есть желание решить проблему голодания писателей в случае, когда мы не управляем задачами, а пользуем семафоры уровня ОС — уже 3 семафора. И это только для разруливания проблематики совместного или монопольного доступа к одному ресурсу.
Вот видите, как здорово. Вижу, у вас начинает появляться понимание, что одной interlocked инструкцией мы никак не обойдёмся.
V>А ресурсы у тебя иерархические, как матрёшки, и в точности такая же задача возникает на каждом слое матрёшки — об этом было сказано вообще сразу и шло как контекст.
V>(при переходе от таблицы к странице, до этого при переходе от базы к таблице, до этого на уровне схемы)
Отож. Поэтому для того, чтобы прочитать десяток строк из таблицы по индексу, придётся побегать по нескольким десяткам "семафоров".
V>И я уже напоминал, что различные уровни изоляции достигаются не только и не столько за счёт блокировок, а чаще за счёт алгоритмов, растущих из банального COW.
V>Т.е., на некоторых уровнях изоляции конкурентные чтения и даже транзакционная запись может быть, но совершенно без эксклюзивных блокировок "ресурсов", как тебе такое?
Такое — плохо. Недаром в стандарте ANSI нет такого уровня изоляции, при котором можно обойтись без эксклюзивных блокировок.
И, кстати, версионников, которые бы при записи обходились без эксклюзивных блокировок, в природе тоже не существует.
V>А недефолтные алгоритмы — сплошное ноу-хау, увы.
V>И почему так — именно тебе не так давно рассказывал уже.
А, вот и секретность пожаловала в студию.
V>Пфф, очередной культурный шок.
V>Коду вообще требуется, чтобы его кто-то писал.
А то.
V>Выглядит как когнитивный диссонанс по причине "само не работает".
V>Т.е., на уровне мозжечка привык рассуждать в духе "с неба упало и само работает", вот до чего дотнет доводит. ))
Вижу, я плохо объяснил вам тему обсуждения, т.к. вы неверно интерпретируете мои вопросы. Попробую ещё раз объяснить: у меня нет проблемы с пониманием того, как устроена механика управления блокировками в СУБД.
И сомнений в том, что она поддаётся реализации, тоже нету. В конце концов — у нас же есть готовые примеры, многие из которых опубликованы в исходниках.
Я просто пытаюсь оценить затраты на эту механику при исполнении реальных запросов. Чтобы эти затраты оценить, нужно хотя бы примерно себе представлять, как всё это устроено внутри.
Когда я говорю "там сложнее, чем вы пишете", то сложно не означает "непонятно". Сложно означает "вычислительно сложно", т.е. то, что выглядит с т.з. диаграммы на доске как "захват блокировки на строку", внутри реализуется как множество инструкций, часть из которых требуют синхронизации (хотя бы через interlocked). А вы делаете совершенно бредовые утверждения про вот эту вычислительную сложность, и ещё и ухитряетесь с менторским видом высказывать не относящиеся к делу банальности.
V>Только в твоём случае этот феномен доходит до крайности: "обычные люди это сделать не смогли бы, такие как я или любые мои собеседники в интернете".
С чего вы взяли? Вы берётесь показывать решение — и сразу сливаетесь, как только речь заходит о коде. Как, впрочем, и более-менее всегда. Начиная от вычисления 2d-фильтров, и заканчивая планами запросов.
Про способности "обчных людей" я ничего не предполагаю — я всего лишь хочу увидеть код, о котором вы так смело рассуждаете.
V>Но ты же не мог всерьёз просить меня ответить тебе работающими исходниками полноценной СУБД? ))
Достаточно умозрительного кода, иллюстрирующего основные тезисы.
V>С херна ли эффективнее существующих?
V>Кто тебе такое обещал?
Вот это поворот! Мы же как раз начали с построения сервера приложений, совмещённого с СУБД, для максимизации эффективности: https://rsdn.org/forum/flame.comp/8103984.1
Автор: vdimas
Дата: 01.10.21
Дата: 01.10.21
V>Обсуждение возникло по причине того, что ты злостно путаешь уровни изоляции и "блокировки".
Я один раз с недосыпа оговорился. А вот у вас пока что затруднения и с пониманием того, что такое ACID, и с уровнями изоляции. С иерархией блокировок, я вижу, наступает понимание.
V>А будучи реализованным через ручное управление заявками, можно подключать наработки из СМО — они туда сам просятся и так, вообще-то, и поступают при разработке подобных систем сложных диспетчеризаций заявок.
Ну, про ручное управление тема пока что не раскрыта.
V>Твоё зацикливание на СУБД говорит лишь о том, что ты не понимаешь сам этот класс задач.
IP-телефония, на фоне СУБД, с точки зрения разделяемых ресурсов — так, детский лепет на лужайке.
V>Еще тебе говорилось, что вместо "различных видов блокировок" (как ты рассуждал в начале этой беседы), стоит оперировать не их именами собственными, а достаточно рассматривать их нумерованную иерархию, бо на каждом уровне иерархии в плане отношений с низлежащим слоем в СУБД соблюдается строгое подобие от уровня выше.
Да не сможете вы уложить update/exclusive/shared ни в какую нумерованную иерархию. Вы по прежнему путаете гранулярность блокировок и их виды.
То, что вы описываете — и есть те самые intent блокировки, которых по-вашему не существует. И (сюрприз-сюрприз), можно обходиться и без них! Вот только тогда код захвата крупногранулярной блокировки становится громоздким и дорогостоящим.
V>СМО — это не абстрактные рассуждения, а вполне конкретные, и сразу же было сказано почему — так УДОБНЕЙ.
Удобней чем что? Чем принятая в мире СУБД классификация блокировок? Ну-ну.
V>Да, просимулировать происходящее можно и на обычных семафорах, в т.ч. используя код того полусамописного семафора, который я дал, т.к. при симуляции обычно не интересует быстродействие эмулятора, интересует происходящее в количественном выражении, т.е., где, какие, сколько операций, какова вероятность/статистика развития тех или иных сценариев из большого комбинаторного их сочетания и т.д. и т.п.
Что именно вы предлагаете просимулировать и зачем? Убедиться, что ваша реализация падает на простейших сценариях вроде update table1 set field1 = 0 where field2 = 0?
V>И?
V>Одна на таблицу.
V>До этого одна на базу.
V>До этого одна на схему.
V>До этого минимум одна на входной механизм самописных очередей задач.
Ага. И после этого — ещё и постраничные блокировки по мере сканирования. Т.к. захватывать shared table lock на всё время транзакции — свинство даже на уровне serializable.
V>До этого на пул запросов, на ресурсы, требуемые для работы хотя бы парсера и валидации SQL, составления плана запроса согласно текущей схемы базы и т.д. и т.п.
Вот этого ничего не надо, т.к. ресурсы парсера и валидатора — не уникальные, и доступ к ним в синхронизации не нуждается. А блокировка на схему, от которой зависит валидность плана (и возможность построить план, если это нужно) уже упомянута.
V>Но в дефолтном алгоритме RO-RW блокировки аж три семафора, три недешёвых вызова АПИ ОС только на реализацию всего двух состояний СМО всего одного уровня из иерархии.
V>Разве не ого? ))
V>А если состояний "ячейки" СМО более одного (как ты там упоминал еще виды блокировок) — то и вовсе по-дефолту обычно делают на условной переменной, со всеми её suspicion wake up и чудовищной вероятности столкновения потоков на единственном мьютексе, обслуживающем эту условную переменную.
Воот. Постепенно вырисовывается понимание, почему у меня скепсис относительно больших выигрышей за счёт компиляции запроса. При "интерпретации" план строится из относительно крупных кубиков — ну типа "просканировать индекс вот с таким предикатом". Накладные расходы на состыковывание кубиков сопоставимы (на первый взгляд) с накладными расходами на "проверить, нужно ли захватить row lock для выполнения bookmark lookup, или уже захвачен более крупногранулярный lock".
S>> Показываю на пальцах: Допустим, у нас уровень параллельности = 8. Разделяемые блокировки на ресурсе реализуются как acquire(1), эксклюзивные — как acquire(8).
S>>Блокировки строки — опять же, acquire(1), страницы — acquire(8).
V>8 будет если на данном уровне иерархии ровно 8 фактических shared-владений.
V>А если одно?
V>А если ни одного?
Вот поэтому-то я и хотел, чтобы вы привели код. Какой смысл обсуждать свойства "моей" реализации "вашей" идеи?
Покажите мне код, который делает то, что вы написали.
V>Ты там цикл не видел разве в псевдокоде обновления счётчика?
А цикл-то тут при чём? Он же не от количества "владений" зависит, а только от наличия конфликтующих обновлений состояния самого семафора.
S>>Транзакция A продолжает работу, захватывает shared lock на записи номер 4, 5, 6, 7, 8, 9, 10 при помощи Page(42).acquire(1) — успешно.
V>Зачем?
V>Ты ведь уже shared-владешь страницей, т.е. можешь читать данные этой страницы "просто так".
Эмм, а откуда транзакция "знает", что уже shared-владеет страницей? В коде вашего семафора нет никакой информации о том, кто чем владеет. Если вы подразумеваете, что транзакция (или менеджер блокировок) ведёт таблицы владения, то стоимость операций над этими таблицами тоже нужно учитывать. То есть опять — даже в простейшем случае одноуровневой иерархии ресурсов у нас никак не хватает "единственной interlocked операции".
V>Это тогда надо заходить на страницу под другой дисциплиной блокировки, например, "я могу блокировать с разными дисциплинами произвольные твои строки конкуретно", и тогда страница не пустит shread-RO "клиентов" к себе.
V>И тогда acquire ты будешь запрашивать у конкретной строки, а не страницы, бо на страницу под нужной тебе прикладной дисциплиной ты уже вошёл.
Совершенно верно тогда надо помимо захвата "разделяемой блокировки" на уровне страницы дополнительно захватывать нужный вид блокировки на уровне строки. Я взял "разделямую" в кавычки потому, что на самом деле intent lock не во всех отношениях ведёт себя как просто shared lock, хотя в упрощённой модели их можно использовать взаимозаменяемо.
Как мы уже выяснили, нельзя просто захватить row lock — надо сначала захватить intent-блокировки на базу, таблицу, страницу.
V>Или, при реализации COW, если страница захвачена читателями и для тех уровни изоляции позволяют, твоя транзакция может поступить иначе — сделать копию страницы, обновить строки в этой копии, а потом обновит цепочку страниц (про COW тебе тоже говорилось сразу).
И я про COW сразу отвечал, что он убивает производительность даже при чтениях. Потому, что механика определения того, какая версия страницы является актуальной для данной транзакции, небесплатна.
V>А текущая страница для shared RO-заявок переходит в режим фантома и будет жить до тех пор, пока ею shared-владеют, потом тихо умрёт.
Вот вы же опять манипулируете какой-то магией. Вот это вот всё — "режим фантома" и прочее — это же всё не божьим промыслом происходит. Кто-то должен отслеживать состояния транзакций, которые видят фантомные страницы, и своевременно эти страницы помечать как свободные. Вы уверены, что понимаете, как это повлияет на производительность обработки запросов?
V>Здесь опять путаешь уровни иерархий.
V>Говорил же тебе — тупо пронумеруй, меньше вероятность ошибиться. ))
Нет, просто вы плохо объясняете свои мысли. Именно поэтому я хотел от вас кода. Потребовалось всего-то 20 постов туда-сюда, чтобы мы сошлись на недостаточности одной interlocked операции для сканирования таблицы.
V>Но тогда привет турникетам и база быстро встанет колом, при таком-то кол-ве уровней иерархии.
Нет, если мы чётко понимаем, чем отличаются блокировки строк от блокировок страниц, то никаких вставаний колом не происходит. Просто для модификации одной строки нужно захватить две блокировки — строки и страницы (в предположении, что блокировка таблицы уже захвачена).
V>Надеюсь, на следующей итерации у тебя будет получше с пониманием целевого — а зачем вообще требуется нетривиальная возня с режимами СМО.
V>Про уровни параллелизма когда речь, то ресурсом является вычислительный блок, (процессор, ядро, поток в ядре и т.д., смотря как железка устроена).
Ага. Именно поэтому в примере выше было взято число 8. А не, скажем, 2000 — что больше похоже на количество записей на странице.
V>Например, транзакция, выполняющая COW, делает обновления многошагово, т.е. через последовательность заявок.
V>Первая заявка иницировала транзакцию, произвела первое чтение.
V>Если были конкуретные чтения или даже чтения-записи, то в рамках транзакции могут создаваться копии данных, как упомянул выше. Одна операция чтения — один уровень параллелизма (допустим для простоты). В системах с отказами и последействиями, например, нужной страницы нет в памяти, — будет отказ и новая постановка в очередь с новым состоянием заявки (см эту категоризацию СМО, сама эта категоризация даёт много подсказок).
Я по-прежнему не вижу, чем эта категоризация СМО может помочь при проектировании СУБД.
V>Освободили "ресурс", т.е. процессор-ядро-поток, исполняем следующую заявку. Вполне может быть, что следующей будет заявка из той же самой транзакции, она ведь при инициализации могла накидать сразу пачку заявок, т.е. вероятность последовательного расположения заявок от одной транзакции будет высокой.
Ну вот с этой стороны я пока не очень понимаю, как это всё строить с точки зрения реализации. Что такое "заявка"? Task<T>?
V>В общем, тебе придётся найти подтверждение этой (уже не первой) инсинуации или прилюдно извиниться.
Какой?
V>Ага, т.е. я прав — ты теперь пытаешься замылить саму суть обсуждения, как обычно ища виноватых в своем непонимании на стороне. ))
Нет, я по-прежнему пытаюсь вернуть вас к сути. Обратите внимание — уже 20 постов я задаю одни и те же вопросы.
V>"На коленке" я жду от тебя куда как более примитивную часть этого кода — простое управление shared-exclusive доступом ко всего одному ресурсу, т.е. очень простой СМО из двух состояний.
Давайте лучше вы напишете эту часть кода. Которая хотя бы будет реентерабельной
Когда сделаете, можно буд
При наличии конфликтов всё становится гораздо сложнее смоделировать, т.к. нужно откуда-то брать параметры типа "времени удержания блокировок", "вероятность конфликтов" и прочие данные, которые невозможно получить умозрительно. Поэтому мы обсуждаем только happy path.
А речь изначально шла о том, что при исполнении запроса не бывает так, что нужен доступ только к одному ресурсу.
V>И жду понимание того, как с помощью аналогичных механизмов, раскиданных по разным уровням иерархий, реализовать комбинаторику возникающих сценариев при организации различных уровней изоляции.
вся "комбинаторика" при реализации различных уровней изоляции хорошо известна. Для неё не нужна никакая теория СМО. И даже гранулярность блокировок уровню изоляции ортогональна.
Уровни изоляции описываются тем, в какой момент захватываются и отпускаются shared lock, независимо от их гранулярности. Ну, кроме serializable, где важными становятся table и range блокировки.
А вы опять умничаете в области, с которой плохо знакомы.
V>И тогда ты будешь готов согласиться с самыми первыми моими заявлениями, насчёт простой нумерации уровней иерархий.
V>Ну конечно же да.
V>Потому что в какой-нить транзакции я могу по некоему условию менять даже схему таблиц, т.е. заранее неизвестно.
V>И тогда ACID на всех уровнях, кроме сериализованного не будет соблюдаться от момента начала такого участка транзакции, т.е. базе придётся войти в сериализованный режим.
)
Господи, чушь-то какая. Изменения схемы таблиц совершенно не требуют никакого глобального мутекса. Вы, наверное, не видели взрослых СУБД.
Изменения схемы прекрасно живут на блокировках схемы. Я могу стартовать транзакцию, выполнить в ней create table и пойти покурить, не делая commit — и никакие другие транзакции этого не заметят.
Я могу сделать alter table drop column — и он дождётся, пока последняя транзакция, использующая эту таблицу завершится, и дропнет колонку.
И если параллельно со мной никакие транзакции не работают конкретно с этой таблицей, то они прекрасно продолжат работать, как ни в чём не бывало.
Я не знаю, что вы называаете "сериализованным режимом", но если речь про захват глобального мьютекса — то нет, вы не угадали, для изменений схемы он не нужен, и настоящие СУБД работают не так.
S>>Почитайте любой учебник по СУБД.
V>Опять мимо, тут нужен учебник не для пользователя СУБД, а для разработчика СУБД (угу, не разработчика прикладной базы данных, а самой СУБД).
Из тех четырёх, которые я привёл, два — для разработчиков СУБД.
V>СМО — неотъемлимая часть такого учебника.
Давайте так: дайте ссылку на главу по СМО в любом учебнике по разработке СУБД на ваш вкус.
S>>Глобальная блокировка является достаточным, но вовсе не необходимым условием для serializability.
V>Опять та же ошибка — зависит от уровня иерархии оперируемой сущности.
Что именно зависит? Значение термина "глобальная блокировка"? Очень странно. Я полагал, что глобальность — она на то и глобальность, что одна на всех.
V>СУБД тут ни разу не уникальны, т.е. до смешного — сама попытка отослать к учебнику по некоей СУБД демонстрирует не просто непонимание предметной области, а отсутствие простой "записи" в эрудиции о наличии самой предметной области.
Не по "некоей СУБД", к учебнику по разработке СУБД.
V>В английском различают термины lock и block, в русском языке возникает путаница.
V>Поэтому, стоит использовать или английскую терминологию, или длинную русскую, навроде "блокировка для совместного доступа по чтению".
V>В русскоязычном IT "просто блокировка" означает blocking lock на английском.
Именно поэтому я ни разу не использовал термин "просто блокировка".
И в большинстве мест я использовал английскую терминологию. Очень странно с вашей стороны делать вид, что вы двадцать постов подряд не понимали, что мы обсуждаем СУБД.
V>Простой пример:
V>вот у тебя запрос с агрегацией только по чтению.
V>Какие могут быть уровни изоляции:
V>- никакой, т.е. код исполнения запроса читает и те данные, что уже есть и те, что были изменены; есть вероятность, что результатом агрегата станет такое значение, которое не могло быть ни в одном из "конечных" состояний базы, т.е. между транзакциями.
V>- ACID-изоляция; конкурентные транзакции не могут изменить твои данные; это может быть достигнуто исключительным доступом к таблице (блокировкой), либо же, транзакция, выполняющая агрегирующее чтение, будет работать с копиями данных, на момент начала чтения; вот тебе пример того, что блокировки и уровни изоляции — совсем не одно и то же;
V>- откаты/обработка конфликтов; например, самостоятельный перезапуск базой запроса, если уже просмотренные данные были изменены конкурирующими транзакциями;
V>Первый случай еще обычно разделяют на два-три, по степени допущения несогласованности, но пример с агрегатом был взят именно затем, чтобы отличаться от "простой выборки".
V>Именно в этом месте "учебники по СУБД" нехило улыбают, бо я привел пример одной из самых распространнёных ошибок подсчёта агрегата при неверно выбранном уровне изоляции.
Ок, хорошо, что вы привели конкретный пример. Его хотя бы можно обсуждать. Вот для банального агрегатного запроса типа select sum(salary) from employees; какой нужен уровень изоляции? Внезапно — repeatable read.
Если мы пошли по пути блокировок, внезапно оказывается, что совершенно необязательно захватывать исключительный доступ к таблице.
Достаточно удерживать разделяемую блокировку до конца транзакции.
Более того — даже делать её уровня таблицы совершенно необязательно. Можно захватывать shared page locks по мере сканирования данных — и всё ещё никаких проблем с сериализуемостью не будет.
Понятно, почему так происходит, или надо подробно разжевать?
S>>Например, рассмотрите классическую задачу атомарного перевода денег между счетами, и убедитесь, что repeatable read достаточно для обеспечения согласованности данных в этой задаче.
V>Но недостаточно для моей задачи.
V>Если в рамках конкурирующей транзакции были изменены две строки данных, одну из них уже прочитали и подсчитали в агрегате, другую прочитали уже после фиксации конкурентной транзакции — агрегат выдаст фуфел на выходе.
Именно поэтому для данного запроса нужен repeatable read. А вот serializable — избыточен.
V>Ну да, вместо конструктива, вместо попыток понять конкретно обсуждаемый сабж, занимаешься пусканием "дымовой завесы".
Ну, ок. Не хотите читать учебники — не читайте. Но не обижайтесь, когда я буду вас тыкать носом в непонимание вещей, которые там изложены простым языком.
V>Какого именно утверждения?
Про то, что ACID достижим только при помощи одного глобального мьюьекса.
V>Системы резервирования работают, внезапно, через различные допущения с целью того самого ускорения обслуживания.
V>Например, работавшая (не знаю как сейчас) еще с 90-х система резервирования ЖД-билетов работала через пулы, раскиданные по звездообразной схеме.
V>При исчерпании пула у локального и вышестоящего узла, резервирование порой выполняется ооочень долго.
V>Потому что чудес не бывает.
V>Ты бы эта хотя бы свои собственные примеры проверял, прежде чем приводить их якобы в пример якобы своей правоты.
Печаль, печаль. Подумайте ещё раз: если бы ACID был недостижим без одного глобального mutex, то резервирование бы было медленным всегда, а не при "исчерпании пула".
V>А с современными скоростями интернета и быстродействием серверов и этого уже не требуется, ведь "один мьютекс" нужен не на всю базу ЖД-трафика, а на конкретный рейс, т.е., подозреваю, с пулами сегодня никто не возится, достаточно обеспечить fault tolerance, задача поиска свободных мест из десятков-сотен значений сегодня занимает микросекунды.
Хм, опять началась какая-то каша в голове. Что такое "глобальный мьютекс на конкретный рейс"? И чем он отличается от "локального мьютекса"?
Вы совершенно напрасно изобретаете какую-то самодеятельную терминологию.
V>И да, при попытке фактического резервирования repetable read уже не катит, упс?
С чего вы это взяли? Смотря что входит в "фактическое резервирование" — может хватить даже read committed. Попробуйте продемонстрировать контрпример
V>Зато прекрасно катит автоматический откат и повторное резервирование, скажем, на рейсы с пересадкой, когда при последовательном резервировании одно из них вышло неудачным.
V>(третий упомянутый алгоритм обеспечения согласованности)
Да, расскажите мне про optimistic locking. Но лучше — в другой раз. Там всё сложнее, чем кажется на первый взгляд, а вы ещё с pessimistic locking не разобрались.
Re[97]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, vdimas, Вы писали:
V>Будем разгребать:
V>Синклер:
V>- Неадекватное решение — реализация набора видов блокировок в СУБД через самописанный семафор, поддерживающий отрицательные значения счётчиков.
V>Я:
V>- твоё утверждение ложно, бо в СУБД широко используют именно самописные семафоры.
Ну разве можно так бессовестно передёргивать? Все ходы записаны.
Я вас просил привести решение задачи — вы начали с описания некоего решения на основе семафора; затем оговорились, что это решение — плохое, поэтому нужно добавить ещё один семафор, впрочем это решение тоже плохое, поэтому нужно использовать пользовательские очереди задач.
При этом изо всего этого ряда решений код вы привели только для первого, и то неполный, а в стиле "вот вам int main(){}, остальное впишите между фигурными скобками".
Я не утверждаю, что вообще применение каких-либо "самописанных семафоров" плохо. Я говорю, что конкретно ваше решение, предложенное в этой ветке — плохое.
V>Синклер:
V>- нет, блокировка строки и блокировка страницы делается через разные ресурсы, а не через один семафор.
V>Я:
V>- а как соотносятся твои "разный" (в плане экземпляров) и "самописный"?
Очень просто. Я пытаюсь вернуться к мифической "одной interlocked операции", которой было достаточно для сканирования таблицы.
(Уже начинаю подозревать, что речь шла о глобальном семафоре, который, по-вашему, является единственным средством достижения истинного ACID. Нет, такая СУБД будет катастрофически неэффективна на практике, независимо от того, компилируются ли планы запросов статически.)
Пока что вам никак не удаётся показать эту операцию. В основном — потому, что вы бегаете от вопросов про код, отделываясь общими рассуждениями про последовательность решения задач и отсылками к теории СМО.
Интересует не теория, а практика.
V>- и почему "блокировка" делается через "ресурсы"? ))
Нужно же как-то называть то, что мы захватываем и отпускаем. Общепринятое название — просто lock, но у вас какая-то беда с пониманием термина "блокировка".
V>В общем, просьба не применять пока мест просто термин "блокировка", бо ты используешь его неверно уже десяток постов подряд.
Ещё раз умоляю — откройте ссылку на википедию, которую я вам дал, и прекратите кривляться.
V>Если нет — вот одна из первых ссылок в гугле, просто прочти первый абзац, там сходу понятно, что принято называть "ресурсом" в СМО:
V>https://cyberleninka.ru/viewer_images/16416395/f/1.png
Ну прикольно. Вы даже не обратили внимания, что определения термина "ресурс" в вашем источнике нет. Просто нечто, что необходимо для выполнения заявки.
Ок, можно отбросить этот термин и пользоваться только общепринятым термином "блокировка" из мира СУБД. Ссылку на его определение я вам дал.
Если же мы вернёмся в мир СУБД, то в нём "ресурсом" называется некоторый элемент базы данных — строка, страница, таблица, индекс, key range, обьект схемы, вся база данных и т.п.
V>Чтобы дойти до операции блокировки строки, ты должен сначала пройти уровень блокировки страницы.
В общем случае нет. Просто альтернатива настолько неэффективна, что в большинстве реализаций перед захватом блокировки на "кусочек" композитного ресурса делается транзитивный захват intent lock-ов на все вышестоящие ресурсы в иерархии.
V>Т.е. не заблокировать страницу, но "отметиться" на каком-нить примитиве сигнализации уровня страницы, и, отметившись удачно, запретить конкурентным задачам блокировать страницу. Т.е., shared-владение ресурсом запрещает блокировку ресурса (т.е. запрещает монопольное овладение ресурсом, а не производит это овладение, как может показаться из твоей ошибочной терминологии), т.е., чтобы заблокировать страницу, небходимо будет дождаться освобождения всех shared-владений.
Ещё раз порекомендую вам прочитать общепринятое определение термина "блокировка" применительно к СУБД. Оно гораздо удобнее, потому что без него придётся мучиться с многословными формулировками.
Вот к примеру, то, что вы написали выше "пройти уровень блокировки страницы" — не имеет чёткого смысла. Вам приходится выкручиваться при помощи конструкции "попытаться запретить конкурентным задачам блокировать страницу".
А в мире СУБД просто говорят "транзакция захватывает intent exclusive lock на страницу", и всем всё понятно.
V>Хотя ни о каком прикладном отношении RO-RW речь не идёт, речь идёт только о shared, либо монопольном владении, просто при shared-владении безопасны конкурентные чтения, вот и всё кино. Но всё это уже уровень прикладной семантики, т.е. интерпретирования состояний неких переменных, изменяемых атомарно.
Совершенно верно. Поэтому в мире СУБД про читателей и писателей говорить не принято (в отличие, скажем, от мира параллельного программирования, где всё называют именно так (тыц, тыц, тыдыц). А принято так и называть — shared lock и exclusive lock.
V>В данном случае у некоей простейшей СМО (или простейшей такой подсистемы) есть всего два состояния — shared и exclusive.
V>Как обыграть эту пару состояний — я рассказал более чем доступно, ИМХО.
Ну, для начала, там, конечно же, не пара состояний . Состояний там примерно 2^32- в зависимости от того, ограничиваем ли мы максимальное количество одновременных читателей.
Вы привели неполную и неудачную реализацию одного из способов обыграть эту "пару состояний". Даже в википедии приведено ещё два.
В реальных СУБД помимо shared и exclusive применяют update lock, который уже совсем сложно описать одной фразой вроде "запретить конкурентным задачам блокировать страницу".
Кроме того, нужна ещё и реентерабельность, которую ваше решение не обеспечивает и близко, а также умение "апгрейда" блокировки с shared в exclusive. Этого у вас тоже нет.
V>Если состояний будет более одного, их, например, можно будет закодировать в старших битах счётчика, и тогда код условия под if потребует наложения маски битового тела счётчика или еще каких проверок, согласно выбранной семантики закодированного значения.
Вы, наверное, имеете в виду не количество состояний, а количество переменных, описывающих состояние.
V>Не зря я в самом начале предложил абстрагироваться от идентификаторов блокировок, а просто пронумеровать их, связав в иерархию, чтобы не засорять моск ненужной информацией.
Ну так я уже неделю жду, пока вы "пронумеруете" идентификаторы блокировок. Хотя бы для ровно одной гранулярности, и простой системы shared/update/exclusive, с общепринятой матрицей совместимости.
V>Стадия анализа задач, про которую я уже совсем не скромно намекаю который пост подряд, должна дать тебе отображение прикладных требований на выбранную схему СМО, адекватную поставленной задаче, включая графы переходов, если состояний более одного.
Эта стадия пройдена задолго до меня. А вам, прежде чем бросаться закидывать шапками хорошо известную проблематику, стоило бы всё же почитать какие-то основы. Даже учебника пользователя СУБД достаточно для того, чтобы понять, как именно обеспечиваются различные уровни изоляции, включая serializable, при помощи различных протоколов синхронизации.
V>И да, просьба заканчивать буксовать на "единственном семафоре", бо в подробном описании различных алгоритмов RO-RW блокировки ключевой семафор я называл целевым, но никогда не было речи о том, что один семафор может обслужить разные уровни иерархии, если есть такая потребность в разных уровнях. Один он будет на своём уровне.
Речь была о том, что table scan должна обслужить единственная interlocked переменная. Булшит про СМО появился позже, в попытках отвлечь рассуждение от кода.
V>К тому же, достаточно того, что упоминался "турникет" — а это семафор со счётчиком 1, где "турникетом" его делает способ оперирования этим семафором. Т.е. уже минимум два семафора. И еще требуется сериализация писателей, если есть желание решить проблему голодания писателей в случае, когда мы не управляем задачами, а пользуем семафоры уровня ОС — уже 3 семафора. И это только для разруливания проблематики совместного или монопольного доступа к одному ресурсу.
Вот видите, как здорово. Вижу, у вас начинает появляться понимание, что одной interlocked инструкцией мы никак не обойдёмся.
V>А ресурсы у тебя иерархические, как матрёшки, и в точности такая же задача возникает на каждом слое матрёшки — об этом было сказано вообще сразу и шло как контекст.
V>(при переходе от таблицы к странице, до этого при переходе от базы к таблице, до этого на уровне схемы)
Отож. Поэтому для того, чтобы прочитать десяток строк из таблицы по индексу, придётся побегать по нескольким десяткам "семафоров".
V>И я уже напоминал, что различные уровни изоляции достигаются не только и не столько за счёт блокировок, а чаще за счёт алгоритмов, растущих из банального COW.
V>Т.е., на некоторых уровнях изоляции конкурентные чтения и даже транзакционная запись может быть, но совершенно без эксклюзивных блокировок "ресурсов", как тебе такое?
Такое — плохо. Недаром в стандарте ANSI нет такого уровня изоляции, при котором можно обойтись без эксклюзивных блокировок.
И, кстати, версионников, которые бы при записи обходились без эксклюзивных блокировок, в природе тоже не существует.
V>А недефолтные алгоритмы — сплошное ноу-хау, увы.
V>И почему так — именно тебе не так давно рассказывал уже.
А, вот и секретность пожаловала в студию.
V>Пфф, очередной культурный шок.
V>Коду вообще требуется, чтобы его кто-то писал.
А то.
V>Выглядит как когнитивный диссонанс по причине "само не работает".
V>Т.е., на уровне мозжечка привык рассуждать в духе "с неба упало и само работает", вот до чего дотнет доводит. ))
Вижу, я плохо объяснил вам тему обсуждения, т.к. вы неверно интерпретируете мои вопросы. Попробую ещё раз объяснить: у меня нет проблемы с пониманием того, как устроена механика управления блокировками в СУБД.
И сомнений в том, что она поддаётся реализации, тоже нету. В конце концов — у нас же есть готовые примеры, многие из которых опубликованы в исходниках.
Я просто пытаюсь оценить затраты на эту механику при исполнении реальных запросов. Чтобы эти затраты оценить, нужно хотя бы примерно себе представлять, как всё это устроено внутри.
Когда я говорю "там сложнее, чем вы пишете", то сложно не означает "непонятно". Сложно означает "вычислительно сложно", т.е. то, что выглядит с т.з. диаграммы на доске как "захват блокировки на строку", внутри реализуется как множество инструкций, часть из которых требуют синхронизации (хотя бы через interlocked). А вы делаете совершенно бредовые утверждения про вот эту вычислительную сложность, и ещё и ухитряетесь с менторским видом высказывать не относящиеся к делу банальности.
V>Только в твоём случае этот феномен доходит до крайности: "обычные люди это сделать не смогли бы, такие как я или любые мои собеседники в интернете".
С чего вы взяли? Вы берётесь показывать решение — и сразу сливаетесь, как только речь заходит о коде. Как, впрочем, и более-менее всегда. Начиная от вычисления 2d-фильтров, и заканчивая планами запросов.
Про способности "обчных людей" я ничего не предполагаю — я всего лишь хочу увидеть код, о котором вы так смело рассуждаете.
V>Но ты же не мог всерьёз просить меня ответить тебе работающими исходниками полноценной СУБД? ))
Достаточно умозрительного кода, иллюстрирующего основные тезисы.
V>С херна ли эффективнее существующих?
V>Кто тебе такое обещал?
Вот это поворот! Мы же как раз начали с построения сервера приложений, совмещённого с СУБД, для максимизации эффективности: https://rsdn.org/forum/flame.comp/8103984.1
V>Обсуждение возникло по причине того, что ты злостно путаешь уровни изоляции и "блокировки".
Я один раз с недосыпа оговорился. А вот у вас пока что затруднения и с пониманием того, что такое ACID, и с уровнями изоляции. С иерархией блокировок, я вижу, наступает понимание.
V>А будучи реализованным через ручное управление заявками, можно подключать наработки из СМО — они туда сам просятся и так, вообще-то, и поступают при разработке подобных систем сложных диспетчеризаций заявок.
Ну, про ручное управление тема пока что не раскрыта.
V>Твоё зацикливание на СУБД говорит лишь о том, что ты не понимаешь сам этот класс задач.
IP-телефония, на фоне СУБД, с точки зрения разделяемых ресурсов — так, детский лепет на лужайке.
V>Еще тебе говорилось, что вместо "различных видов блокировок" (как ты рассуждал в начале этой беседы), стоит оперировать не их именами собственными, а достаточно рассматривать их нумерованную иерархию, бо на каждом уровне иерархии в плане отношений с низлежащим слоем в СУБД соблюдается строгое подобие от уровня выше.
Да не сможете вы уложить update/exclusive/shared ни в какую нумерованную иерархию. Вы по прежнему путаете гранулярность блокировок и их виды.
То, что вы описываете — и есть те самые intent блокировки, которых по-вашему не существует. И (сюрприз-сюрприз), можно обходиться и без них! Вот только тогда код захвата крупногранулярной блокировки становится громоздким и дорогостоящим.
V>СМО — это не абстрактные рассуждения, а вполне конкретные, и сразу же было сказано почему — так УДОБНЕЙ.
Удобней чем что? Чем принятая в мире СУБД классификация блокировок? Ну-ну.
V>Да, просимулировать происходящее можно и на обычных семафорах, в т.ч. используя код того полусамописного семафора, который я дал, т.к. при симуляции обычно не интересует быстродействие эмулятора, интересует происходящее в количественном выражении, т.е., где, какие, сколько операций, какова вероятность/статистика развития тех или иных сценариев из большого комбинаторного их сочетания и т.д. и т.п.
Что именно вы предлагаете просимулировать и зачем? Убедиться, что ваша реализация падает на простейших сценариях вроде update table1 set field1 = 0 where field2 = 0?
V>И?
V>Одна на таблицу.
V>До этого одна на базу.
V>До этого одна на схему.
V>До этого минимум одна на входной механизм самописных очередей задач.
Ага. И после этого — ещё и постраничные блокировки по мере сканирования. Т.к. захватывать shared table lock на всё время транзакции — свинство даже на уровне serializable.
V>До этого на пул запросов, на ресурсы, требуемые для работы хотя бы парсера и валидации SQL, составления плана запроса согласно текущей схемы базы и т.д. и т.п.
Вот этого ничего не надо, т.к. ресурсы парсера и валидатора — не уникальные, и доступ к ним в синхронизации не нуждается. А блокировка на схему, от которой зависит валидность плана (и возможность построить план, если это нужно) уже упомянута.
V>Но в дефолтном алгоритме RO-RW блокировки аж три семафора, три недешёвых вызова АПИ ОС только на реализацию всего двух состояний СМО всего одного уровня из иерархии.
V>Разве не ого? ))
V>А если состояний "ячейки" СМО более одного (как ты там упоминал еще виды блокировок) — то и вовсе по-дефолту обычно делают на условной переменной, со всеми её suspicion wake up и чудовищной вероятности столкновения потоков на единственном мьютексе, обслуживающем эту условную переменную.
Воот. Постепенно вырисовывается понимание, почему у меня скепсис относительно больших выигрышей за счёт компиляции запроса. При "интерпретации" план строится из относительно крупных кубиков — ну типа "просканировать индекс вот с таким предикатом". Накладные расходы на состыковывание кубиков сопоставимы (на первый взгляд) с накладными расходами на "проверить, нужно ли захватить row lock для выполнения bookmark lookup, или уже захвачен более крупногранулярный lock".
S>> Показываю на пальцах: Допустим, у нас уровень параллельности = 8. Разделяемые блокировки на ресурсе реализуются как acquire(1), эксклюзивные — как acquire(8).
S>>Блокировки строки — опять же, acquire(1), страницы — acquire(8).
V>8 будет если на данном уровне иерархии ровно 8 фактических shared-владений.
V>А если одно?
V>А если ни одного?
Вот поэтому-то я и хотел, чтобы вы привели код. Какой смысл обсуждать свойства "моей" реализации "вашей" идеи?
Покажите мне код, который делает то, что вы написали.
V>Ты там цикл не видел разве в псевдокоде обновления счётчика?
А цикл-то тут при чём? Он же не от количества "владений" зависит, а только от наличия конфликтующих обновлений состояния самого семафора.
S>>Транзакция A продолжает работу, захватывает shared lock на записи номер 4, 5, 6, 7, 8, 9, 10 при помощи Page(42).acquire(1) — успешно.
V>Зачем?
V>Ты ведь уже shared-владешь страницей, т.е. можешь читать данные этой страницы "просто так".
Эмм, а откуда транзакция "знает", что уже shared-владеет страницей? В коде вашего семафора нет никакой информации о том, кто чем владеет. Если вы подразумеваете, что транзакция (или менеджер блокировок) ведёт таблицы владения, то стоимость операций над этими таблицами тоже нужно учитывать. То есть опять — даже в простейшем случае одноуровневой иерархии ресурсов у нас никак не хватает "единственной interlocked операции".
V>Это тогда надо заходить на страницу под другой дисциплиной блокировки, например, "я могу блокировать с разными дисциплинами произвольные твои строки конкуретно", и тогда страница не пустит shread-RO "клиентов" к себе.
V>И тогда acquire ты будешь запрашивать у конкретной строки, а не страницы, бо на страницу под нужной тебе прикладной дисциплиной ты уже вошёл.
Совершенно верно тогда надо помимо захвата "разделяемой блокировки" на уровне страницы дополнительно захватывать нужный вид блокировки на уровне строки. Я взял "разделямую" в кавычки потому, что на самом деле intent lock не во всех отношениях ведёт себя как просто shared lock, хотя в упрощённой модели их можно использовать взаимозаменяемо.
Как мы уже выяснили, нельзя просто захватить row lock — надо сначала захватить intent-блокировки на базу, таблицу, страницу.
V>Или, при реализации COW, если страница захвачена читателями и для тех уровни изоляции позволяют, твоя транзакция может поступить иначе — сделать копию страницы, обновить строки в этой копии, а потом обновит цепочку страниц (про COW тебе тоже говорилось сразу).
И я про COW сразу отвечал, что он убивает производительность даже при чтениях. Потому, что механика определения того, какая версия страницы является актуальной для данной транзакции, небесплатна.
V>А текущая страница для shared RO-заявок переходит в режим фантома и будет жить до тех пор, пока ею shared-владеют, потом тихо умрёт.
Вот вы же опять манипулируете какой-то магией. Вот это вот всё — "режим фантома" и прочее — это же всё не божьим промыслом происходит. Кто-то должен отслеживать состояния транзакций, которые видят фантомные страницы, и своевременно эти страницы помечать как свободные. Вы уверены, что понимаете, как это повлияет на производительность обработки запросов?
V>Здесь опять путаешь уровни иерархий.
V>Говорил же тебе — тупо пронумеруй, меньше вероятность ошибиться. ))
Нет, просто вы плохо объясняете свои мысли. Именно поэтому я хотел от вас кода. Потребовалось всего-то 20 постов туда-сюда, чтобы мы сошлись на недостаточности одной interlocked операции для сканирования таблицы.
V>Но тогда привет турникетам и база быстро встанет колом, при таком-то кол-ве уровней иерархии.
Нет, если мы чётко понимаем, чем отличаются блокировки строк от блокировок страниц, то никаких вставаний колом не происходит. Просто для модификации одной строки нужно захватить две блокировки — строки и страницы (в предположении, что блокировка таблицы уже захвачена).
V>Надеюсь, на следующей итерации у тебя будет получше с пониманием целевого — а зачем вообще требуется нетривиальная возня с режимами СМО.
V>Про уровни параллелизма когда речь, то ресурсом является вычислительный блок, (процессор, ядро, поток в ядре и т.д., смотря как железка устроена).
Ага. Именно поэтому в примере выше было взято число 8. А не, скажем, 2000 — что больше похоже на количество записей на странице.
V>Например, транзакция, выполняющая COW, делает обновления многошагово, т.е. через последовательность заявок.
V>Первая заявка иницировала транзакцию, произвела первое чтение.
V>Если были конкуретные чтения или даже чтения-записи, то в рамках транзакции могут создаваться копии данных, как упомянул выше. Одна операция чтения — один уровень параллелизма (допустим для простоты). В системах с отказами и последействиями, например, нужной страницы нет в памяти, — будет отказ и новая постановка в очередь с новым состоянием заявки (см эту категоризацию СМО, сама эта категоризация даёт много подсказок).
Я по-прежнему не вижу, чем эта категоризация СМО может помочь при проектировании СУБД.
V>Освободили "ресурс", т.е. процессор-ядро-поток, исполняем следующую заявку. Вполне может быть, что следующей будет заявка из той же самой транзакции, она ведь при инициализации могла накидать сразу пачку заявок, т.е. вероятность последовательного расположения заявок от одной транзакции будет высокой.
Ну вот с этой стороны я пока не очень понимаю, как это всё строить с точки зрения реализации. Что такое "заявка"? Task<T>?
V>В общем, тебе придётся найти подтверждение этой (уже не первой) инсинуации или прилюдно извиниться.
Какой?
V>Ага, т.е. я прав — ты теперь пытаешься замылить саму суть обсуждения, как обычно ища виноватых в своем непонимании на стороне. ))
Нет, я по-прежнему пытаюсь вернуть вас к сути. Обратите внимание — уже 20 постов я задаю одни и те же вопросы.
V>"На коленке" я жду от тебя куда как более примитивную часть этого кода — простое управление shared-exclusive доступом ко всего одному ресурсу, т.е. очень простой СМО из двух состояний.
Давайте лучше вы напишете эту часть кода. Которая хотя бы будет реентерабельной
Когда сделаете, можно будет перейти к вопросам со звёздочкой — например, что делать с deadlock? Но об этом поговорим потом
V>И жду понимание того, как с помощью аналогичных механизмов, раскиданных по разным уровням иерархий, реализовать комбинаторику возникающих сценариев при организации различных уровней изоляции.
вся "комбинаторика" при реализации различных уровней изоляции хорошо известна. Для неё не нужна никакая теория СМО. И даже гранулярность блокировок уровню изоляции ортогональна.
Уровни изоляции описываются тем, в какой момент захватываются и отпускаются shared lock, независимо от их гранулярности. Ну, кроме serializable, где важными становятся table и range блокировки.
А вы опять умничаете в области, с которой плохо знакомы.
V>И тогда ты будешь готов согласиться с самыми первыми моими заявлениями, насчёт простой нумерации уровней иерархий.
V>Ну конечно же да.
V>Потому что в какой-нить транзакции я могу по некоему условию менять даже схему таблиц, т.е. заранее неизвестно.
V>И тогда ACID на всех уровнях, кроме сериализованного не будет соблюдаться от момента начала такого участка транзакции, т.е. базе придётся войти в сериализованный режим.
)
Господи, чушь-то какая. Изменения схемы таблиц совершенно не требуют никакого глобального мутекса. Вы, наверное, не видели взрослых СУБД.
Изменения схемы прекрасно живут на блокировках схемы. Я могу стартовать транзакцию, выполнить в ней create table и пойти покурить, не делая commit — и никакие другие транзакции этого не заметят.
Я могу сделать alter table drop column — и он дождётся, пока последняя транзакция, использующая эту таблицу завершится, и дропнет колонку.
И если параллельно со мной никакие транзакции не работают конкретно с этой таблицей, то они прекрасно продолжат работать, как ни в чём не бывало.
Я не знаю, что вы называаете "сериализованным режимом", но если речь про захват глобального мьютекса — то нет, вы не угадали, для изменений схемы он не нужен, и настоящие СУБД работают не так.
S>>Почитайте любой учебник по СУБД.
V>Опять мимо, тут нужен учебник не для пользователя СУБД, а для разработчика СУБД (угу, не разработчика прикладной базы данных, а самой СУБД).
Из тех четырёх, которые я привёл, два — для разработчиков СУБД.
V>СМО — неотъемлимая часть такого учебника.
Давайте так: дайте ссылку на главу по СМО в любом учебнике по разработке СУБД на ваш вкус.
S>>Глобальная блокировка является достаточным, но вовсе не необходимым условием для serializability.
V>Опять та же ошибка — зависит от уровня иерархии оперируемой сущности.
Что именно зависит? Значение термина "глобальная блокировка"? Очень странно. Я полагал, что глобальность — она на то и глобальность, что одна на всех.
V>СУБД тут ни разу не уникальны, т.е. до смешного — сама попытка отослать к учебнику по некоей СУБД демонстрирует не просто непонимание предметной области, а отсутствие простой "записи" в эрудиции о наличии самой предметной области.
Не по "некоей СУБД", к учебнику по разработке СУБД.
V>В английском различают термины lock и block, в русском языке возникает путаница.
V>Поэтому, стоит использовать или английскую терминологию, или длинную русскую, навроде "блокировка для совместного доступа по чтению".
V>В русскоязычном IT "просто блокировка" означает blocking lock на английском.
Именно поэтому я ни разу не использовал термин "просто блокировка".
И в большинстве мест я использовал английскую терминологию. Очень странно с вашей стороны делать вид, что вы двадцать постов подряд не понимали, что мы обсуждаем СУБД.
V>Простой пример:
V>вот у тебя запрос с агрегацией только по чтению.
V>Какие могут быть уровни изоляции:
V>- никакой, т.е. код исполнения запроса читает и те данные, что уже есть и те, что были изменены; есть вероятность, что результатом агрегата станет такое значение, которое не могло быть ни в одном из "конечных" состояний базы, т.е. между транзакциями.
V>- ACID-изоляция; конкурентные транзакции не могут изменить твои данные; это может быть достигнуто исключительным доступом к таблице (блокировкой), либо же, транзакция, выполняющая агрегирующее чтение, будет работать с копиями данных, на момент начала чтения; вот тебе пример того, что блокировки и уровни изоляции — совсем не одно и то же;
V>- откаты/обработка конфликтов; например, самостоятельный перезапуск базой запроса, если уже просмотренные данные были изменены конкурирующими транзакциями;
V>Первый случай еще обычно разделяют на два-три, по степени допущения несогласованности, но пример с агрегатом был взят именно затем, чтобы отличаться от "простой выборки".
V>Именно в этом месте "учебники по СУБД" нехило улыбают, бо я привел пример одной из самых распространнёных ошибок подсчёта агрегата при неверно выбранном уровне изоляции.
Ок, хорошо, что вы привели конкретный пример. Его хотя бы можно обсуждать. Вот для банального агрегатного запроса типа select sum(salary) from employees; какой нужен уровень изоляции? Внезапно — repeatable read.
Если мы пошли по пути блокировок, внезапно оказывается, что совершенно необязательно захватывать исключительный доступ к таблице.
Достаточно удерживать разделяемую блокировку до конца транзакции.
Более того — даже делать её уровня таблицы совершенно необязательно. Можно захватывать shared page locks по мере сканирования данных — и всё ещё никаких проблем с сериализуемостью не будет.
Понятно, почему так происходит, или надо подробно разжевать?
S>>Например, рассмотрите классическую задачу атомарного перевода денег между счетами, и убедитесь, что repeatable read достаточно для обеспечения согласованности данных в этой задаче.
V>Но недостаточно для моей задачи.
V>Если в рамках конкурирующей транзакции были изменены две строки данных, одну из них уже прочитали и подсчитали в агрегате, другую прочитали уже после фиксации конкурентной транзакции — агрегат выдаст фуфел на выходе.
Именно поэтому для данного запроса нужен repeatable read. А вот serializable — избыточен.
V>Ну да, вместо конструктива, вместо попыток понять конкретно обсуждаемый сабж, занимаешься пусканием "дымовой завесы".
Ну, ок. Не хотите читать учебники — не читайте. Но не обижайтесь, когда я буду вас тыкать носом в непонимание вещей, которые там изложены простым языком.
V>Какого именно утверждения?
Про то, что ACID достижим только при помощи одного глобального мьюьекса.
V>Системы резервирования работают, внезапно, через различные допущения с целью того самого ускорения обслуживания.
V>Например, работавшая (не знаю как сейчас) еще с 90-х система резервирования ЖД-билетов работала через пулы, раскиданные по звездообразной схеме.
V>При исчерпании пула у локального и вышестоящего узла, резервирование порой выполняется ооочень долго.
V>Потому что чудес не бывает.
V>Ты бы эта хотя бы свои собственные примеры проверял, прежде чем приводить их якобы в пример якобы своей правоты.
Печаль, печаль. Подумайте ещё раз: если бы ACID был недостижим без одного глобального mutex, то резервирование бы было медленным всегда, а не при "исчерпании пула".
V>А с современными скоростями интернета и быстродействием серверов и этого уже не требуется, ведь "один мьютекс" нужен не на всю базу ЖД-трафика, а на конкретный рейс, т.е., подозреваю, с пулами сегодня никто не возится, достаточно обеспечить fault tolerance, задача поиска свободных мест из десятков-сотен значений сегодня занимает микросекунды.
Хм, опять началась какая-то каша в голове. Что такое "глобальный мьютекс на конкретный рейс"? И чем он отличается от "локального мьютекса"?
Вы совершенно напрасно изобретаете какую-то самодеятельную терминологию.
V>И да, при попытке фактического резервирования repetable read уже не катит, упс?
С чего вы это взяли? Смотря что входит в "фактическое резервирование" — может хватить даже read committed. Попробуйте продемонстрировать контрпример
V>Зато прекрасно катит автоматический откат и повторное резервирование, скажем, на рейсы с пересадкой, когда при последовательном резервировании одно из них вышло неудачным.
V>(третий упомянутый алгоритм обеспечения согласованности)
Да, расскажите мне про optimistic locking. Но лучше — в другой раз. Там всё сложнее, чем кажется на первый взгляд, а вы ещё с pessimistic locking не разобрались.
V>Будем разгребать:
V>Синклер:
V>- Неадекватное решение — реализация набора видов блокировок в СУБД через самописанный семафор, поддерживающий отрицательные значения счётчиков.
V>Я:
V>- твоё утверждение ложно, бо в СУБД широко используют именно самописные семафоры.
Ну разве можно так бессовестно передёргивать? Все ходы записаны.
Я вас просил привести решение задачи — вы начали с описания некоего решения на основе семафора; затем оговорились, что это решение — плохое, поэтому нужно добавить ещё один семафор, впрочем это решение тоже плохое, поэтому нужно использовать пользовательские очереди задач.
При этом изо всего этого ряда решений код вы привели только для первого, и то неполный, а в стиле "вот вам int main(){}, остальное впишите между фигурными скобками".
Я не утверждаю, что вообще применение каких-либо "самописанных семафоров" плохо. Я говорю, что конкретно ваше решение, предложенное в этой ветке — плохое.
V>Синклер:
V>- нет, блокировка строки и блокировка страницы делается через разные ресурсы, а не через один семафор.
V>Я:
V>- а как соотносятся твои "разный" (в плане экземпляров) и "самописный"?
Очень просто. Я пытаюсь вернуться к мифической "одной interlocked операции", которой было достаточно для сканирования таблицы.
(Уже начинаю подозревать, что речь шла о глобальном семафоре, который, по-вашему, является единственным средством достижения истинного ACID. Нет, такая СУБД будет катастрофически неэффективна на практике, независимо от того, компилируются ли планы запросов статически.)
Пока что вам никак не удаётся показать эту операцию. В основном — потому, что вы бегаете от вопросов про код, отделываясь общими рассуждениями про последовательность решения задач и отсылками к теории СМО.
Интересует не теория, а практика.
V>- и почему "блокировка" делается через "ресурсы"? ))
Нужно же как-то называть то, что мы захватываем и отпускаем. Общепринятое название — просто lock, но у вас какая-то беда с пониманием термина "блокировка".
V>В общем, просьба не применять пока мест просто термин "блокировка", бо ты используешь его неверно уже десяток постов подряд.
Ещё раз умоляю — откройте ссылку на википедию, которую я вам дал, и прекратите кривляться.
V>Если нет — вот одна из первых ссылок в гугле, просто прочти первый абзац, там сходу понятно, что принято называть "ресурсом" в СМО:
V>https://cyberleninka.ru/viewer_images/16416395/f/1.png
Ну прикольно. Вы даже не обратили внимания, что определения термина "ресурс" в вашем источнике нет. Просто нечто, что необходимо для выполнения заявки.
Ок, можно отбросить этот термин и пользоваться только общепринятым термином "блокировка" из мира СУБД. Ссылку на его определение я вам дал.
Если же мы вернёмся в мир СУБД, то в нём "ресурсом" называется некоторый элемент базы данных — строка, страница, таблица, индекс, key range, обьект схемы, вся база данных и т.п.
V>Чтобы дойти до операции блокировки строки, ты должен сначала пройти уровень блокировки страницы.
В общем случае нет. Просто альтернатива настолько неэффективна, что в большинстве реализаций перед захватом блокировки на "кусочек" композитного ресурса делается транзитивный захват intent lock-ов на все вышестоящие ресурсы в иерархии.
V>Т.е. не заблокировать страницу, но "отметиться" на каком-нить примитиве сигнализации уровня страницы, и, отметившись удачно, запретить конкурентным задачам блокировать страницу. Т.е., shared-владение ресурсом запрещает блокировку ресурса (т.е. запрещает монопольное овладение ресурсом, а не производит это овладение, как может показаться из твоей ошибочной терминологии), т.е., чтобы заблокировать страницу, небходимо будет дождаться освобождения всех shared-владений.
Ещё раз порекомендую вам прочитать общепринятое определение термина "блокировка" применительно к СУБД. Оно гораздо удобнее, потому что без него придётся мучиться с многословными формулировками.
Вот к примеру, то, что вы написали выше "пройти уровень блокировки страницы" — не имеет чёткого смысла. Вам приходится выкручиваться при помощи конструкции "попытаться запретить конкурентным задачам блокировать страницу".
А в мире СУБД просто говорят "транзакция захватывает intent exclusive lock на страницу", и всем всё понятно.
V>Хотя ни о каком прикладном отношении RO-RW речь не идёт, речь идёт только о shared, либо монопольном владении, просто при shared-владении безопасны конкурентные чтения, вот и всё кино. Но всё это уже уровень прикладной семантики, т.е. интерпретирования состояний неких переменных, изменяемых атомарно.
Совершенно верно. Поэтому в мире СУБД про читателей и писателей говорить не принято (в отличие, скажем, от мира параллельного программирования, где всё называют именно так (тыц, тыц, тыдыц). А принято так и называть — shared lock и exclusive lock.
V>В данном случае у некоей простейшей СМО (или простейшей такой подсистемы) есть всего два состояния — shared и exclusive.
V>Как обыграть эту пару состояний — я рассказал более чем доступно, ИМХО.
Ну, для начала, там, конечно же, не пара состояний . Состояний там примерно 2^32- в зависимости от того, ограничиваем ли мы максимальное количество одновременных читателей.
Вы привели неполную и неудачную реализацию одного из способов обыграть эту "пару состояний". Даже в википедии приведено ещё два.
В реальных СУБД помимо shared и exclusive применяют update lock, который уже совсем сложно описать одной фразой вроде "запретить конкурентным задачам блокировать страницу".
Кроме того, нужна ещё и реентерабельность, которую ваше решение не обеспечивает и близко, а также умение "апгрейда" блокировки с shared в exclusive. Этого у вас тоже нет.
V>Если состояний будет более одного, их, например, можно будет закодировать в старших битах счётчика, и тогда код условия под if потребует наложения маски битового тела счётчика или еще каких проверок, согласно выбранной семантики закодированного значения.
Вы, наверное, имеете в виду не количество состояний, а количество переменных, описывающих состояние.
V>Не зря я в самом начале предложил абстрагироваться от идентификаторов блокировок, а просто пронумеровать их, связав в иерархию, чтобы не засорять моск ненужной информацией.
Ну так я уже неделю жду, пока вы "пронумеруете" идентификаторы блокировок. Хотя бы для ровно одной гранулярности, и простой системы shared/update/exclusive, с общепринятой матрицей совместимости.
V>Стадия анализа задач, про которую я уже совсем не скромно намекаю который пост подряд, должна дать тебе отображение прикладных требований на выбранную схему СМО, адекватную поставленной задаче, включая графы переходов, если состояний более одного.
Эта стадия пройдена задолго до меня. А вам, прежде чем бросаться закидывать шапками хорошо известную проблематику, стоило бы всё же почитать какие-то основы. Даже учебника пользователя СУБД достаточно для того, чтобы понять, как именно обеспечиваются различные уровни изоляции, включая serializable, при помощи различных протоколов синхронизации.
V>И да, просьба заканчивать буксовать на "единственном семафоре", бо в подробном описании различных алгоритмов RO-RW блокировки ключевой семафор я называл целевым, но никогда не было речи о том, что один семафор может обслужить разные уровни иерархии, если есть такая потребность в разных уровнях. Один он будет на своём уровне.
Речь была о том, что table scan должна обслужить единственная interlocked переменная. Булшит про СМО появился позже, в попытках отвлечь рассуждение от кода.
V>К тому же, достаточно того, что упоминался "турникет" — а это семафор со счётчиком 1, где "турникетом" его делает способ оперирования этим семафором. Т.е. уже минимум два семафора. И еще требуется сериализация писателей, если есть желание решить проблему голодания писателей в случае, когда мы не управляем задачами, а пользуем семафоры уровня ОС — уже 3 семафора. И это только для разруливания проблематики совместного или монопольного доступа к одному ресурсу.
Вот видите, как здорово. Вижу, у вас начинает появляться понимание, что одной interlocked инструкцией мы никак не обойдёмся.
V>А ресурсы у тебя иерархические, как матрёшки, и в точности такая же задача возникает на каждом слое матрёшки — об этом было сказано вообще сразу и шло как контекст.
V>(при переходе от таблицы к странице, до этого при переходе от базы к таблице, до этого на уровне схемы)
Отож. Поэтому для того, чтобы прочитать десяток строк из таблицы по индексу, придётся побегать по нескольким десяткам "семафоров".
V>И я уже напоминал, что различные уровни изоляции достигаются не только и не столько за счёт блокировок, а чаще за счёт алгоритмов, растущих из банального COW.
V>Т.е., на некоторых уровнях изоляции конкурентные чтения и даже транзакционная запись может быть, но совершенно без эксклюзивных блокировок "ресурсов", как тебе такое?
Такое — плохо. Недаром в стандарте ANSI нет такого уровня изоляции, при котором можно обойтись без эксклюзивных блокировок.
И, кстати, версионников, которые бы при записи обходились без эксклюзивных блокировок, в природе тоже не существует.
V>А недефолтные алгоритмы — сплошное ноу-хау, увы.
V>И почему так — именно тебе не так давно рассказывал уже.
А, вот и секретность пожаловала в студию.
V>Пфф, очередной культурный шок.
V>Коду вообще требуется, чтобы его кто-то писал.
А то.
V>Выглядит как когнитивный диссонанс по причине "само не работает".
V>Т.е., на уровне мозжечка привык рассуждать в духе "с неба упало и само работает", вот до чего дотнет доводит. ))
Вижу, я плохо объяснил вам тему обсуждения, т.к. вы неверно интерпретируете мои вопросы. Попробую ещё раз объяснить: у меня нет проблемы с пониманием того, как устроена механика управления блокировками в СУБД.
И сомнений в том, что она поддаётся реализации, тоже нету. В конце концов — у нас же есть готовые примеры, многие из которых опубликованы в исходниках.
Я просто пытаюсь оценить затраты на эту механику при исполнении реальных запросов. Чтобы эти затраты оценить, нужно хотя бы примерно себе представлять, как всё это устроено внутри.
Когда я говорю "там сложнее, чем вы пишете", то сложно не означает "непонятно". Сложно означает "вычислительно сложно", т.е. то, что выглядит с т.з. диаграммы на доске как "захват блокировки на строку", внутри реализуется как множество инструкций, часть из которых требуют синхронизации (хотя бы через interlocked). А вы делаете совершенно бредовые утверждения про вот эту вычислительную сложность, и ещё и ухитряетесь с менторским видом высказывать не относящиеся к делу банальности.
V>Только в твоём случае этот феномен доходит до крайности: "обычные люди это сделать не смогли бы, такие как я или любые мои собеседники в интернете".
С чего вы взяли? Вы берётесь показывать решение — и сразу сливаетесь, как только речь заходит о коде. Как, впрочем, и более-менее всегда. Начиная от вычисления 2d-фильтров, и заканчивая планами запросов.
Про способности "обчных людей" я ничего не предполагаю — я всего лишь хочу увидеть код, о котором вы так смело рассуждаете.
V>Но ты же не мог всерьёз просить меня ответить тебе работающими исходниками полноценной СУБД? ))
Достаточно умозрительного кода, иллюстрирующего основные тезисы.
V>С херна ли эффективнее существующих?
V>Кто тебе такое обещал?
Вот это поворот! Мы же как раз начали с построения сервера приложений, совмещённого с СУБД, для максимизации эффективности: https://rsdn.org/forum/flame.comp/8103984.1
Автор: vdimas
Дата: 01.10.21
Дата: 01.10.21
V>Обсуждение возникло по причине того, что ты злостно путаешь уровни изоляции и "блокировки".
Я один раз с недосыпа оговорился. А вот у вас пока что затруднения и с пониманием того, что такое ACID, и с уровнями изоляции. С иерархией блокировок, я вижу, наступает понимание.
V>А будучи реализованным через ручное управление заявками, можно подключать наработки из СМО — они туда сам просятся и так, вообще-то, и поступают при разработке подобных систем сложных диспетчеризаций заявок.
Ну, про ручное управление тема пока что не раскрыта.
V>Твоё зацикливание на СУБД говорит лишь о том, что ты не понимаешь сам этот класс задач.
IP-телефония, на фоне СУБД, с точки зрения разделяемых ресурсов — так, детский лепет на лужайке.
V>Еще тебе говорилось, что вместо "различных видов блокировок" (как ты рассуждал в начале этой беседы), стоит оперировать не их именами собственными, а достаточно рассматривать их нумерованную иерархию, бо на каждом уровне иерархии в плане отношений с низлежащим слоем в СУБД соблюдается строгое подобие от уровня выше.
Да не сможете вы уложить update/exclusive/shared ни в какую нумерованную иерархию. Вы по прежнему путаете гранулярность блокировок и их виды.
То, что вы описываете — и есть те самые intent блокировки, которых по-вашему не существует. И (сюрприз-сюрприз), можно обходиться и без них! Вот только тогда код захвата крупногранулярной блокировки становится громоздким и дорогостоящим.
V>СМО — это не абстрактные рассуждения, а вполне конкретные, и сразу же было сказано почему — так УДОБНЕЙ.
Удобней чем что? Чем принятая в мире СУБД классификация блокировок? Ну-ну.
V>Да, просимулировать происходящее можно и на обычных семафорах, в т.ч. используя код того полусамописного семафора, который я дал, т.к. при симуляции обычно не интересует быстродействие эмулятора, интересует происходящее в количественном выражении, т.е., где, какие, сколько операций, какова вероятность/статистика развития тех или иных сценариев из большого комбинаторного их сочетания и т.д. и т.п.
Что именно вы предлагаете просимулировать и зачем? Убедиться, что ваша реализация падает на простейших сценариях вроде update table1 set field1 = 0 where field2 = 0?
V>И?
V>Одна на таблицу.
V>До этого одна на базу.
V>До этого одна на схему.
V>До этого минимум одна на входной механизм самописных очередей задач.
Ага. И после этого — ещё и постраничные блокировки по мере сканирования. Т.к. захватывать shared table lock на всё время транзакции — свинство даже на уровне serializable.
V>До этого на пул запросов, на ресурсы, требуемые для работы хотя бы парсера и валидации SQL, составления плана запроса согласно текущей схемы базы и т.д. и т.п.
Вот этого ничего не надо, т.к. ресурсы парсера и валидатора — не уникальные, и доступ к ним в синхронизации не нуждается. А блокировка на схему, от которой зависит валидность плана (и возможность построить план, если это нужно) уже упомянута.
V>Но в дефолтном алгоритме RO-RW блокировки аж три семафора, три недешёвых вызова АПИ ОС только на реализацию всего двух состояний СМО всего одного уровня из иерархии.
V>Разве не ого? ))
V>А если состояний "ячейки" СМО более одного (как ты там упоминал еще виды блокировок) — то и вовсе по-дефолту обычно делают на условной переменной, со всеми её suspicion wake up и чудовищной вероятности столкновения потоков на единственном мьютексе, обслуживающем эту условную переменную.
Воот. Постепенно вырисовывается понимание, почему у меня скепсис относительно больших выигрышей за счёт компиляции запроса. При "интерпретации" план строится из относительно крупных кубиков — ну типа "просканировать индекс вот с таким предикатом". Накладные расходы на состыковывание кубиков сопоставимы (на первый взгляд) с накладными расходами на "проверить, нужно ли захватить row lock для выполнения bookmark lookup, или уже захвачен более крупногранулярный lock".
S>> Показываю на пальцах: Допустим, у нас уровень параллельности = 8. Разделяемые блокировки на ресурсе реализуются как acquire(1), эксклюзивные — как acquire(8).
S>>Блокировки строки — опять же, acquire(1), страницы — acquire(8).
V>8 будет если на данном уровне иерархии ровно 8 фактических shared-владений.
V>А если одно?
V>А если ни одного?
Вот поэтому-то я и хотел, чтобы вы привели код. Какой смысл обсуждать свойства "моей" реализации "вашей" идеи?
Покажите мне код, который делает то, что вы написали.
V>Ты там цикл не видел разве в псевдокоде обновления счётчика?
А цикл-то тут при чём? Он же не от количества "владений" зависит, а только от наличия конфликтующих обновлений состояния самого семафора.
S>>Транзакция A продолжает работу, захватывает shared lock на записи номер 4, 5, 6, 7, 8, 9, 10 при помощи Page(42).acquire(1) — успешно.
V>Зачем?
V>Ты ведь уже shared-владешь страницей, т.е. можешь читать данные этой страницы "просто так".
Эмм, а откуда транзакция "знает", что уже shared-владеет страницей? В коде вашего семафора нет никакой информации о том, кто чем владеет. Если вы подразумеваете, что транзакция (или менеджер блокировок) ведёт таблицы владения, то стоимость операций над этими таблицами тоже нужно учитывать. То есть опять — даже в простейшем случае одноуровневой иерархии ресурсов у нас никак не хватает "единственной interlocked операции".
V>Это тогда надо заходить на страницу под другой дисциплиной блокировки, например, "я могу блокировать с разными дисциплинами произвольные твои строки конкуретно", и тогда страница не пустит shread-RO "клиентов" к себе.
V>И тогда acquire ты будешь запрашивать у конкретной строки, а не страницы, бо на страницу под нужной тебе прикладной дисциплиной ты уже вошёл.
Совершенно верно тогда надо помимо захвата "разделяемой блокировки" на уровне страницы дополнительно захватывать нужный вид блокировки на уровне строки. Я взял "разделямую" в кавычки потому, что на самом деле intent lock не во всех отношениях ведёт себя как просто shared lock, хотя в упрощённой модели их можно использовать взаимозаменяемо.
Как мы уже выяснили, нельзя просто захватить row lock — надо сначала захватить intent-блокировки на базу, таблицу, страницу.
V>Или, при реализации COW, если страница захвачена читателями и для тех уровни изоляции позволяют, твоя транзакция может поступить иначе — сделать копию страницы, обновить строки в этой копии, а потом обновит цепочку страниц (про COW тебе тоже говорилось сразу).
И я про COW сразу отвечал, что он убивает производительность даже при чтениях. Потому, что механика определения того, какая версия страницы является актуальной для данной транзакции, небесплатна.
V>А текущая страница для shared RO-заявок переходит в режим фантома и будет жить до тех пор, пока ею shared-владеют, потом тихо умрёт.
Вот вы же опять манипулируете какой-то магией. Вот это вот всё — "режим фантома" и прочее — это же всё не божьим промыслом происходит. Кто-то должен отслеживать состояния транзакций, которые видят фантомные страницы, и своевременно эти страницы помечать как свободные. Вы уверены, что понимаете, как это повлияет на производительность обработки запросов?
V>Здесь опять путаешь уровни иерархий.
V>Говорил же тебе — тупо пронумеруй, меньше вероятность ошибиться. ))
Нет, просто вы плохо объясняете свои мысли. Именно поэтому я хотел от вас кода. Потребовалось всего-то 20 постов туда-сюда, чтобы мы сошлись на недостаточности одной interlocked операции для сканирования таблицы.
V>Но тогда привет турникетам и база быстро встанет колом, при таком-то кол-ве уровней иерархии.
Нет, если мы чётко понимаем, чем отличаются блокировки строк от блокировок страниц, то никаких вставаний колом не происходит. Просто для модификации одной строки нужно захватить две блокировки — строки и страницы (в предположении, что блокировка таблицы уже захвачена).
V>Надеюсь, на следующей итерации у тебя будет получше с пониманием целевого — а зачем вообще требуется нетривиальная возня с режимами СМО.
V>Про уровни параллелизма когда речь, то ресурсом является вычислительный блок, (процессор, ядро, поток в ядре и т.д., смотря как железка устроена).
Ага. Именно поэтому в примере выше было взято число 8. А не, скажем, 2000 — что больше похоже на количество записей на странице.
V>Например, транзакция, выполняющая COW, делает обновления многошагово, т.е. через последовательность заявок.
V>Первая заявка иницировала транзакцию, произвела первое чтение.
V>Если были конкуретные чтения или даже чтения-записи, то в рамках транзакции могут создаваться копии данных, как упомянул выше. Одна операция чтения — один уровень параллелизма (допустим для простоты). В системах с отказами и последействиями, например, нужной страницы нет в памяти, — будет отказ и новая постановка в очередь с новым состоянием заявки (см эту категоризацию СМО, сама эта категоризация даёт много подсказок).
Я по-прежнему не вижу, чем эта категоризация СМО может помочь при проектировании СУБД.
V>Освободили "ресурс", т.е. процессор-ядро-поток, исполняем следующую заявку. Вполне может быть, что следующей будет заявка из той же самой транзакции, она ведь при инициализации могла накидать сразу пачку заявок, т.е. вероятность последовательного расположения заявок от одной транзакции будет высокой.
Ну вот с этой стороны я пока не очень понимаю, как это всё строить с точки зрения реализации. Что такое "заявка"? Task<T>?
V>В общем, тебе придётся найти подтверждение этой (уже не первой) инсинуации или прилюдно извиниться.
Какой?
V>Ага, т.е. я прав — ты теперь пытаешься замылить саму суть обсуждения, как обычно ища виноватых в своем непонимании на стороне. ))
Нет, я по-прежнему пытаюсь вернуть вас к сути. Обратите внимание — уже 20 постов я задаю одни и те же вопросы.
V>"На коленке" я жду от тебя куда как более примитивную часть этого кода — простое управление shared-exclusive доступом ко всего одному ресурсу, т.е. очень простой СМО из двух состояний.
Давайте лучше вы напишете эту часть кода. Которая хотя бы будет реентерабельной
Когда сделаете, можно будет перейти к вопросам со звёздочкой — например, что делать с deadlock? Но об этом поговорим потом
V>И жду понимание того, как с помощью аналогичных механизмов, раскиданных по разным уровням иерархий, реализовать комбинаторику возникающих сценариев при организации различных уровней изоляции.
вся "комбинаторика" при реализации различных уровней изоляции хорошо известна. Для неё не нужна никакая теория СМО. И даже гранулярность блокировок уровню изоляции ортогональна.
Уровни изоляции описываются тем, в какой момент захватываются и отпускаются shared lock, независимо от их гранулярности. Ну, кроме serializable, где важными становятся table и range блокировки.
А вы опять умничаете в области, с которой плохо знакомы.
V>И тогда ты будешь готов согласиться с самыми первыми моими заявлениями, насчёт простой нумерации уровней иерархий.
V>Ну конечно же да.
V>Потому что в какой-нить транзакции я могу по некоему условию менять даже схему таблиц, т.е. заранее неизвестно.
V>И тогда ACID на всех уровнях, кроме сериализованного не будет соблюдаться от момента начала такого участка транзакции, т.е. базе придётся войти в сериализованный режим.
)
Господи, чушь-то какая. Изменения схемы таблиц совершенно не требуют никакого глобального мутекса. Вы, наверное, не видели взрослых СУБД.
Изменения схемы прекрасно живут на блокировках схемы. Я могу стартовать транзакцию, выполнить в ней create table и пойти покурить, не делая commit — и никакие другие транзакции этого не заметят.
Я могу сделать alter table drop column — и он дождётся, пока последняя транзакция, использующая эту таблицу завершится, и дропнет колонку.
И если параллельно со мной никакие транзакции не работают конкретно с этой таблицей, то они прекрасно продолжат работать, как ни в чём не бывало.
Я не знаю, что вы называаете "сериализованным режимом", но если речь про захват глобального мьютекса — то нет, вы не угадали, для изменений схемы он не нужен, и настоящие СУБД работают не так.
S>>Почитайте любой учебник по СУБД.
V>Опять мимо, тут нужен учебник не для пользователя СУБД, а для разработчика СУБД (угу, не разработчика прикладной базы данных, а самой СУБД).
Из тех четырёх, которые я привёл, два — для разработчиков СУБД.
V>СМО — неотъемлимая часть такого учебника.
Давайте так: дайте ссылку на главу по СМО в любом учебнике по разработке СУБД на ваш вкус.
S>>Глобальная блокировка является достаточным, но вовсе не необходимым условием для serializability.
V>Опять та же ошибка — зависит от уровня иерархии оперируемой сущности.
Что именно зависит? Значение термина "глобальная блокировка"? Очень странно. Я полагал, что глобальность — она на то и глобальность, что одна на всех.
V>СУБД тут ни разу не уникальны, т.е. до смешного — сама попытка отослать к учебнику по некоей СУБД демонстрирует не просто непонимание предметной области, а отсутствие простой "записи" в эрудиции о наличии самой предметной области.
Не по "некоей СУБД", к учебнику по разработке СУБД.
V>В английском различают термины lock и block, в русском языке возникает путаница.
V>Поэтому, стоит использовать или английскую терминологию, или длинную русскую, навроде "блокировка для совместного доступа по чтению".
V>В русскоязычном IT "просто блокировка" означает blocking lock на английском.
Именно поэтому я ни разу не использовал термин "просто блокировка".
И в большинстве мест я использовал английскую терминологию. Очень странно с вашей стороны делать вид, что вы двадцать постов подряд не понимали, что мы обсуждаем СУБД.
V>Простой пример:
V>вот у тебя запрос с агрегацией только по чтению.
V>Какие могут быть уровни изоляции:
V>- никакой, т.е. код исполнения запроса читает и те данные, что уже есть и те, что были изменены; есть вероятность, что результатом агрегата станет такое значение, которое не могло быть ни в одном из "конечных" состояний базы, т.е. между транзакциями.
V>- ACID-изоляция; конкурентные транзакции не могут изменить твои данные; это может быть достигнуто исключительным доступом к таблице (блокировкой), либо же, транзакция, выполняющая агрегирующее чтение, будет работать с копиями данных, на момент начала чтения; вот тебе пример того, что блокировки и уровни изоляции — совсем не одно и то же;
V>- откаты/обработка конфликтов; например, самостоятельный перезапуск базой запроса, если уже просмотренные данные были изменены конкурирующими транзакциями;
V>Первый случай еще обычно разделяют на два-три, по степени допущения несогласованности, но пример с агрегатом был взят именно затем, чтобы отличаться от "простой выборки".
V>Именно в этом месте "учебники по СУБД" нехило улыбают, бо я привел пример одной из самых распространнёных ошибок подсчёта агрегата при неверно выбранном уровне изоляции.
Ок, хорошо, что вы привели конкретный пример. Его хотя бы можно обсуждать. Вот для банального агрегатного запроса типа select sum(salary) from employees; какой нужен уровень изоляции? Внезапно — repeatable read.
Если мы пошли по пути блокировок, внезапно оказывается, что совершенно необязательно захватывать исключительный доступ к таблице.
Достаточно удерживать разделяемую блокировку до конца транзакции.
Более того — даже делать её уровня таблицы совершенно необязательно. Можно захватывать shared page locks по мере сканирования данных — и всё ещё никаких проблем с сериализуемостью не будет.
Понятно, почему так происходит, или надо подробно разжевать?
S>>Например, рассмотрите классическую задачу атомарного перевода денег между счетами, и убедитесь, что repeatable read достаточно для обеспечения согласованности данных в этой задаче.
V>Но недостаточно для моей задачи.
V>Если в рамках конкурирующей транзакции были изменены две строки данных, одну из них уже прочитали и подсчитали в агрегате, другую прочитали уже после фиксации конкурентной транзакции — агрегат выдаст фуфел на выходе.
Именно поэтому для данного запроса нужен repeatable read. А вот serializable — избыточен.
V>Ну да, вместо конструктива, вместо попыток понять конкретно обсуждаемый сабж, занимаешься пусканием "дымовой завесы".
Ну, ок. Не хотите читать учебники — не читайте. Но не обижайтесь, когда я буду вас тыкать носом в непонимание вещей, которые там изложены простым языком.
V>Какого именно утверждения?
Про то, что ACID достижим только при помощи одного глобального мьюьекса.
V>Системы резервирования работают, внезапно, через различные допущения с целью того самого ускорения обслуживания.
V>Например, работавшая (не знаю как сейчас) еще с 90-х система резервирования ЖД-билетов работала через пулы, раскиданные по звездообразной схеме.
V>При исчерпании пула у локального и вышестоящего узла, резервирование порой выполняется ооочень долго.
V>Потому что чудес не бывает.
V>Ты бы эта хотя бы свои собственные примеры проверял, прежде чем приводить их якобы в пример якобы своей правоты.
Печаль, печаль. Подумайте ещё раз: если бы ACID был недостижим без одного глобального mutex, то резервирование бы было медленным всегда, а не при "исчерпании пула".
V>А с современными скоростями интернета и быстродействием серверов и этого уже не требуется, ведь "один мьютекс" нужен не на всю базу ЖД-трафика, а на конкретный рейс, т.е., подозреваю, с пулами сегодня никто не возится, достаточно обеспечить fault tolerance, задача поиска свободных мест из десятков-сотен значений сегодня занимает микросекунды.
Хм, опять началась какая-то каша в голове. Что такое "глобальный мьютекс на конкретный рейс"? И чем он отличается от "локального мьютекса"?
Вы совершенно напрасно изобретаете какую-то самодеятельную терминологию.
V>И да, при попытке фактического резервирования repetable read уже не катит, упс?
С чего вы это взяли? Смотря что входит в "фактическое резервирование" — может хватить даже read committed. Попробуйте продемонстрировать контрпример
V>Зато прекрасно катит автоматический откат и повторное резервирование, скажем, на рейсы с пересадкой, когда при последовательном резервировании одно из них вышло неудачным.
V>(третий упомянутый алгоритм обеспечения согласованности)
Да, расскажите мне про optimistic locking. Но лучше — в другой раз. Там всё сложнее, чем кажется на первый взгляд, а вы ещё с pessimistic locking не разобрались.