Это слегка отредактированный перевод статьи с англоязычной википедии, разбавленный картинками с сайта, на который ссылается википедийская статья. Лентяи.
Хотел спросить, для тех, кто не знает C# и Linq, что такое Include() ? Выглядит как join.
И ещё: я правильно понял, что причина тормозов в том, что драйвер оракловского протокола работает синхронно: заказывает fetchSize строк, и пока все не отдаст приложению, новые не запросит? И канал у вас был с большой latency, в которую, при таком протоколе, оно и упиралось, при том что пропускная способность позволяла, и вы это обошли тем, что gRPC хорошо умеет в стриминг? А fetchSize нельзя подкрутить?
Если второй поток вызовет getTotalUsers() между put() и incrementAndGet(), то он получит то же значение, как если бы вызвал перед put(), и это норм. Авторы пытались показать, что методами, которые меняют состояние объекта, нельзя привести объект в состояние, когда userCount и users разъедутся.
Я не знаю Kotlin, хотел спросить: ваша реализация группировки работает так: вы получаете списки строк, потом превращаете их в строки списков, и на последнем шаге для ключа группировки заменяете список одинаковых значений на единственное значение. Как с точки зрения проверки типов работает, что вы в мапу списков помещаете не список, а единственное значение? Я вижу, что groupedRow: Map<String, List<Any?>> , но не понимаю, что такое делает + , что тип стал Map<String, Any>
Мда. Во-первых, надо поработать над языком, написано ужасно. Во-вторых, надо поработать над оформлением, набор красно-чёрных картинок занимает 70% вертикального пространства статьи, нафиг не надо. В третьих, надо поработать над структурой.
Вы, с одной стороны, советуете не думать, что очистка записей в страницах выполняется в рамках транзакции, и я это понимаю, эта транзакция не имеет никакого отношения к версии строки, ей просто место нужно. Но вы сами довольно много подробностей про эту транзакцию написали: её номер сохраняется в очищенной строке, её статус впоследствии проверяется. Зачем? Строку почистили, потому что страница уже была загружена в память и было оптимально разместить новую запись на этой странице. Какое дело, кто почистил строку?
Про очистку строки и бит LP_DEAD - был неправ, мне причудилось, что написано "удалена или вышла за горизонт". Спасибо, что прояснили, что имеется в виду "очищенная" строка.
Мои непонятки относились к первым двум абзацам. Главную мысль статьи я понял: отдельные функции пытаются делать свою локальную пометку мусора в тех пространствах, в которые заходят. Зашли на страницу - пометили записи как очищенные. Прошли по указателю в индексе, но записи помечены как очищенные - пометим индексный указатель как очищенный, чтобы в следующий раз страницу с записями не загружать с диска.
Я далеко не с первого раза понял начальные абзацы, мне кажется, что не хватает пояснений.
В первом абзаце есть вот такое:
Внутристраничная очистка (HOT cleanup) – это оптимизация, благодаря которой старые версии строк могут эффективно удаляться из блоков таблиц. Освобождённое место используется под размещение новой версии строки. Освобождается только место, занимаемое версиями строк, вышедшими за горизонт базы данных (xmin horizon)
Я так понимаю, что это чисто техническая активность по освобождению места в странице, не выполняемая в рамках каких-то транзакций. Причём удаляемые старые версии могли стать таковыми любым способом, например UPDATE создал новую версию строки.
А потом во втором абзаце уже идёт вот такое:
Алгоритм быстрой очистки табличной страницы реализован в файле pruneheap.c (heap page pruning and HOT-chain management code) исходного кода PostgreSQL. При удалении строки в блоке (странице) таблицы удаляющая транзакция проставляет свой номер в заголовок строки в поле xmax и пытается найти место под новую версию строки в том же самом блоке, при этом признак фиксации не проставляется.
Здесь уже, как я понял, речь совсем про другое: удаление строки из таблицы через DELETE в рамках пользовательской транзакции. Как это связано с "быстрой очисткой страницы" - непонятно и не объяснено.
Когда вы говорите, что бит LP_DEAD выставляется, когда index scan обнаружил, что "строка удалена", вы что имеете в виду? Какая-то транзакция могла пометить версию строки как "удалённая", но для параллельно выполняющихся repeatable read транзакций нужно возвращать эту строку, поэтому что-то сомнительно. А если имеется в виду запись, помеченная как "очищенная", тогда понятно.
Неудачная структура статьи: главная часть - это очень базовое объяснение транзакций, которое знакомо мало-мальски работающему с базами данных человеку. Потом перепрыгнули на JOOQ непонятно зачем, про его связь с транзакциями не сказано ничего. И дальше про сохранялку из Kafka. Меня вышибает термин "JDBC коннектор Kafka", я не могу воспринимать это иначе чем нечто, что реализует JDBC-интерфейс для данных, хранящихся в логах Kafka. Оказалось, имелся в виду Kafka Connect, который сливает в базу данные из топиков. Мне кажется, вы в объяснении где-то напутали, и хотели сказать, что read repeatable вам не подошёл, а подошёл read commited. Ну логично, там же только сохранение данных. В конце вы сами пишете, что read commited - уровень по-умолчанию, значит даже у человека, не понимающего в транзакциях, всё бы сработало.
Люди добрые, помогите, пожалуйста? Читал году примерно в 2000-2001 в электронном виде фэнтези-произведение российского автора, забыл и автора и название. Сюжет таков: какого-то принца колдуны отправляют искать "дракона", забрав невесту в заложницы. После множества мощных приключений он попадает в пустыню, "дракон" оказывается летательным аппаратом, герой возвращается и мстит колдунам. По градации автора этой статьи это очень глупое чтение, не попаданство, но абсолютно безыдейное sword and sorcery, без worldbuilding-а, без характеров и без истории. Очень хочется перечитать.
Какие-то советы для начинающих индусов из года примерно 2010. Блог компании OTUS, наш девиз "похер на качество контента, мы берём количеством".
Проблема со склеиванием строк не в том, что память потребляется, память-то выделить очень быстро. Проблема в том, что из старого объекта копируются символы в новый объект, много раз. StringBuilder делает то же самое внутри, когда не хватает буфера. Правильная оптимизация - это прикинуть окончательный размер строки, и передать его в StringBuilder.
Не нужно size() выносить в переменную, компилятор это оптимизирует сам.
Там про смысл написано вполне чётко: кластеризация располагает строки таблицы не в порядке добавления, а в порядке какого-либо индекса, обычно первичного ключа. Очень часто значения первичного ключа формируются самой базой из возрастающей последовательности, поэтому индекс по первичному ключу соответствует порядку добавления. Но Posgres при обновлении строк создаёт их новые версии, поэтому со временем порядок строк на диске будет сильно отличаться от порядка по первичному ключу.
Автор-питонист хотел поджечь Java-жопы, и ему это удалось. При этом пытался немножко выглядеть объективными и профессиональным, но наличие мемасов в статье выдавало Штирлица. А потом в комментах начали поджигать жопу автору. Нравится!
Теперь совсем про другое: про реализацию quicksort на Java, которую автор привёл. Там в partition() два индекса, и для некоторого количества элементов в начале оба индекса будут совпадать, пока эти начальные элементы меньше pivot-а. Представленный алгоритм будет менять элемент самого с собой. В худшем случае, если исходный массив уже отсортирован, будет много лишней работы. Можно добавить if, но он будет напрасно исполняться на том этапе, когда i уже гарантированно меньше j, тоже неприятно.
private static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low ;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
i++;
}
}
Где гарантии, Билли? Нам нужны гарантии! (Чёрный Пёс)
Это слегка отредактированный перевод статьи с англоязычной википедии, разбавленный картинками с сайта, на который ссылается википедийская статья. Лентяи.
Офигительнейше, очень приятный язык.
Хотел спросить, для тех, кто не знает C# и Linq, что такое Include() ? Выглядит как join.
И ещё: я правильно понял, что причина тормозов в том, что драйвер оракловского протокола работает синхронно: заказывает fetchSize строк, и пока все не отдаст приложению, новые не запросит? И канал у вас был с большой latency, в которую, при таком протоколе, оно и упиралось, при том что пропускная способность позволяла, и вы это обошли тем, что gRPC хорошо умеет в стриминг? А fetchSize нельзя подкрутить?
Если второй поток вызовет getTotalUsers() между put() и incrementAndGet(), то он получит то же значение, как если бы вызвал перед put(), и это норм. Авторы пытались показать, что методами, которые меняют состояние объекта, нельзя привести объект в состояние, когда userCount и users разъедутся.
При всём уважении к автору, я ничего не понял.
Очень хорошая статья.
Я не знаю Kotlin, хотел спросить: ваша реализация группировки работает так: вы получаете списки строк, потом превращаете их в строки списков, и на последнем шаге для ключа группировки заменяете список одинаковых значений на единственное значение. Как с точки зрения проверки типов работает, что вы в мапу списков помещаете не список, а единственное значение? Я вижу, что
groupedRow: Map<
String
, List<
Any
?>>
, но не понимаю, что такое делает + , что тип стал Map<String, Any>Мда. Во-первых, надо поработать над языком, написано ужасно. Во-вторых, надо поработать над оформлением, набор красно-чёрных картинок занимает 70% вертикального пространства статьи, нафиг не надо. В третьих, надо поработать над структурой.
Я расскажу в комментарии, что это не статья, а LLM-ный мусор для заманухи в телеграм-канал.
Вы, с одной стороны, советуете не думать, что очистка записей в страницах выполняется в рамках транзакции, и я это понимаю, эта транзакция не имеет никакого отношения к версии строки, ей просто место нужно. Но вы сами довольно много подробностей про эту транзакцию написали: её номер сохраняется в очищенной строке, её статус впоследствии проверяется. Зачем? Строку почистили, потому что страница уже была загружена в память и было оптимально разместить новую запись на этой странице. Какое дело, кто почистил строку?
Про очистку строки и бит LP_DEAD - был неправ, мне причудилось, что написано "удалена или вышла за горизонт". Спасибо, что прояснили, что имеется в виду "очищенная" строка.
Мои непонятки относились к первым двум абзацам. Главную мысль статьи я понял: отдельные функции пытаются делать свою локальную пометку мусора в тех пространствах, в которые заходят. Зашли на страницу - пометили записи как очищенные. Прошли по указателю в индексе, но записи помечены как очищенные - пометим индексный указатель как очищенный, чтобы в следующий раз страницу с записями не загружать с диска.
Я далеко не с первого раза понял начальные абзацы, мне кажется, что не хватает пояснений.
В первом абзаце есть вот такое:
Я так понимаю, что это чисто техническая активность по освобождению места в странице, не выполняемая в рамках каких-то транзакций. Причём удаляемые старые версии могли стать таковыми любым способом, например UPDATE создал новую версию строки.
А потом во втором абзаце уже идёт вот такое:
Здесь уже, как я понял, речь совсем про другое: удаление строки из таблицы через DELETE в рамках пользовательской транзакции. Как это связано с "быстрой очисткой страницы" - непонятно и не объяснено.
Когда вы говорите, что бит LP_DEAD выставляется, когда index scan обнаружил, что "строка удалена", вы что имеете в виду? Какая-то транзакция могла пометить версию строки как "удалённая", но для параллельно выполняющихся repeatable read транзакций нужно возвращать эту строку, поэтому что-то сомнительно. А если имеется в виду запись, помеченная как "очищенная", тогда понятно.
Мусорная статья, написанная нейросетью.
А ну хотя чего я удивляюсь, часть про транзакции и заключение писала LLM, а от автора тут только часть про JOOQ и сохранялку из Kafka. Фу так писать.
Занятный троллинг
Неудачная структура статьи: главная часть - это очень базовое объяснение транзакций, которое знакомо мало-мальски работающему с базами данных человеку. Потом перепрыгнули на JOOQ непонятно зачем, про его связь с транзакциями не сказано ничего. И дальше про сохранялку из Kafka. Меня вышибает термин "JDBC коннектор Kafka", я не могу воспринимать это иначе чем нечто, что реализует JDBC-интерфейс для данных, хранящихся в логах Kafka. Оказалось, имелся в виду Kafka Connect, который сливает в базу данные из топиков. Мне кажется, вы в объяснении где-то напутали, и хотели сказать, что read repeatable вам не подошёл, а подошёл read commited. Ну логично, там же только сохранение данных. В конце вы сами пишете, что read commited - уровень по-умолчанию, значит даже у человека, не понимающего в транзакциях, всё бы сработало.
Увидел в названии "философия", заинтересовался, прочитал. Мда, и правда "философия".
Люди добрые, помогите, пожалуйста? Читал году примерно в 2000-2001 в электронном виде фэнтези-произведение российского автора, забыл и автора и название. Сюжет таков: какого-то принца колдуны отправляют искать "дракона", забрав невесту в заложницы. После множества мощных приключений он попадает в пустыню, "дракон" оказывается летательным аппаратом, герой возвращается и мстит колдунам. По градации автора этой статьи это очень глупое чтение, не попаданство, но абсолютно безыдейное sword and sorcery, без worldbuilding-а, без характеров и без истории. Очень хочется перечитать.
Какие-то советы для начинающих индусов из года примерно 2010. Блог компании OTUS, наш девиз "похер на качество контента, мы берём количеством".
Проблема со склеиванием строк не в том, что память потребляется, память-то выделить очень быстро. Проблема в том, что из старого объекта копируются символы в новый объект, много раз. StringBuilder делает то же самое внутри, когда не хватает буфера. Правильная оптимизация - это прикинуть окончательный размер строки, и передать его в StringBuilder.
Не нужно size() выносить в переменную, компилятор это оптимизирует сам.
Там про смысл написано вполне чётко: кластеризация располагает строки таблицы не в порядке добавления, а в порядке какого-либо индекса, обычно первичного ключа. Очень часто значения первичного ключа формируются самой базой из возрастающей последовательности, поэтому индекс по первичному ключу соответствует порядку добавления. Но Posgres при обновлении строк создаёт их новые версии, поэтому со временем порядок строк на диске будет сильно отличаться от порядка по первичному ключу.
Автор-питонист хотел поджечь Java-жопы, и ему это удалось. При этом пытался немножко выглядеть объективными и профессиональным, но наличие мемасов в статье выдавало Штирлица. А потом в комментах начали поджигать жопу автору. Нравится!
Теперь совсем про другое: про реализацию quicksort на Java, которую автор привёл. Там в partition() два индекса, и для некоторого количества элементов в начале оба индекса будут совпадать, пока эти начальные элементы меньше pivot-а. Представленный алгоритм будет менять элемент самого с собой. В худшем случае, если исходный массив уже отсортирован, будет много лишней работы. Можно добавить if, но он будет напрасно исполняться на том этапе, когда i уже гарантированно меньше j, тоже неприятно.
Прямо даже не знаю, хоть два цикла делай.
Ужасно. Один из абзацев присутствует в двух местах. Ничего толкового про кодировки не написано.