2013-06-26 5 views
0

Я испытываю неэффективные запросы на спящий режим, и я считаю, что причиной является так называемая проблема N + 1, о которой я читал в других местах. Тем не менее, я не уверен, как решить проблему, поскольку мои объекты вложены, и большинство примеров, которые я видел в Интернете, тривиальны.Избегайте проблемы N + 1 во вложенном объекте?

Вот краткое описание моего приложения.

У меня есть класс ExecutionTrace, который имеет отношения «один ко многим» с классом TraceLine. TraceLine в свою очередь имеет отношения «один ко многим» с классом TraceReference. Каждый из этих классов имеет атрибут id, сгенерированный Hibernate. (Все классы содержат несколько переменных-членов, но пока я их не буду опускать.)

В моем приложении я запрашиваю для определенного ExecutionTrace, а затем мое приложение будет перебирать все его объекты TraceLine и их TraceReference объектов. Таким образом, я задал LazyCollectionOption к false для всех этих отношений:

// ExecutionTrace.java: 
... 
@Id @GeneratedValue 
@Column(name = "file_id") 
public long id; 
... 
@OneToMany(mappedBy = "container_trace_", cascade = CascadeType.ALL) 
@LazyCollection(LazyCollectionOption.FALSE) 
@OrderBy(clause = "id") 
public List<TraceLine> lines_ = new ArrayList<TraceLine>(); 
... 


// TraceLine.java: 
... 
@Id @GeneratedValue 
@Column(name = "trace_line_id") 
@Index(name = "traceIDIndex") 
public long id; 
... 
@ManyToOne(cascade = CascadeType.ALL) 
@JoinColumn(name = "file_id") 
@LazyCollection(LazyCollectionOption.TRUE) 
public ExecutionTrace container_trace_; 
... 
@OneToMany(mappedBy = "trace_line_", cascade = CascadeType.ALL) 
@LazyCollection(LazyCollectionOption.FALSE) 
@OrderBy(clause = "id") 
public List<TraceReference> trace_references_ = new ArrayList<TraceReference>(); 
... 


// TraceReference.java: 
... 
@Id @GeneratedValue 
@Column(name = "trace_reference_id") 
@Index(name="TraceReferenceIDIndex") 
public long id; 
... 
@ManyToOne(cascade = CascadeType.ALL) 
@JoinColumn(name = "trace_line_id") 
@LazyCollection(LazyCollectionOption.TRUE) 
public TraceLine trace_line_; 
... 

Мой спящий режим запроса для выбора ExecutionTrace является:

String f_hash = ...; 

Transaction tx3 = session.beginTransaction(); 
    String queryString = " from ExecutionTrace where hash_ = '"+ f_hash +"'"; 

List<ExecutionTrace> res2 = session.createQuery(queryString).list(); 
tx3.commit(); 

В одном из моих простых тестов, есть 1 ExecutionTrace объект, который содержит 645 TraceLine объектов, каждый из которых содержит от 1 до 5 TraceReference объектов. Запрос и код для итерации через все они занимают более 50 секунд на скромном оборудовании. База данных PostgreSQL работает на том же компьютере, что и приложение.

Глядя на запросах SQL брошенных Hibernate, я заметил, что:

  • Существует один запрос, чтобы выбрать все объекты TraceLine. Это хорошо.
  • Найдено 645 запросов для выбора TraceReference объектов. Это плохо. Кроме того, у всех их есть много внешних объединений, что делает их неэффективными.

Например, вот запрос на TraceReference:

select 
     trace_refe0_.trace_line_id as trace5_6_18_, 
     trace_refe0_.trace_reference_id as trace1_18_, 
     trace_refe0_.trace_reference_id as trace1_7_17_, 
     trace_refe0_.new_value as new2_7_17_, 
     trace_refe0_.old_value as old3_7_17_, 
     trace_refe0_.operand_id as operand4_7_17_, 
     trace_refe0_.trace_line_id as trace5_7_17_, 
     operand1_.operand_id as operand1_11_0_, 
     operand1_.operand_index as operand2_11_0_, 
     operand1_.is_dest as is3_11_0_, 
     operand1_.is_source as is4_11_0_, 
     operand1_.operand_size as operand5_11_0_, 
     operand1_.operand_u_prop_id as operand6_11_0_, 
     operand1_.maps_to_variable as maps7_11_0_, 
     operandtyp2_.operand_prop_id as operand2_12_1_, 
     operandtyp2_1_.constant_value as constant1_13_1_, 
     operandtyp2_2_.register_name as register1_14_1_, 
     operandtyp2_4_.base_operand_id as base2_16_1_, 
     operandtyp2_4_.displacement_operand_id as displace3_16_1_, 
     operandtyp2_4_.index_operand_id as index4_16_1_, 
     operandtyp2_4_.ml_name as ml1_16_1_, 
     operandtyp2_4_.scale_operand_id as scale5_16_1_, 
     operandtyp2_.DTYPE as DTYPE12_1_, 
     register3_.operand_prop_id as operand2_12_2_, 
     register3_1_.register_name as register1_14_2_, 
     constant4_.operand_prop_id as operand2_12_3_, 
     constant4_1_.constant_value as constant1_13_3_, 
     register5_.operand_prop_id as operand2_12_4_, 
     register5_1_.register_name as register1_14_4_, 
     constant6_.operand_prop_id as operand2_12_5_, 
     constant6_1_.constant_value as constant1_13_5_, 
     variable7_.variable_id as variable1_18_6_, 
     variable7_.file_id as file6_18_6_, 
     variable7_.function_id as function7_18_6_, 
     variable7_.variable_is_argument as variable2_18_6_, 
     variable7_.variable_is_global as variable3_18_6_, 
     variable7_.variable_is_static as variable4_18_6_, 
     variable7_.variable_name as variable5_18_6_, 
     variable7_.data_type_id as data8_18_6_, 
     sourcefile8_.file_id as file2_0_7_, 
     sourcefile8_.file_path as file3_0_7_, 
     sourcefile8_.file_name as file4_0_7_, 
     sourcefile8_.source_lang as source5_0_7_, 
     function9_.function_id as function1_8_8_, 
     function9_.file_id as file6_8_8_, 
     function9_.function_end_address as function2_8_8_, 
     function9_.function_address as function3_8_8_, 
     function9_.function_name as function4_8_8_, 
     function9_.function_is_static as function5_8_8_, 
     sourcefile10_.file_id as file2_0_9_, 
     sourcefile10_.file_path as file3_0_9_, 
     sourcefile10_.file_name as file4_0_9_, 
     sourcefile10_.source_lang as source5_0_9_, 
     datatype11_.data_type_id as data2_19_10_, 
     datatype11_.defined_by as defined5_19_10_, 
     datatype11_.data_type_length as data3_19_10_, 
     datatype11_.data_type_name as data4_19_10_, 
     datatype11_2_.pointer_to as pointer1_20_10_, 
     datatype11_3_.array_size as array1_21_10_, 
     datatype11_3_.contains_type as contains2_21_10_, 
     datatype11_4_.enumerator_count as enumerator1_22_10_, 
     datatype11_5_.type_ as type1_24_10_, 
     datatype11_6_.constant_of_type as constant1_25_10_, 
     datatype11_7_.return_type as return1_27_10_, 
     datatype11_.DTYPE as DTYPE19_10_, 
     sourcefile12_.file_id as file2_0_11_, 
     sourcefile12_.file_path as file3_0_11_, 
     sourcefile12_.file_name as file4_0_11_, 
     sourcefile12_.source_lang as source5_0_11_, 
     datatype13_.data_type_id as data2_19_12_, 
     datatype13_.defined_by as defined5_19_12_, 
     datatype13_.data_type_length as data3_19_12_, 
     datatype13_.data_type_name as data4_19_12_, 
     datatype13_2_.pointer_to as pointer1_20_12_, 
     datatype13_3_.array_size as array1_21_12_, 
     datatype13_3_.contains_type as contains2_21_12_, 
     datatype13_4_.enumerator_count as enumerator1_22_12_, 
     datatype13_5_.type_ as type1_24_12_, 
     datatype13_6_.constant_of_type as constant1_25_12_, 
     datatype13_7_.return_type as return1_27_12_, 
     datatype13_.DTYPE as DTYPE19_12_, 
     datatype14_.data_type_id as data2_19_13_, 
     datatype14_.defined_by as defined5_19_13_, 
     datatype14_.data_type_length as data3_19_13_, 
     datatype14_.data_type_name as data4_19_13_, 
     datatype14_2_.pointer_to as pointer1_20_13_, 
     datatype14_3_.array_size as array1_21_13_, 
     datatype14_3_.contains_type as contains2_21_13_, 
     datatype14_4_.enumerator_count as enumerator1_22_13_, 
     datatype14_5_.type_ as type1_24_13_, 
     datatype14_6_.constant_of_type as constant1_25_13_, 
     datatype14_7_.return_type as return1_27_13_, 
     datatype14_.DTYPE as DTYPE19_13_, 
     datatype15_.data_type_id as data2_19_14_, 
     datatype15_.defined_by as defined5_19_14_, 
     datatype15_.data_type_length as data3_19_14_, 
     datatype15_.data_type_name as data4_19_14_, 
     datatype15_2_.pointer_to as pointer1_20_14_, 
     datatype15_3_.array_size as array1_21_14_, 
     datatype15_3_.contains_type as contains2_21_14_, 
     datatype15_4_.enumerator_count as enumerator1_22_14_, 
     datatype15_5_.type_ as type1_24_14_, 
     datatype15_6_.constant_of_type as constant1_25_14_, 
     datatype15_7_.return_type as return1_27_14_, 
     datatype15_.DTYPE as DTYPE19_14_, 
     datatype16_.data_type_id as data2_19_15_, 
     datatype16_.defined_by as defined5_19_15_, 
     datatype16_.data_type_length as data3_19_15_, 
     datatype16_.data_type_name as data4_19_15_, 
     datatype16_2_.pointer_to as pointer1_20_15_, 
     datatype16_3_.array_size as array1_21_15_, 
     datatype16_3_.contains_type as contains2_21_15_, 
     datatype16_4_.enumerator_count as enumerator1_22_15_, 
     datatype16_5_.type_ as type1_24_15_, 
     datatype16_6_.constant_of_type as constant1_25_15_, 
     datatype16_7_.return_type as return1_27_15_, 
     datatype16_.DTYPE as DTYPE19_15_, 
     datatype17_.data_type_id as data2_19_16_, 
     datatype17_.defined_by as defined5_19_16_, 
     datatype17_.data_type_length as data3_19_16_, 
     datatype17_.data_type_name as data4_19_16_, 
     datatype17_2_.pointer_to as pointer1_20_16_, 
     datatype17_3_.array_size as array1_21_16_, 
     datatype17_3_.contains_type as contains2_21_16_, 
     datatype17_4_.enumerator_count as enumerator1_22_16_, 
     datatype17_5_.type_ as type1_24_16_, 
     datatype17_6_.constant_of_type as constant1_25_16_, 
     datatype17_7_.return_type as return1_27_16_, 
     datatype17_.DTYPE as DTYPE19_16_ 
    from 
     TraceReference trace_refe0_ 
    left outer join 
     Operand operand1_ 
      on trace_refe0_.operand_id=operand1_.operand_id 
    left outer join 
     OperandProperties operandtyp2_ 
      on operand1_.operand_u_prop_id=operandtyp2_.operand_prop_id 
    left outer join 
     Constant operandtyp2_1_ 
      on operandtyp2_.operand_prop_id=operandtyp2_1_.operand_prop_id 
    left outer join 
     Register operandtyp2_2_ 
      on operandtyp2_.operand_prop_id=operandtyp2_2_.operand_prop_id 
    left outer join 
     UnknownOperandProperties operandtyp2_3_ 
      on operandtyp2_.operand_prop_id=operandtyp2_3_.operand_prop_id 
    left outer join 
     MemoryLocation operandtyp2_4_ 
      on operandtyp2_.operand_prop_id=operandtyp2_4_.operand_prop_id 
    left outer join 
     OperandProperties register3_ 
      on operandtyp2_4_.base_operand_id=register3_.operand_prop_id 
    left outer join 
     Register register3_1_ 
      on register3_.operand_prop_id=register3_1_.operand_prop_id 
    left outer join 
     OperandProperties constant4_ 
      on operandtyp2_4_.displacement_operand_id=constant4_.operand_prop_id 
    left outer join 
     Constant constant4_1_ 
      on constant4_.operand_prop_id=constant4_1_.operand_prop_id 
    left outer join 
     OperandProperties register5_ 
      on operandtyp2_4_.index_operand_id=register5_.operand_prop_id 
    left outer join 
     Register register5_1_ 
      on register5_.operand_prop_id=register5_1_.operand_prop_id 
    left outer join 
     OperandProperties constant6_ 
      on operandtyp2_4_.scale_operand_id=constant6_.operand_prop_id 
    left outer join 
     Constant constant6_1_ 
      on constant6_.operand_prop_id=constant6_1_.operand_prop_id 
    left outer join 
     Variable variable7_ 
      on operand1_.maps_to_variable=variable7_.variable_id 
    left outer join 
     FileOnDisk sourcefile8_ 
      on variable7_.file_id=sourcefile8_.file_id 
    left outer join 
     ObjectFile sourcefile8_1_ 
      on sourcefile8_.file_id=sourcefile8_1_.file_id 
    left outer join 
     SourceFile sourcefile8_2_ 
      on sourcefile8_.file_id=sourcefile8_2_.file_id 
    left outer join 
     SourceFunction function9_ 
      on variable7_.function_id=function9_.function_id 
    left outer join 
     FileOnDisk sourcefile10_ 
      on function9_.file_id=sourcefile10_.file_id 
    left outer join 
     ObjectFile sourcefile10_1_ 
      on sourcefile10_.file_id=sourcefile10_1_.file_id 
    left outer join 
     SourceFile sourcefile10_2_ 
      on sourcefile10_.file_id=sourcefile10_2_.file_id 
    left outer join 
     DataType datatype11_ 
      on variable7_.data_type_id=datatype11_.data_type_id 
    left outer join 
     Pointer datatype11_1_ 
      on datatype11_.data_type_id=datatype11_1_.data_type_id 
    left outer join 
     Pointer datatype11_2_ 
      on datatype11_.data_type_id=datatype11_2_.data_type_id 
    left outer join 
     SourceArray datatype11_3_ 
      on datatype11_.data_type_id=datatype11_3_.data_type_id 
    left outer join 
     Enum datatype11_4_ 
      on datatype11_.data_type_id=datatype11_4_.data_type_id 
    left outer join 
     Typedef datatype11_5_ 
      on datatype11_.data_type_id=datatype11_5_.data_type_id 
    left outer join 
     ConstantType datatype11_6_ 
      on datatype11_.data_type_id=datatype11_6_.data_type_id 
    left outer join 
     FunctionType datatype11_7_ 
      on datatype11_.data_type_id=datatype11_7_.data_type_id 
    left outer join 
     FileOnDisk sourcefile12_ 
      on datatype11_.defined_by=sourcefile12_.file_id 
    left outer join 
     ObjectFile sourcefile12_1_ 
      on sourcefile12_.file_id=sourcefile12_1_.file_id 
    left outer join 
     SourceFile sourcefile12_2_ 
      on sourcefile12_.file_id=sourcefile12_2_.file_id 
    left outer join 
     DataType datatype13_ 
      on datatype11_2_.pointer_to=datatype13_.data_type_id 
    left outer join 
     Pointer datatype13_1_ 
      on datatype13_.data_type_id=datatype13_1_.data_type_id 
    left outer join 
     Pointer datatype13_2_ 
      on datatype13_.data_type_id=datatype13_2_.data_type_id 
    left outer join 
     SourceArray datatype13_3_ 
      on datatype13_.data_type_id=datatype13_3_.data_type_id 
    left outer join 
     Enum datatype13_4_ 
      on datatype13_.data_type_id=datatype13_4_.data_type_id 
    left outer join 
     Typedef datatype13_5_ 
      on datatype13_.data_type_id=datatype13_5_.data_type_id 
    left outer join 
     ConstantType datatype13_6_ 
      on datatype13_.data_type_id=datatype13_6_.data_type_id 
    left outer join 
     FunctionType datatype13_7_ 
      on datatype13_.data_type_id=datatype13_7_.data_type_id 
    left outer join 
     DataType datatype14_ 
      on datatype13_3_.contains_type=datatype14_.data_type_id 
    left outer join 
     Pointer datatype14_1_ 
      on datatype14_.data_type_id=datatype14_1_.data_type_id 
    left outer join 
     Pointer datatype14_2_ 
      on datatype14_.data_type_id=datatype14_2_.data_type_id 
    left outer join 
     SourceArray datatype14_3_ 
      on datatype14_.data_type_id=datatype14_3_.data_type_id 
    left outer join 
     Enum datatype14_4_ 
      on datatype14_.data_type_id=datatype14_4_.data_type_id 
    left outer join 
     Typedef datatype14_5_ 
      on datatype14_.data_type_id=datatype14_5_.data_type_id 
    left outer join 
     ConstantType datatype14_6_ 
      on datatype14_.data_type_id=datatype14_6_.data_type_id 
    left outer join 
     FunctionType datatype14_7_ 
      on datatype14_.data_type_id=datatype14_7_.data_type_id 
    left outer join 
     DataType datatype15_ 
      on datatype14_5_.type_=datatype15_.data_type_id 
    left outer join 
     Pointer datatype15_1_ 
      on datatype15_.data_type_id=datatype15_1_.data_type_id 
    left outer join 
     Pointer datatype15_2_ 
      on datatype15_.data_type_id=datatype15_2_.data_type_id 
    left outer join 
     SourceArray datatype15_3_ 
      on datatype15_.data_type_id=datatype15_3_.data_type_id 
    left outer join 
     Enum datatype15_4_ 
      on datatype15_.data_type_id=datatype15_4_.data_type_id 
    left outer join 
     Typedef datatype15_5_ 
      on datatype15_.data_type_id=datatype15_5_.data_type_id 
    left outer join 
     ConstantType datatype15_6_ 
      on datatype15_.data_type_id=datatype15_6_.data_type_id 
    left outer join 
     FunctionType datatype15_7_ 
      on datatype15_.data_type_id=datatype15_7_.data_type_id 
    left outer join 
     DataType datatype16_ 
      on datatype15_6_.constant_of_type=datatype16_.data_type_id 
    left outer join 
     Pointer datatype16_1_ 
      on datatype16_.data_type_id=datatype16_1_.data_type_id 
    left outer join 
     Pointer datatype16_2_ 
      on datatype16_.data_type_id=datatype16_2_.data_type_id 
    left outer join 
     SourceArray datatype16_3_ 
      on datatype16_.data_type_id=datatype16_3_.data_type_id 
    left outer join 
     Enum datatype16_4_ 
      on datatype16_.data_type_id=datatype16_4_.data_type_id 
    left outer join 
     Typedef datatype16_5_ 
      on datatype16_.data_type_id=datatype16_5_.data_type_id 
    left outer join 
     ConstantType datatype16_6_ 
      on datatype16_.data_type_id=datatype16_6_.data_type_id 
    left outer join 
     FunctionType datatype16_7_ 
      on datatype16_.data_type_id=datatype16_7_.data_type_id 
    left outer join 
     DataType datatype17_ 
      on datatype16_7_.return_type=datatype17_.data_type_id 
    left outer join 
     Pointer datatype17_1_ 
      on datatype17_.data_type_id=datatype17_1_.data_type_id 
    left outer join 
     Pointer datatype17_2_ 
      on datatype17_.data_type_id=datatype17_2_.data_type_id 
    left outer join 
     SourceArray datatype17_3_ 
      on datatype17_.data_type_id=datatype17_3_.data_type_id 
    left outer join 
     Enum datatype17_4_ 
      on datatype17_.data_type_id=datatype17_4_.data_type_id 
    left outer join 
     Typedef datatype17_5_ 
      on datatype17_.data_type_id=datatype17_5_.data_type_id 
    left outer join 
     ConstantType datatype17_6_ 
      on datatype17_.data_type_id=datatype17_6_.data_type_id 
    left outer join 
     FunctionType datatype17_7_ 
      on datatype17_.data_type_id=datatype17_7_.data_type_id 
    where 
     trace_refe0_.trace_line_id=? 
    order by 
     trace_refe0_.trace_reference_id 
TRACE [main] 06/26/13 10:00:54 preparing statement 
TRACE [main] 06/26/13 10:00:54 binding parameter [1] as [BIGINT] - 82883 

Я попытался модифицировать мой запрос так, чтобы она выполняла некоторые внутренние соединения:

String queryString = " from ExecutionTrace as et " + 
       "inner join fetch et.lines_ as lines " + 
     "where et.hash_ = '"+ f_hash +"'"; 

Это привело 0 запросов, чтобы выбрать TraceLine s и 645 запросов для выбора TraceReference s. Нет помощи. Затем я попытался множественным внутренние соединения:

String queryString = 
    " from ExecutionTrace as et " + 
     "inner join fetch et.lines_ as lines " + 
     "inner join fetch lines.trace_references_ " + 
    "where et.hash_ = '"+ f_hash +"'"; 

Это привело к исключению:

Exception in thread "main" org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags. 

Я чувствую, что я близок к решению этой проблемы, но просто не хватает что-то легкое.

+0

Это 'MultipleBagFetchException' is, IIRC, вызвано тем, что ваши коллекции - это оба списка. В этом случае Hibernate не знает, как выполнять запросы, потому что списки сортируются. Если возможно, или если у вас есть специальная возможность сортировать 'TraceLine'/'TraceReference', тогда вы можете использовать 'Set' вместо этого. –

ответ

0

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

1.Использование подвыбора режим извлечения

Используйте @Fetch аннотацию, чтобы вызвать Hibernate к продукту суб-выберите запрос:

@OneToMany(mappedBy = "container_trace_", cascade = CascadeType.ALL) 
@LazyCollection(LazyCollectionOption.TRUE) 
@OrderBy(clause = "id") 
@Fetch(FetchMode.SUBSELECT) 
public List<TraceLine> lines_ = new ArrayList<TraceLine>(); 
  • Pro: выше аннотации заставят все TraceLine объектов быть извлечены в том же (т. е. в том же предложении выбора), что и ExecutionTrace, что приводит к гораздо меньшему количеству операторов select.
  • Con: Если у ExecutionTrace есть много объектов TraceLine, выбор субтитров приведет к тому, что ваш JVM будет исчерпан.

2. Используйте Порции

Если у вас слишком много TraceLine объектов, чтобы вписаться в память, и, следовательно, вы не можете использовать подзапрос, как описано выше, у вас есть еще один вариант. Используйте аннотацию @BatchSize, чтобы заставить Hibernate получать 10 TraceLine объектов за раз, а не только один.

@OneToMany(mappedBy = "container_trace_", cascade = CascadeType.ALL) 
@LazyCollection(LazyCollectionOption.TRUE) 
@OrderBy(clause = "id") 
@BatchSize(size = 10) 
public List<TraceLine> lines_ = new ArrayList<TraceLine>(); 
  • Pro: Это эффективно снижает количество выбирает на коэффициент 10
  • Pro: не будет работать из памяти
  • Con: Еще будет большое количество выбирает, если количество объектов TraceLine велико.

Ни одно решение не является совершенным. В моем заявлении я пошел с партиями.