Почему Hibernate разбивает мою пакетную вставку на 3 запроса - PullRequest
0 голосов
/ 10 апреля 2020

В настоящее время я пытаюсь реализовать пакетную вставку с использованием Hibernate. Вот несколько вещей, которые я реализовал:

1. Сущность

@Entity
@Table(name = "my_bean_table")
@Data
public class MyBean {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "bean_c_seq", allocationSize=50)
    @Column(name = "my_bean_id")
    private Long id;

    @Column(name = "my_bean_name")
    private String name;

    @Column(name = "my_bean_age")
    private int age;

    public MyBean(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

2.application.properties

Спящий режим и источник данных настраиваются следующим образом:

spring.datasource.url=jdbc:postgresql://{ip}:{port}/${db}?reWriteBatchedInserts=true&loggerLevel=TRACE&loggerFile=pgjdbc.log
spring.jpa.show-sql=truespring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true

Примечание: &loggerLevel=TRACE&loggerFile=pgjdbc.log предназначен для целей отладки

3. Элементы в моей PostgresSQL базе данных

CREATE TABLE my_bean_table
(
    my_bean_id bigint NOT NULL DEFAULT nextval('my_bean_seq'::regclass),
    my_bean_name "char(100)" NOT NULL,
    my_bean_age smallint NOT NULL,
    CONSTRAINT bean_c_table_pkey PRIMARY KEY (bean_c_id)
)

CREATE SEQUENCE my_bean_seq
    INCREMENT 50
    START 1
    MINVALUE 1
    MAXVALUE 9223372036854775807
    CACHE 1;

РЕДАКТИРОВАТЬ: добавлен ItemWriter

public class MyBeanWriter implements ItemWriter<MyBean> {

    private Logger logger = LoggerFactory.getLogger(MyBeanWriter .class);

    @Autowired
    MyBeanRepository repository;

    @Override
    public void write(List<? extends BeanFluxC> items) throws Exception {
        repository.saveAll(items);
    }

}

интервал фиксации также установлен на 50 .

В файле журнала, предоставляемом драйвером jdb c, я получаю следующие строки:

avr. 10, 2020 7:26:48 PM org.postgresql.core.v3.QueryExecutorImpl execute
FINEST:   batch execute 3 queries, handler=org.postgresql.jdbc.BatchResultHandler@1317ac2c, maxRows=0, fetchSize=0, flags=5
avr. 10, 2020 7:26:48 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST:  FE=> Parse(stmt=null,query="insert into my_bean_table (my_bean_age, my_bean_name, my_bean_id) values ($1, $2, $3),($4, $5, $6),($7, $8, $9),($10, $11, $12),($13, $14, $15),($16, $17, $18),($19, $20, $21),($22, $23, $24),($25, $26, $27),($28, $29, $30),($31, $32, $33),($34, $35, $36),($37, $38, $39),($40, $41, $42),($43, $44, $45),($46, $47, $48),($49, $50, $51),($52, $53, $54),($55, $56, $57),($58, $59, $60),($61, $62, $63),($64, $65, $66),($67, $68, $69),($70, $71, $72),($73, $74, $75),($76, $77, $78),($79, $80, $81),($82, $83, $84),($85, $86, $87),($88, $89, $90),($91, $92, $93),($94, $95, $96)",oids={23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20})
...
FINEST:  FE=> Execute(portal=null,limit=1)
avr. 10, 2020 7:26:48 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST:  FE=> Parse(stmt=null,query="insert into my_bean_table (my_bean_age, my_bean_name, my_bean_id) values ($1, $2, $3),($4, $5, $6),($7, $8, $9),($10, $11, $12),($13, $14, $15),($16, $17, $18),($19, $20, $21),($22, $23, $24),($25, $26, $27),($28, $29, $30),($31, $32, $33),($34, $35, $36),($37, $38, $39),($40, $41, $42),($43, $44, $45),($46, $47, $48)",oids={23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20,23,1043,20})
...
avr. 10, 2020 7:26:48 PM org.postgresql.core.v3.QueryExecutorImpl sendParse
FINEST:  FE=> Parse(stmt=null,query="insert into my_bean_table (my_bean_age, my_bean_name, my_bean_id) values ($1, $2, $3),($4, $5, $6)",oids={23,1043,20,23,1043,20})

Вот мой вопрос: почему пакетный запрос разбит на 3 запроса:

  • первый запрос: 32 элемента
  • второй запрос: 16 элементов
  • третий запрос: 2 элемента

Примечание: я пытался установить размер пакета до 100 и 200, и я все еще получил 3 разных запроса.

Ответы [ 2 ]

1 голос
/ 11 апреля 2020

Найден при отладке класса PgPreparedStatement и его метода transformQueriesAndParameters():

 @Override
  protected void transformQueriesAndParameters() throws SQLException {
    ...
    BatchedQuery originalQuery = (BatchedQuery) preparedQuery.query;
    // Single query cannot have more than {@link Short#MAX_VALUE} binds, thus
    // the number of multi-values blocks should be capped.
    // Typically, it does not make much sense to batch more than 128 rows: performance
    // does not improve much after updating 128 statements with 1 multi-valued one, thus
    // we cap maximum batch size and split there.
    ...
    final int highestBlockCount = 128;
    final int maxValueBlocks = bindCount == 0 ? 1024 /* if no binds, use 1024 rows */
        : Integer.highestOneBit( // deriveForMultiBatch supports powers of two only
            Math.min(Math.max(1, (Short.MAX_VALUE - 1) / bindCount), highestBlockCount));
}
  • один запрос для пакетной вставки может содержать не более 128 элементов
  • другое количество строк будет равно двум

Сейчас я использую 128 в качестве приращения последовательности в базе данных и в качестве параметра размера пакета на стороне клиента, это работает как шарм .

0 голосов
/ 11 апреля 2020

У меня нет окончательного ответа, но это поведение кажется очень похожим и, вероятно, по той же причине, что и при пакетной загрузке.

Используются разные операторы с числом наборов параметров, равным степени двух. Это должно минимизировать количество различных выполненных операторов. Базы данных должны анализировать операторы и использовать кэши для хранения проанализированных операторов. Если клиент выполнит тонны операторов, которые выполняют по сути одно и то же, но отличаются по количеству наборов параметров, это сделает кэш бесполезным.

С другой стороны, я не видел его с пакетными вставками, а только с массовые операции извлечения. У меня есть пара предположений, почему это может происходить:

  1. Ваши идентификаторы генерируются базой данных, поэтому, прежде чем данные могут быть записаны в базу данных, идентификаторы должны быть запрошены из базы данных последовательность. Может быть, поведение выбора, которое просачивается через вставки

  2. Это может быть оптимизация, выполненная драйвером JDB C, который переписывает подобные операторы auf. * ​​1013 *

  3. Hibernate делает это все время, и я просто пропустил это. Хотя я думаю, что это странно делать, когда количество наборов параметров равно размеру пакета.

...