Информация об изменениях

Сообщение Re[85]: MS забило на дотнет. Питону - да, сишарпу - нет? от 02.10.2021 9:46

Изменено 02.10.2021 10:27 Sinclair

Re[85]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, netch80, Вы писали:
N>А почему собственно?
N>Представим себе, что мы посылаем асинхронные запросы, для каждого из которых требуем нотификации успешного завершения только когда байтики, грубо говоря, сформировали соответствующие сдвиги атомов и электронов. А потом, получив подтверждение записи WAL, пишем изменённые страницы на диск.
N>Чем плохо?
Так и делается.


N>Хм. Это какая СУБД так делает?

Более-менее все.
N>Я скорее предположил бы что-то типа такого (уточнить по уровню изоляции):
N>1. Последовательно, или ускоренно по индексу, читаем все записи, ставя лок на запись на каждую подпадающую под условие. Тут можно разгонять упреждающее чтение для скорости.
Что такое "читаем"? Вы имеете в виду "делаем копию в специальную временную область памяти, видимую только текущей транзакции"?
Как вы предполагаете разгонять упреждающее чтение для MMF? Читать в отдельном потоке по одному инту из тех страниц, которые нам скоро потребуются?
N>2. Записываем в WAL изменённые строки для каждой подпавшей под условие; это можно сгруппировать. Ждём подтверждения записи.
Не очень понятно, что такое "сгруппировать". Вы имеете в виду, что после фазы 1 мы отдельно запустим цикл по тем данным, которые мы скопировали во временный буфер, и создадим ещё один буфер, с новыми версиями этих записей?
А потом запишем эти
N>3. По команде commit — запускаем (лучше одновременно) запись изменённых версий строк во все соответствующие блоки. СУБД должна сгруппировать эти действия по своим блокам, а диск — обеспечить соответствующее упорядочение и группировку по своим блокам. Ждём завершения всех записей. Одновременность подачи позволяет диску максимально оптимизировать операции.
Не очень понятно, как вы "одновременно" сделаете запись изменённых версий в MMF файл. Ну, то есть это, конечно, можно делать в несколько потоков, но и только.
N>4. В WAL фиксируем завершение транзакции (ждём подтверждения записи и тогда отдаём ok на commit).
N>Что тут не так и что может давать какие-то ограничения?
В основном тут не так с эффективностью использования памяти. Получается, что мы не используем преимущества MMF напрямую, а строим поверх него свой набор буферов.
В принципе, так делать можно, но у нас тогда кэш СУБД начинает конкурировать за память с кэшем ФС. Поскольку все записи у нас делаются "вручную", можно просто убрать MMF совсем и в момент записи изменений просто сбрасывать страницы в обычный файл.

S>>- на SSD мы получим многократные перезаписи одной и той же страницы, очень быстро изнашивая диск.

N>Какой именно страницы и в какой это СУБД такое происходит?
N>Имеется в виду секция WAL? Почему СУБД не может сгруппировать пачку записей для WAL в пределах одной транзакции?
Отвечу с конца: группировка данных перед записью в файл называется буферизацией. Да, именно секция WAL и имеется в виду. Нет, никакая СУБД так не делает — по причинам, которые я изложил.
Записи WAL пишутся в лог по мере поступления, перемежаясь с записями от параллельно выполняемых транзакций. Буфер на всю транзакцию может оказаться неприемлемо большим при большом объёме вносимых изменений.

N>flush() в таком виде нужен только для оптимизации синхронных операций записи. Асинхронным он даже вреден (точнее, вредно кэширование, которым он управляет — поэтому для асинхронных операций он бессмысленен).

N>Но пусть вы даже работаете синхронными операциями. При необходимости обеспечить строгую последовательность действий: запись операции в WAL — модификация таблиц — завершение транзакции записью в WAL, что запись состоялась, значит, что должен быть логический барьер
N>1) все записи действий в WAL идут до всех записей в таблицу;
N>2) все записи в таблицу идут до финализации в WAL,
Второй барьер не нужен. Важен только первый. Запись в лог делать асинхронной смысла нет: всё равно мы должны в конце транзакции обеспечить синхронный flush, т.к. пока мы не получили от диска подтверждения записи лога, коммит подтверждать нельзя.

N>и если вы не хотите терять производительность на слишком частом дёргании flush() — придётся синхронизировать его между соседними транзакциями. Вы уверены, что вам это нужно?

В общепринятой схеме flush делается 1 раз на транзакцию, и синхронизации обращения к логу не слишком мешают — гранулярность записей обычно достаточно невелика (есть исключения при операциях заливки BLOB).

S>>2. Открыть файл c флагами FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH. Тогда можно делать просто FileStream.Flush().

S>>Подчеркну: в обоих случаях мы делаем Flush лога на на каждое изменение, а только на каждый commit transaction. То есть транзакция, которая трогает 1000 записей, будет выполняться ~10ms (если выбранный размер буфера достаточен для того, чтобы избежать промежуточных сбросов в процессе исполнения).


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

Непонятно, что такое "инициировать независимо пачку операций на одну транзакцию" и зачем это может быть надо. В логе записи вполне себе зависимы, и идут в одном направлении. Синхронная запись в таких случаях — то, что доктор прописал.
Re[85]: MS забило на дотнет. Питону - да, сишарпу - нет?
Здравствуйте, netch80, Вы писали:
N>А почему собственно?
N>Представим себе, что мы посылаем асинхронные запросы, для каждого из которых требуем нотификации успешного завершения только когда байтики, грубо говоря, сформировали соответствующие сдвиги атомов и электронов. А потом, получив подтверждение записи WAL, пишем изменённые страницы на диск.
N>Чем плохо?
Так и делается.


N>Хм. Это какая СУБД так делает?

Более-менее все.
N>Я скорее предположил бы что-то типа такого (уточнить по уровню изоляции):
N>1. Последовательно, или ускоренно по индексу, читаем все записи, ставя лок на запись на каждую подпадающую под условие. Тут можно разгонять упреждающее чтение для скорости.
Что такое "читаем"? Вы имеете в виду "делаем копию в специальную временную область памяти, видимую только текущей транзакции"?
Как вы предполагаете разгонять упреждающее чтение для MMF? Читать в отдельном потоке по одному инту из тех страниц, которые нам скоро потребуются?
N>2. Записываем в WAL изменённые строки для каждой подпавшей под условие; это можно сгруппировать. Ждём подтверждения записи.
Не очень понятно, что такое "сгруппировать". Вы имеете в виду, что после фазы 1 мы отдельно запустим цикл по тем данным, которые мы скопировали во временный буфер, и создадим ещё один буфер, с новыми версиями этих записей?
А потом запишем эти записи в лог? И всё это — только ради того, чтобы не использовать встроенное в любой stream кэширование?
N>3. По команде commit — запускаем (лучше одновременно) запись изменённых версий строк во все соответствующие блоки. СУБД должна сгруппировать эти действия по своим блокам, а диск — обеспечить соответствующее упорядочение и группировку по своим блокам. Ждём завершения всех записей. Одновременность подачи позволяет диску максимально оптимизировать операции.
Не очень понятно, как вы "одновременно" сделаете запись изменённых версий в MMF файл. Ну, то есть это, конечно, можно делать в несколько потоков, но и только.
N>4. В WAL фиксируем завершение транзакции (ждём подтверждения записи и тогда отдаём ok на commit).
N>Что тут не так и что может давать какие-то ограничения?
В основном тут не так с эффективностью использования памяти. Получается, что мы не используем преимущества MMF напрямую, а строим поверх него свой набор буферов.
В принципе, так делать можно, но у нас тогда кэш СУБД начинает конкурировать за память с кэшем ФС. Поскольку все записи у нас делаются "вручную", можно просто убрать MMF совсем и в момент записи изменений просто сбрасывать страницы в обычный файл.

S>>- на SSD мы получим многократные перезаписи одной и той же страницы, очень быстро изнашивая диск.

N>Какой именно страницы и в какой это СУБД такое происходит?
N>Имеется в виду секция WAL? Почему СУБД не может сгруппировать пачку записей для WAL в пределах одной транзакции?
Отвечу с конца: группировка данных перед записью в файл называется буферизацией. Да, именно секция WAL и имеется в виду. Нет, никакая СУБД так не делает — по причинам, которые я изложил.
Записи WAL пишутся в лог по мере поступления, перемежаясь с записями от параллельно выполняемых транзакций. Буфер на всю транзакцию может оказаться неприемлемо большим при большом объёме вносимых изменений.

N>flush() в таком виде нужен только для оптимизации синхронных операций записи. Асинхронным он даже вреден (точнее, вредно кэширование, которым он управляет — поэтому для асинхронных операций он бессмысленен).

N>Но пусть вы даже работаете синхронными операциями. При необходимости обеспечить строгую последовательность действий: запись операции в WAL — модификация таблиц — завершение транзакции записью в WAL, что запись состоялась, значит, что должен быть логический барьер
N>1) все записи действий в WAL идут до всех записей в таблицу;
N>2) все записи в таблицу идут до финализации в WAL,
Второй барьер не нужен. Важен только первый. Запись в лог делать асинхронной смысла нет: всё равно мы должны в конце транзакции обеспечить синхронный flush, т.к. пока мы не получили от диска подтверждения записи лога, коммит подтверждать нельзя.

N>и если вы не хотите терять производительность на слишком частом дёргании flush() — придётся синхронизировать его между соседними транзакциями. Вы уверены, что вам это нужно?

В общепринятой схеме flush делается 1 раз на транзакцию, и синхронизации обращения к логу не слишком мешают — гранулярность записей обычно достаточно невелика (есть исключения при операциях заливки BLOB).

S>>2. Открыть файл c флагами FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH. Тогда можно делать просто FileStream.Flush().

S>>Подчеркну: в обоих случаях мы делаем Flush лога на на каждое изменение, а только на каждый commit transaction. То есть транзакция, которая трогает 1000 записей, будет выполняться ~10ms (если выбранный размер буфера достаточен для того, чтобы избежать промежуточных сбросов в процессе исполнения).


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

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