2000 потоков / 25000 RPS = 80 мс в среднем. 32(16) / 25000 = 1.28 (0.64) мс CPU time на запрос (при 100% загрузке). 1.28 / 80 ≈ 1-2% времени на CPU, остальное ожидание. 1 мс на вычисления выглядит, конечно, невероятно круто для спринга, но в целом явный io-bound, для которого неблокирующая асинхронщина самое то.
32 ядра физически не могут выполнять более 32 потоков. 100-150 "активных" это явный артефакт сбора статистики.
500 потоков в неблокирующем режиме вам не нужно, 32 самый максимум. Скорее всего, запросы к бэкэндам у вас были блокирующими. Асинхронщина такого не любит, ей нужен non-blocking io. Иначе преимуществ вы не увидите. Похоже, ваш эксперимент в очередной раз это подтвердил.
В результате потоков действительно стало меньше - 500 вместо 2000, причем никто из них не ждал, а все чем-то занимались.
Разницы между 500 и 2000 потоками в системе мы не заметили - нагрузка на CPU не изменилась.
Вот тут не очень понятно, цифры как-то не бьются. Ядер-то у вас сколько на сервере? Какая нагрузка в RPS и сколько занимает обработка одного запроса?
"Все чем-то занимались" звучит как будто у вас 500 ядер были загружены. Если вдруг у вас и вправду такой жирный сервер, то для случая, когда "большую часть времени сервисы ждут ответа своих соседей", это очень странный выбор железа.
И если в модели "один запрос - один поток" вам хватало 2000 потоков на таком количестве ядер, то это выглядит скорее как CPU-bound нагрузка, что не бьётся с "ждут ответа своих соседей".
Если же ядер в сервере сильно меньше 500, то асинхронщину вы как-то неправильно приготовили. Количество потоков в идеале должно быть примерно равно количеству ядер.
Условно, если у вас 32 или 64 ядра, то 500 потоков не сильно лучше 2000, куча времени будет тратиться на ворочание тредами.
Но это не бьётся уже с утверждением "все чем-то занимались". Большая часть потоков в такой кофигурации может заниматься только ожиданием.
обычно не так важно, где произойдет сериализация: в базе или на стороне клиента
Если у вас сериализация на клиенте, то зачем вам транзакции в базе?
Если у приложения требование, скажем, 50 ms на p99, а транзакции выполняются за миллисекунду + сеть, то сериализация небольшого числа таких транзакций не будет проблемой.
А может и будет. В примере выше нередко бывает так, что сериализация это дедлок и отстрел транзакции-жертвы секунд через 30. На три порядка выше целевого по p99, упс.
Полностью с вами согласен, что serializable по умолчанию - безопаснее. Аппелировал к сильному утверждению, что нет доказательств, что производительность "serializable" ощутимо хуже. С оговоркой "в большинстве случаев" было бы нормально. Ещё лучше было бы сделать оговорку, что при невысокой concurrency. Т.к. при высокой конкуренции, даже без блокировок, множественные ретраи могут эту производительность убить. Другие уровни изоляции именно для решения данной проблемы и были оставлены.
Мы не нашли доказательств того, что производительность уровня изоляции «serializable» ощутимо хуже.
От архитектуры конкретной СУБД зависит. Первый контрпример, который приходит в голову, это классические реляционные блокировочники, например, SQL Server. В них serializable может приводить к фактически последовательному выполнению транзакций.
Range locks are placed in the range of key values that match the search conditions of each statement executed in a transaction. This blocks other transactions from updating or inserting any rows that would qualify for any of the statements executed by the current transaction. This means that if any of the statements in a transaction are executed a second time, they will read the same set of rows. The range locks are held until the transaction completes. This is the most restrictive of the isolation levels because it locks entire ranges of keys and holds the locks until the transaction completes. Because concurrency is lower, use this option only when necessary.
А ещё в них же широкие блокировки увеличивают вероятность дедлоков, что, опять же, на производительности сказывается не самым лучшим образом.
Это "понаехавшие", т.е. без местной прописки. По ощущениям, их где-то 2/3 и есть. Они тут живут, порой десятилетиями, платят налоги в городской бюджет, но не имеют права на всякую социалку, от медицины до оформления бумажек. Самые желанные клиенты для любого застройщика.
В 2011 просто случился разворот бизнеса в сторону маркетплейса. А до этого Озон многие годы был практически монополистом на онлайновом книжном рынке. С сильно развитой логистикой. К 2011 году помимо денег у них был ещё и огромный опыт на схожем рынке, каким мало кто из конкурентов мог похвастаться.
А что в этом плохого? Ну, кроме того, что некоторые библиотеки одной компании не понимают это и кидают ошибку?
отсутствие RETURN с различающимся значением в случае успеха и неуспеха
Ошибки можно и через механизм raiserror передавать. Бонусом будет возможность вернуть не только код ошибки, но и текстовое сообщение. И ещё по severity их отсортировать. Ну и возможность использовать try/catch для всего, а не городить огород с проверкой части ошибок по кодам возврата.
А ответ на заданный вопрос всё ещё можно найти в самом определении ACID. Транзакции друг от друга изолированы. И в данном конкретном случае мы никакого результата не увидим, пока транзакция с апдейтом не завершится. Наша вторая транзакция будет ожидать возможности навесить Shared-lock и будет ждать, пока оттуда первая не снимет Exclusive-lock.
Как-то странно видеть в статье, которая топит за try/catch и прочие новомодные фишки, полное игнорирование того, что изоляция транзакций может быть реализована разными способами. Snapshot isolation когда появился? В 2012? В 2008?
Команда TRUNCATE в MS SQL Server
Скажите, а сколько раз в production code вам доводилось использовать TRUNCATE? Имеет ли смысл спрашивать про нюансы ее работы, если они нужны раз в три года и их всегда можно подсмотреть в справочнике?
И насколько важны именно аспекты работы TRUNCATE в контексте isolation? Мне кажется куда более полезным на практике, что кандидат представляет, что TRUNCATE в сиквел сервере это DML операция, требующая как минимум прав на ALTER TABLE. И ее нельзя, например, применить к таблице, на которую ссылается внешний ключ. И всяких других граблях, из-за которых использование TRUNCATE может оказаться плохой идеей.
Что получим в результате? Но как же так, ведь мы всё откатили! Опять сломался ACID.
А вот раз тебе и бац - табличные переменные вполне себе имеют некоторые особенности.
ACID относится к сохраняемому состоянию данных СУБД, а значения локальных переменных, даже табличных, в него явно не входят. Никого ведь не удивляет, что при откате транзакции какой-нибудь set @answer = 32 не откатывается.
IMHO, на миддла достаточно будет спросить:
Базовый синтаксис SQL, попросить написать какой-нибудь несложный запрос с рекурсией и оконными функциями
Уровни изоляции, какие бывают и какие эффекты можно наблюдать на каждом уровне
Индексы и стратегии джойнов, их алгоритмическую сложность
Планы запросов, как их курить и что делать, если сиквел сервер выбирает хреновый план.
Для РФ не нужно ничего распознавать, у нас все чеки электронные. Достаточно отсканировать QR код на чеке, в нем есть все, что нужно для загрузки данных чека с сайта ФНС. Есть даже официальное приложение для этого, "Проверка чеков".
Для случаев, когда запись редка, особенно конкурентная, а чтений много и объем данных относительно небольшой, SQLite неплохо справляется. Например, для каких-нибудь развесистых конфигов.
Даже с SQL 99, в котором появились recursive CTE.
Мы, возможно, удивитесь, но гимн с тех пор не сильно изменился.
Про бабушку с половыми органами дедушки ещё в эпоху покоренья Крыма шутили!
ИИ тоже в офис пора загонять. А то сидит себе удаленно в своем цоде и непонятно чем занимается, а денежки за него капают. Менеджерам неспокойно.
Выделить ему этаж в офисе, как раньше, во времена больших ЭВМ. Будет стоять, шуметь, лампочками моргать. Сразу видно - работает.
2000 потоков / 25000 RPS = 80 мс в среднем. 32(16) / 25000 = 1.28 (0.64) мс CPU time на запрос (при 100% загрузке). 1.28 / 80 ≈ 1-2% времени на CPU, остальное ожидание. 1 мс на вычисления выглядит, конечно, невероятно круто для спринга, но в целом явный io-bound, для которого неблокирующая асинхронщина самое то.
32 ядра физически не могут выполнять более 32 потоков. 100-150 "активных" это явный артефакт сбора статистики.
500 потоков в неблокирующем режиме вам не нужно, 32 самый максимум. Скорее всего, запросы к бэкэндам у вас были блокирующими. Асинхронщина такого не любит, ей нужен non-blocking io. Иначе преимуществ вы не увидите. Похоже, ваш эксперимент в очередной раз это подтвердил.
Вот тут не очень понятно, цифры как-то не бьются. Ядер-то у вас сколько на сервере? Какая нагрузка в RPS и сколько занимает обработка одного запроса?
"Все чем-то занимались" звучит как будто у вас 500 ядер были загружены. Если вдруг у вас и вправду такой жирный сервер, то для случая, когда "большую часть времени сервисы ждут ответа своих соседей", это очень странный выбор железа.
И если в модели "один запрос - один поток" вам хватало 2000 потоков на таком количестве ядер, то это выглядит скорее как CPU-bound нагрузка, что не бьётся с "ждут ответа своих соседей".
Если же ядер в сервере сильно меньше 500, то асинхронщину вы как-то неправильно приготовили. Количество потоков в идеале должно быть примерно равно количеству ядер.
Условно, если у вас 32 или 64 ядра, то 500 потоков не сильно лучше 2000, куча времени будет тратиться на ворочание тредами.
Но это не бьётся уже с утверждением "все чем-то занимались". Большая часть потоков в такой кофигурации может заниматься только ожиданием.
Undefined behaviour в C++ настолько суров, что выстреливает даже в пропозалах в стандарт.
Если у вас сериализация на клиенте, то зачем вам транзакции в базе?
А может и будет. В примере выше нередко бывает так, что сериализация это дедлок и отстрел транзакции-жертвы секунд через 30. На три порядка выше целевого по p99, упс.
Полностью с вами согласен, что serializable по умолчанию - безопаснее. Аппелировал к сильному утверждению, что нет доказательств, что производительность "serializable" ощутимо хуже. С оговоркой "в большинстве случаев" было бы нормально. Ещё лучше было бы сделать оговорку, что при невысокой concurrency. Т.к. при высокой конкуренции, даже без блокировок, множественные ретраи могут эту производительность убить. Другие уровни изоляции именно для решения данной проблемы и были оставлены.
От архитектуры конкретной СУБД зависит. Первый контрпример, который приходит в голову, это классические реляционные блокировочники, например, SQL Server. В них serializable может приводить к фактически последовательному выполнению транзакций.
Цитата из документации:
А ещё в них же широкие блокировки увеличивают вероятность дедлоков, что, опять же, на производительности сказывается не самым лучшим образом.
Это "понаехавшие", т.е. без местной прописки. По ощущениям, их где-то 2/3 и есть. Они тут живут, порой десятилетиями, платят налоги в городской бюджет, но не имеют права на всякую социалку, от медицины до оформления бумажек. Самые желанные клиенты для любого застройщика.
Не, обычно это говорит о том, что стоимость SMS-сообщений является существенной статьей расходов в рамках бизнес-модели компании.
Максимальная длина SMS - 160 символов латиницей или 70 кириллицей. Длинные сообщения разбиваются на части, каждая из которых тарифицируется отдельно.
Латиница выходит почти в два раза дешевле. А шлюзы, не умеющие в Unicode в 2024 году, придется очень сильно поискать :)
В 2011 просто случился разворот бизнеса в сторону маркетплейса. А до этого Озон многие годы был практически монополистом на онлайновом книжном рынке. С сильно развитой логистикой. К 2011 году помимо денег у них был ещё и огромный опыт на схожем рынке, каким мало кто из конкурентов мог похвастаться.
Извините, но 9 слоев для начинающего архитектора как-то маловато.
"Жил грешно и умер смешно".
А что в этом плохого? Ну, кроме того, что некоторые библиотеки одной компании не понимают это и кидают ошибку?
Ошибки можно и через механизм raiserror передавать. Бонусом будет возможность вернуть не только код ошибки, но и текстовое сообщение. И ещё по severity их отсортировать. Ну и возможность использовать try/catch для всего, а не городить огород с проверкой части ошибок по кодам возврата.
Как-то странно видеть в статье, которая топит за try/catch и прочие новомодные фишки, полное игнорирование того, что изоляция транзакций может быть реализована разными способами. Snapshot isolation когда появился? В 2012? В 2008?
Скажите, а сколько раз в production code вам доводилось использовать TRUNCATE? Имеет ли смысл спрашивать про нюансы ее работы, если они нужны раз в три года и их всегда можно подсмотреть в справочнике?
И насколько важны именно аспекты работы TRUNCATE в контексте isolation? Мне кажется куда более полезным на практике, что кандидат представляет, что TRUNCATE в сиквел сервере это DML операция, требующая как минимум прав на ALTER TABLE. И ее нельзя, например, применить к таблице, на которую ссылается внешний ключ. И всяких других граблях, из-за которых использование TRUNCATE может оказаться плохой идеей.
ACID относится к сохраняемому состоянию данных СУБД, а значения локальных переменных, даже табличных, в него явно не входят. Никого ведь не удивляет, что при откате транзакции какой-нибудь
set @answer = 32
не откатывается.IMHO, на миддла достаточно будет спросить:
Базовый синтаксис SQL, попросить написать какой-нибудь несложный запрос с рекурсией и оконными функциями
Уровни изоляции, какие бывают и какие эффекты можно наблюдать на каждом уровне
Индексы и стратегии джойнов, их алгоритмическую сложность
Планы запросов, как их курить и что делать, если сиквел сервер выбирает хреновый план.
Для РФ не нужно ничего распознавать, у нас все чеки электронные. Достаточно отсканировать QR код на чеке, в нем есть все, что нужно для загрузки данных чека с сайта ФНС. Есть даже официальное приложение для этого, "Проверка чеков".
Ирония в том, что изначально SQLite разрабатывалась для весьма серьезных задач, для управления конфигурацией эскадренных миноносцев.
Для случаев, когда запись редка, особенно конкурентная, а чтений много и объем данных относительно небольшой, SQLite неплохо справляется. Например, для каких-нибудь развесистых конфигов.
Это как OpenJ9 v0.40.0
Как старпер с четвертьвековым стажем скажу: никогда не стоит экономить на газе, рано или поздно вы эпично обосретесь.
Работодатель себе уже кокаин выдал, предвкушая встречу с заказчиком :)