Comments 16
Что-то странная статья. На сколько я помню в Hibernate есть Batch Insert. Судя по статье это именно то что вам нужно.
Абсолютно верно! Есть batch, его размер задается через проперти. У спринга чуть по другому называется.
Есть проблема над решением которой дествительно можно было бы написать статью:
При вставке новых больших данных с id!=null, хибер сначала лезет в БД и узнает есть ли уже такая запись, если нет, делает insert иначе update. Получается на 20К вставок идет 20К select.
Тут варианты:
— либо писать ручками — и довольно много ручного труда
— либо сущность наследовать от Interface Persistable переоперделять метод isNew [не самый безопасный вариант, ломается логика save, delete и возможно еще каких-либо]
Я предпочел ручной вариант хоть и с postgreSql при этом приходится повозится, т.к. если нужно чтобы запись при вставке уже существующего id обновлялась, приходится все колонки дублировать.
Есть проблема над решением которой дествительно можно было бы написать статью:
При вставке новых больших данных с id!=null, хибер сначала лезет в БД и узнает есть ли уже такая запись, если нет, делает insert иначе update. Получается на 20К вставок идет 20К select.
Тут варианты:
— либо писать ручками — и довольно много ручного труда
— либо сущность наследовать от Interface Persistable переоперделять метод isNew [не самый безопасный вариант, ломается логика save, delete и возможно еще каких-либо]
Я предпочел ручной вариант хоть и с postgreSql при этом приходится повозится, т.к. если нужно чтобы запись при вставке уже существующего id обновлялась, приходится все колонки дублировать.
>Тут варианты:
>— либо писать ручками — и довольно много ручного труда
Ну вообще у MS SQL есть merge, и труда там не так уж и много. Нестандартно, необычно — может быть, но не более.
>— либо писать ручками — и довольно много ручного труда
Ну вообще у MS SQL есть merge, и труда там не так уж и много. Нестандартно, необычно — может быть, но не более.
Можете попробовать, если у вас uuid. Ну или с другими генераторами поэкспериментировать.
@GeneratedValue(generator = "UUID")
@GenericGenerator(
name = "UUID",
strategy = "org.hibernate.id.UUIDGenerator")
Почему бы не написать сторед-процедуру, которой в качестве параметра передать сериализованный в строку XML? Разве это не лучше, чем лапшекод на новом гиперуровне, когда сначала мы объявляем свободу от конкретики RDBMS, а затем делаем трудночитаемые и непроверяемые inline-вставки на ассемблере T-SQL?
Уж лучше такое иметь в контролируемом в design-time (пусть даже в database-design-time ;) ) модуле, чем в виде непроверяемой многострочной лапши с опасностью SQL-инъекции через недоэкранированные литералы
Уж лучше такое иметь в контролируемом в design-time (пусть даже в database-design-time ;) ) модуле, чем в виде непроверяемой многострочной лапши с опасностью SQL-инъекции через недоэкранированные литералы
не впечатляет. 500к записей в секунду и выше — вот это будет близко к пределу средненького сервера
А зачем отдельная таблица? Если вы создаете xml то его можно напрямую использовать в native query.
И еслу уж вобще придираться, возможен вариант с native query и массивом как параметр, тогда вообще xml не нужен.
И еслу уж вобще придираться, возможен вариант с native query и массивом как параметр, тогда вообще xml не нужен.
Вместо включения batch в хибере или переходом на что-то более легковесное (jooq, например), добавим еще xml, чтобы точно на все деньги было.
Вы подняли ворох проблем, который утопил нашего Enterprise Architect из-за неверного вектора решения. :)
Итак, проблемы:
1. Действительно, Hibernate генерит массу отдельных insert запросов. Можно оптимизировать on-prem, но в облаке небольшой network latency на 500,000 добавлений вываливается в панику клиентов.
Однако, есть еще и серверная сторона:
2. Чем больше индексов в OLTP базе, тем дольше идет перестройка после каждого insert
3. Чем больше RI, тем дольше верификация
4. При наличии дочерних таблиц (что в статье не было учтено), транзакция утяжеляется.
Наше решение:
— валидация данных перед bulk insert
— bulk insert
— перестройка индексов по окончании добавления данных
Проблема решения: при невалидных данных или ошибке, откатывается весь импорт.
Плюс: максимально-возможная скорость т.к. иморт данных идет с закрытыми глазами, без проверок на каждой записи.
Итак, проблемы:
1. Действительно, Hibernate генерит массу отдельных insert запросов. Можно оптимизировать on-prem, но в облаке небольшой network latency на 500,000 добавлений вываливается в панику клиентов.
Однако, есть еще и серверная сторона:
2. Чем больше индексов в OLTP базе, тем дольше идет перестройка после каждого insert
3. Чем больше RI, тем дольше верификация
4. При наличии дочерних таблиц (что в статье не было учтено), транзакция утяжеляется.
Наше решение:
— валидация данных перед bulk insert
— bulk insert
— перестройка индексов по окончании добавления данных
Проблема решения: при невалидных данных или ошибке, откатывается весь импорт.
Плюс: максимально-возможная скорость т.к. иморт данных идет с закрытыми глазами, без проверок на каждой записи.
А что если между «валидация данных перед bulk insert» и «bulk insert» другая сессия изменит данные и они станут не валидными при нашем «bulk insert»? Блокировать всю таблицу предлагаете?
П.С. а если есть внешние ключи, то и все связанные таблицы тоже
П.С. а если есть внешние ключи, то и все связанные таблицы тоже
Об этом и написано в конце комментария: в случае невалидных данных/ошибки весь импорт будет откачен, т. к. выполняется одной транзакцией. Но обычно, когда такие вещи делаются, позволять кому-то ещё менять таблицу и/или связанные таблицы не очень хорошая идея и имеет смысл выставить блокировку. Хотя есть и исключения. Логи те же самые. Как вы понимаете, в этом случае крайне маловероятно, что какая-либо сессия так изменит данные, что пакетная вставка окажется невалидной. А вот каждую запись в транзакцию оборачивать в высоконагруженной системе — сервер БД может и не потянуть.
Sign up to leave a comment.
Spring: Ускоряем запись в базу данных с помощью XML