/root/doris/be/src/load/memtable/memtable.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "load/memtable/memtable.h" |
19 | | |
20 | | #include <fmt/format.h> |
21 | | #include <gen_cpp/olap_file.pb.h> |
22 | | #include <pdqsort.h> |
23 | | |
24 | | #include <algorithm> |
25 | | #include <limits> |
26 | | #include <string> |
27 | | #include <vector> |
28 | | |
29 | | #include "bvar/bvar.h" |
30 | | #include "common/config.h" |
31 | | #include "core/column/column.h" |
32 | | #include "exprs/aggregate/aggregate_function_reader.h" |
33 | | #include "exprs/aggregate/aggregate_function_simple_factory.h" |
34 | | #include "load/memtable/memtable_memory_limiter.h" |
35 | | #include "runtime/descriptors.h" |
36 | | #include "runtime/exec_env.h" |
37 | | #include "runtime/runtime_profile.h" |
38 | | #include "runtime/thread_context.h" |
39 | | #include "storage/olap_define.h" |
40 | | #include "storage/tablet/tablet_schema.h" |
41 | | #include "util/debug_points.h" |
42 | | #include "util/stopwatch.hpp" |
43 | | |
44 | | namespace doris { |
45 | | #include "common/compile_check_begin.h" |
46 | | |
47 | | bvar::Adder<int64_t> g_memtable_cnt("memtable_cnt"); |
48 | | bvar::Adder<uint64_t> g_flush_cuz_memtable_full("flush_cuz_memtable_full"); |
49 | | |
50 | | using namespace ErrorCode; |
51 | | |
52 | | namespace { |
53 | | |
54 | | // Share one tracked allocation across a batch. Allocating each row separately also |
55 | | // retains an address record and a stack trace per row when memory diagnostics are enabled. |
56 | | template <typename RowFactory> |
57 | 55 | std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) { |
58 | 55 | auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows); |
59 | 17.5k | for (size_t i = 0; i < num_rows; ++i) { |
60 | 17.4k | rows[i] = make_row(i); |
61 | 17.4k | } |
62 | 55 | return rows; |
63 | 55 | } memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable6insertEPKNS_5BlockERKSt6vectorIjNS_18CustomStdAllocatorIjNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEE3$_1EESt10shared_ptrIA_NS_10RowInBlockEEmOT_ Line | Count | Source | 57 | 45 | std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) { | 58 | 45 | auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows); | 59 | 10.3k | for (size_t i = 0; i < num_rows; ++i) { | 60 | 10.2k | rows[i] = make_row(i); | 61 | 10.2k | } | 62 | 45 | return rows; | 63 | 45 | } |
memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable21_sort_by_cluster_keysEvE3$_1EESt10shared_ptrIA_NS_10RowInBlockEEmOT_ Line | Count | Source | 57 | 5 | std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) { | 58 | 5 | auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows); | 59 | 3.08k | for (size_t i = 0; i < num_rows; ++i) { | 60 | 3.07k | rows[i] = make_row(i); | 61 | 3.07k | } | 62 | 5 | return rows; | 63 | 5 | } |
memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable10_aggregateILb0ELb0EEEvvEUlmE_EESt10shared_ptrIA_NS_10RowInBlockEEmOT_ Line | Count | Source | 57 | 5 | std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) { | 58 | 5 | auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows); | 59 | 4.10k | for (size_t i = 0; i < num_rows; ++i) { | 60 | 4.09k | rows[i] = make_row(i); | 61 | 4.09k | } | 62 | 5 | return rows; | 63 | 5 | } |
Unexecuted instantiation: memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable10_aggregateILb0ELb1EEEvvEUlmE_EESt10shared_ptrIA_NS_10RowInBlockEEmOT_ |
64 | | |
65 | | } // namespace |
66 | | |
67 | | MemTable::MemTable(int64_t tablet_id, std::shared_ptr<TabletSchema> tablet_schema, |
68 | | const std::vector<SlotDescriptor*>* slot_descs, TupleDescriptor* tuple_desc, |
69 | | bool enable_unique_key_mow, PartialUpdateInfo* partial_update_info, |
70 | | const std::shared_ptr<ResourceContext>& resource_ctx) |
71 | 41 | : _mem_type(MemType::ACTIVE), |
72 | 41 | _tablet_id(tablet_id), |
73 | 41 | _enable_unique_key_mow(enable_unique_key_mow), |
74 | 41 | _keys_type(tablet_schema->keys_type()), |
75 | 41 | _tablet_schema(tablet_schema), |
76 | 41 | _resource_ctx(resource_ctx), |
77 | 41 | _is_first_insertion(true), |
78 | 41 | _agg_functions(tablet_schema->num_columns()), |
79 | 41 | _offsets_of_aggregate_states(tablet_schema->num_columns()), |
80 | 41 | _total_size_of_aggregate_states(0) { |
81 | 41 | g_memtable_cnt << 1; |
82 | 41 | _mem_tracker = std::make_shared<MemTracker>(); |
83 | 41 | SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( |
84 | 41 | _resource_ctx->memory_context()->mem_tracker()->write_tracker()); |
85 | 41 | SCOPED_CONSUME_MEM_TRACKER(_mem_tracker); |
86 | 41 | _vec_row_comparator = std::make_shared<RowInBlockComparator>(_tablet_schema); |
87 | 41 | if (partial_update_info != nullptr) { |
88 | 18 | _partial_update_mode = partial_update_info->update_mode(); |
89 | 18 | if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) { |
90 | 0 | if (partial_update_info->is_schema_contains_auto_inc_column && |
91 | 0 | !partial_update_info->is_input_columns_contains_auto_inc_column) { |
92 | 0 | _is_partial_update_and_auto_inc = true; |
93 | 0 | } |
94 | 0 | } |
95 | 18 | } |
96 | 41 | _init_columns_offset_by_slot_descs(slot_descs, tuple_desc); |
97 | | // TODO: Support ZOrderComparator in the future |
98 | 41 | _row_in_blocks = std::make_unique<DorisVector<std::shared_ptr<RowInBlock>>>(); |
99 | 41 | _load_mem_limit = MemInfo::mem_limit() * config::load_process_max_memory_limit_percent / 100; |
100 | 41 | } |
101 | | |
102 | | void MemTable::_init_columns_offset_by_slot_descs(const std::vector<SlotDescriptor*>* slot_descs, |
103 | 41 | const TupleDescriptor* tuple_desc) { |
104 | 155 | for (auto slot_desc : *slot_descs) { |
105 | 155 | const auto& slots = tuple_desc->slots(); |
106 | 745 | for (int j = 0; j < slots.size(); ++j) { |
107 | 745 | if (slot_desc->id() == slots[j]->id()) { |
108 | 155 | _column_offset.emplace_back(j); |
109 | 155 | break; |
110 | 155 | } |
111 | 745 | } |
112 | 155 | } |
113 | 41 | if (_is_partial_update_and_auto_inc) { |
114 | 0 | _column_offset.emplace_back(_column_offset.size()); |
115 | 0 | } |
116 | 41 | _num_columns = _column_offset.size(); |
117 | 41 | } |
118 | | |
119 | 23 | void MemTable::_init_agg_functions(const Block* block) { |
120 | 23 | if (_num_columns > _column_offset.size()) [[unlikely]] { |
121 | 0 | throw doris::Exception(doris::ErrorCode::INTERNAL_ERROR, |
122 | 0 | "num_columns {} is greater than block columns {}", _num_columns, |
123 | 0 | _column_offset.size()); |
124 | 0 | } |
125 | 64 | for (auto cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { |
126 | 41 | AggregateFunctionPtr function; |
127 | 41 | if (_keys_type == KeysType::UNIQUE_KEYS && _enable_unique_key_mow) { |
128 | | // In such table, non-key column's aggregation type is NONE, so we need to construct |
129 | | // the aggregate function manually. |
130 | 14 | if (_skip_bitmap_col_idx != cid) { |
131 | 14 | function = AggregateFunctionSimpleFactory::instance().get( |
132 | 14 | "replace_load", {block->get_data_type(cid)}, block->get_data_type(cid), |
133 | 14 | block->get_data_type(cid)->is_nullable(), |
134 | 14 | BeExecVersionManager::get_newest_version()); |
135 | 14 | } else { |
136 | 0 | function = AggregateFunctionSimpleFactory::instance().get( |
137 | 0 | "bitmap_intersect", {block->get_data_type(cid)}, block->get_data_type(cid), |
138 | 0 | false, BeExecVersionManager::get_newest_version()); |
139 | 0 | } |
140 | 27 | } else { |
141 | 27 | function = _tablet_schema->column(cid).get_aggregate_function( |
142 | 27 | AGG_LOAD_SUFFIX, _tablet_schema->column(cid).get_be_exec_version(), |
143 | 27 | block->get_data_type(cid)); |
144 | 27 | if (function == nullptr) { |
145 | 0 | LOG(WARNING) << "column get aggregate function failed, column=" |
146 | 0 | << _tablet_schema->column(cid).name(); |
147 | 0 | } |
148 | 27 | } |
149 | | |
150 | 41 | DCHECK(function != nullptr); |
151 | 41 | _agg_functions[cid] = function; |
152 | 41 | } |
153 | | |
154 | 64 | for (auto cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { |
155 | 41 | _offsets_of_aggregate_states[cid] = _total_size_of_aggregate_states; |
156 | 41 | _total_size_of_aggregate_states += _agg_functions[cid]->size_of_data(); |
157 | | |
158 | | // If not the last aggregate_state, we need pad it so that next aggregate_state will be aligned. |
159 | 41 | if (cid + 1 < _num_columns) { |
160 | 24 | size_t alignment_of_next_state = _agg_functions[cid + 1]->align_of_data(); |
161 | | |
162 | | /// Extend total_size to next alignment requirement |
163 | | /// Add padding by rounding up 'total_size_of_aggregate_states' to be a multiplier of alignment_of_next_state. |
164 | 24 | _total_size_of_aggregate_states = |
165 | 24 | (_total_size_of_aggregate_states + alignment_of_next_state - 1) / |
166 | 24 | alignment_of_next_state * alignment_of_next_state; |
167 | 24 | } |
168 | 41 | } |
169 | 23 | } |
170 | | |
171 | 41 | MemTable::~MemTable() { |
172 | 41 | SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( |
173 | 41 | _resource_ctx->memory_context()->mem_tracker()->write_tracker()); |
174 | 41 | { |
175 | 41 | SCOPED_CONSUME_MEM_TRACKER(_mem_tracker); |
176 | 41 | g_memtable_cnt << -1; |
177 | 41 | if (_keys_type != KeysType::DUP_KEYS) { |
178 | 4.16k | for (auto it = _row_in_blocks->begin(); it != _row_in_blocks->end(); it++) { |
179 | 4.13k | if (!(*it)->has_init_agg()) { |
180 | 4.12k | continue; |
181 | 4.12k | } |
182 | | // We should release agg_places here, because they are not released when a |
183 | | // load is canceled. |
184 | 13 | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { |
185 | 7 | auto function = _agg_functions[i]; |
186 | 7 | DCHECK(function != nullptr); |
187 | 7 | function->destroy((*it)->agg_places(i)); |
188 | 7 | } |
189 | 6 | } |
190 | 34 | } |
191 | | |
192 | 41 | _arena.clear(true); |
193 | 41 | _vec_row_comparator.reset(); |
194 | 41 | _row_in_blocks.reset(); |
195 | 41 | _agg_functions.clear(); |
196 | 41 | _input_mutable_block.clear(); |
197 | 41 | _output_mutable_block.clear(); |
198 | 41 | } |
199 | 41 | if (_is_flush_success) { |
200 | | // If the memtable is flush success, then its memtracker's consumption should be 0 |
201 | 14 | if (_mem_tracker->consumption() != 0 && config::crash_in_memory_tracker_inaccurate) { |
202 | 0 | LOG(FATAL) << "memtable flush success but cosumption is not 0, it is " |
203 | 0 | << _mem_tracker->consumption(); |
204 | 0 | } |
205 | 14 | } |
206 | 41 | } |
207 | | |
208 | 18.4k | int RowInBlockComparator::operator()(const RowInBlock* left, const RowInBlock* right) const { |
209 | 18.4k | return _pblock->compare_at(left->_row_pos, right->_row_pos, _tablet_schema->num_key_columns(), |
210 | 18.4k | *_pblock, -1); |
211 | 18.4k | } |
212 | | |
213 | 47 | Status MemTable::insert(const Block* input_block, const DorisVector<uint32_t>& row_idxs) { |
214 | 47 | SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( |
215 | 47 | _resource_ctx->memory_context()->mem_tracker()->write_tracker()); |
216 | 47 | SCOPED_CONSUME_MEM_TRACKER(_mem_tracker); |
217 | | |
218 | 47 | if (_is_first_insertion) { |
219 | 26 | _is_first_insertion = false; |
220 | 26 | auto clone_block = input_block->clone_without_columns(&_column_offset); |
221 | 26 | _input_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block)); |
222 | 26 | _vec_row_comparator->set_block(&_input_mutable_block); |
223 | 26 | clone_block = input_block->clone_without_columns(&_column_offset); |
224 | 26 | _output_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block)); |
225 | 26 | if (_tablet_schema->has_sequence_col()) { |
226 | 7 | if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) { |
227 | | // for unique key fixed partial update, sequence column index in block |
228 | | // may be different with the index in `_tablet_schema` |
229 | 0 | for (int32_t i = 0; i < clone_block.columns(); i++) { |
230 | 0 | if (clone_block.get_by_position(i).name == SEQUENCE_COL) { |
231 | 0 | _seq_col_idx_in_block = i; |
232 | 0 | break; |
233 | 0 | } |
234 | 0 | } |
235 | 7 | } else { |
236 | 7 | _seq_col_idx_in_block = _tablet_schema->sequence_col_idx(); |
237 | 7 | } |
238 | 7 | } |
239 | 26 | if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS && |
240 | 26 | _tablet_schema->has_skip_bitmap_col()) { |
241 | | // init of _skip_bitmap_col_idx and _delete_sign_col_idx must be before _init_agg_functions() |
242 | 0 | _skip_bitmap_col_idx = _tablet_schema->skip_bitmap_col_idx(); |
243 | 0 | _delete_sign_col_idx = _tablet_schema->delete_sign_idx(); |
244 | 0 | _delete_sign_col_unique_id = _tablet_schema->column(_delete_sign_col_idx).unique_id(); |
245 | 0 | if (_seq_col_idx_in_block != -1) { |
246 | 0 | _seq_col_unique_id = _tablet_schema->column(_seq_col_idx_in_block).unique_id(); |
247 | 0 | } |
248 | 0 | } |
249 | 26 | if (_keys_type != KeysType::DUP_KEYS) { |
250 | | // there may be additional intermediate columns in input_block |
251 | | // we only need columns indicated by column offset in the output |
252 | 23 | RETURN_IF_CATCH_EXCEPTION(_init_agg_functions(&clone_block)); |
253 | 23 | } |
254 | 26 | } |
255 | | |
256 | 47 | auto num_rows = row_idxs.size(); |
257 | 47 | size_t cursor_in_mutableblock = _input_mutable_block.rows(); |
258 | 47 | RETURN_IF_ERROR(_input_mutable_block.add_rows(input_block, row_idxs.data(), |
259 | 47 | row_idxs.data() + num_rows, &_column_offset)); |
260 | 47 | DBUG_EXECUTE_IF("MemTable.insert.row_batch_allocation", DBUG_RUN_CALLBACK(this)); |
261 | 47 | auto row_batch = make_row_batch( |
262 | 10.2k | num_rows, [&](size_t i) { return RowInBlock(cursor_in_mutableblock + i); }); |
263 | 10.3k | for (size_t i = 0; i < num_rows; ++i) { |
264 | 10.2k | _row_in_blocks->emplace_back(row_batch, &row_batch[i]); |
265 | 10.2k | } |
266 | | |
267 | 47 | _stat.raw_rows += num_rows; |
268 | 47 | return Status::OK(); |
269 | 47 | } |
270 | | |
271 | | template <bool has_skip_bitmap_col> |
272 | | void MemTable::_aggregate_two_row_in_block(MutableBlock& mutable_block, RowInBlock* src_row, |
273 | 5.12k | RowInBlock* dst_row) { |
274 | | // for flexible partial update, the caller must guarantees that either src_row and dst_row |
275 | | // both specify the sequence column, or src_row and dst_row both don't specify the |
276 | | // sequence column |
277 | 5.12k | if (_tablet_schema->has_sequence_col() && _seq_col_idx_in_block >= 0) { |
278 | 3 | DCHECK_LT(_seq_col_idx_in_block, mutable_block.columns()); |
279 | 3 | auto col_ptr = mutable_block.mutable_columns()[_seq_col_idx_in_block].get(); |
280 | 3 | auto res = col_ptr->compare_at(dst_row->_row_pos, src_row->_row_pos, *col_ptr, -1); |
281 | | // dst sequence column larger than src, don't need to update |
282 | 3 | if (res > 0) { |
283 | 3 | return; |
284 | 3 | } |
285 | | // need to update the row pos in dst row to the src row pos when has |
286 | | // sequence column |
287 | 0 | dst_row->_row_pos = src_row->_row_pos; |
288 | 0 | } |
289 | | // dst is non-sequence row, or dst sequence is smaller |
290 | 5.12k | if constexpr (!has_skip_bitmap_col) { |
291 | 5.12k | DCHECK(_skip_bitmap_col_idx == -1); |
292 | 10.2k | for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { |
293 | 5.12k | auto* col_ptr = mutable_block.mutable_columns()[cid].get(); |
294 | 5.12k | _agg_functions[cid]->add(dst_row->agg_places(cid), |
295 | 5.12k | const_cast<const doris::IColumn**>(&col_ptr), |
296 | 5.12k | src_row->_row_pos, _arena); |
297 | 5.12k | } |
298 | 5.12k | } else { |
299 | 0 | DCHECK(_skip_bitmap_col_idx != -1); |
300 | 0 | DCHECK_LT(_skip_bitmap_col_idx, mutable_block.columns()); |
301 | 0 | const BitmapValue& skip_bitmap = |
302 | 0 | assert_cast<ColumnBitmap*, TypeCheckOnRelease::DISABLE>( |
303 | 0 | mutable_block.mutable_columns()[_skip_bitmap_col_idx].get()) |
304 | 0 | ->get_data()[src_row->_row_pos]; |
305 | 0 | for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { |
306 | 0 | const auto& col = _tablet_schema->column(cid); |
307 | 0 | if (cid != _skip_bitmap_col_idx && skip_bitmap.contains(col.unique_id())) { |
308 | 0 | continue; |
309 | 0 | } |
310 | 0 | auto* col_ptr = mutable_block.mutable_columns()[cid].get(); |
311 | 0 | _agg_functions[cid]->add(dst_row->agg_places(cid), |
312 | 0 | const_cast<const doris::IColumn**>(&col_ptr), |
313 | 0 | src_row->_row_pos, _arena); |
314 | 0 | } |
315 | 0 | } |
316 | 5.12k | } _ZN5doris8MemTable27_aggregate_two_row_in_blockILb0EEEvRNS_12MutableBlockEPNS_10RowInBlockES5_ Line | Count | Source | 273 | 5.12k | RowInBlock* dst_row) { | 274 | | // for flexible partial update, the caller must guarantees that either src_row and dst_row | 275 | | // both specify the sequence column, or src_row and dst_row both don't specify the | 276 | | // sequence column | 277 | 5.12k | if (_tablet_schema->has_sequence_col() && _seq_col_idx_in_block >= 0) { | 278 | 3 | DCHECK_LT(_seq_col_idx_in_block, mutable_block.columns()); | 279 | 3 | auto col_ptr = mutable_block.mutable_columns()[_seq_col_idx_in_block].get(); | 280 | 3 | auto res = col_ptr->compare_at(dst_row->_row_pos, src_row->_row_pos, *col_ptr, -1); | 281 | | // dst sequence column larger than src, don't need to update | 282 | 3 | if (res > 0) { | 283 | 3 | return; | 284 | 3 | } | 285 | | // need to update the row pos in dst row to the src row pos when has | 286 | | // sequence column | 287 | 0 | dst_row->_row_pos = src_row->_row_pos; | 288 | 0 | } | 289 | | // dst is non-sequence row, or dst sequence is smaller | 290 | 5.12k | if constexpr (!has_skip_bitmap_col) { | 291 | 5.12k | DCHECK(_skip_bitmap_col_idx == -1); | 292 | 10.2k | for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { | 293 | 5.12k | auto* col_ptr = mutable_block.mutable_columns()[cid].get(); | 294 | 5.12k | _agg_functions[cid]->add(dst_row->agg_places(cid), | 295 | 5.12k | const_cast<const doris::IColumn**>(&col_ptr), | 296 | 5.12k | src_row->_row_pos, _arena); | 297 | 5.12k | } | 298 | | } else { | 299 | | DCHECK(_skip_bitmap_col_idx != -1); | 300 | | DCHECK_LT(_skip_bitmap_col_idx, mutable_block.columns()); | 301 | | const BitmapValue& skip_bitmap = | 302 | | assert_cast<ColumnBitmap*, TypeCheckOnRelease::DISABLE>( | 303 | | mutable_block.mutable_columns()[_skip_bitmap_col_idx].get()) | 304 | | ->get_data()[src_row->_row_pos]; | 305 | | for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) { | 306 | | const auto& col = _tablet_schema->column(cid); | 307 | | if (cid != _skip_bitmap_col_idx && skip_bitmap.contains(col.unique_id())) { | 308 | | continue; | 309 | | } | 310 | | auto* col_ptr = mutable_block.mutable_columns()[cid].get(); | 311 | | _agg_functions[cid]->add(dst_row->agg_places(cid), | 312 | | const_cast<const doris::IColumn**>(&col_ptr), | 313 | | src_row->_row_pos, _arena); | 314 | | } | 315 | | } | 316 | 5.12k | } |
Unexecuted instantiation: _ZN5doris8MemTable27_aggregate_two_row_in_blockILb1EEEvRNS_12MutableBlockEPNS_10RowInBlockES5_ |
317 | 11 | Status MemTable::_put_into_output(Block& in_block) { |
318 | 11 | SCOPED_RAW_TIMER(&_stat.put_into_output_ns); |
319 | 11 | DorisVector<uint32_t> row_pos_vec; |
320 | 11 | DCHECK(in_block.rows() <= std::numeric_limits<int>::max()); |
321 | 11 | row_pos_vec.reserve(in_block.rows()); |
322 | 1.04k | for (int i = 0; i < _row_in_blocks->size(); i++) { |
323 | 1.03k | row_pos_vec.emplace_back((*_row_in_blocks)[i]->_row_pos); |
324 | 1.03k | } |
325 | 11 | return _output_mutable_block.add_rows(&in_block, row_pos_vec.data(), |
326 | 11 | row_pos_vec.data() + in_block.rows()); |
327 | 11 | } |
328 | | |
329 | 23 | size_t MemTable::_sort() { |
330 | 23 | SCOPED_RAW_TIMER(&_stat.sort_ns); |
331 | 23 | _stat.sort_times++; |
332 | 23 | size_t same_keys_num = 0; |
333 | | // sort new rows |
334 | 23 | Tie tie = Tie(_last_sorted_pos, _row_in_blocks->size()); |
335 | 72 | for (size_t i = 0; i < _tablet_schema->num_key_columns(); i++) { |
336 | 48.9k | auto cmp = [&](RowInBlock* lhs, RowInBlock* rhs) -> int { |
337 | 48.9k | return _input_mutable_block.compare_one_column(lhs->_row_pos, rhs->_row_pos, i, -1); |
338 | 48.9k | }; |
339 | 49 | _sort_one_column(*_row_in_blocks, tie, cmp); |
340 | 49 | } |
341 | 23 | bool is_dup = (_keys_type == KeysType::DUP_KEYS); |
342 | | // sort extra round by _row_pos to make the sort stable |
343 | 23 | auto iter = tie.iter(); |
344 | 1.05k | while (iter.next()) { |
345 | 1.03k | pdqsort(std::next(_row_in_blocks->begin(), iter.left()), |
346 | 1.03k | std::next(_row_in_blocks->begin(), iter.right()), |
347 | 1.03k | [&is_dup](const std::shared_ptr<RowInBlock>& lhs, |
348 | 1.03k | const std::shared_ptr<RowInBlock>& rhs) -> bool { |
349 | 1.03k | return is_dup ? lhs->_row_pos > rhs->_row_pos : lhs->_row_pos < rhs->_row_pos; |
350 | 1.03k | }); |
351 | 1.03k | same_keys_num += iter.right() - iter.left(); |
352 | 1.03k | } |
353 | | // merge new rows and old rows |
354 | 23 | _vec_row_comparator->set_block(&_input_mutable_block); |
355 | 23 | auto cmp_func = [this, is_dup, &same_keys_num](const std::shared_ptr<RowInBlock>& l, |
356 | 8.18k | const std::shared_ptr<RowInBlock>& r) -> bool { |
357 | 8.18k | auto value = (*(this->_vec_row_comparator))(l.get(), r.get()); |
358 | 8.18k | if (value == 0) { |
359 | 4.09k | same_keys_num++; |
360 | 4.09k | return is_dup ? l->_row_pos > r->_row_pos : l->_row_pos < r->_row_pos; |
361 | 4.09k | } else { |
362 | 4.09k | return value < 0; |
363 | 4.09k | } |
364 | 8.18k | }; |
365 | 23 | auto new_row_it = std::next(_row_in_blocks->begin(), _last_sorted_pos); |
366 | 23 | std::inplace_merge(_row_in_blocks->begin(), new_row_it, _row_in_blocks->end(), cmp_func); |
367 | 23 | _last_sorted_pos = _row_in_blocks->size(); |
368 | 23 | return same_keys_num; |
369 | 23 | } |
370 | | |
371 | 5 | Status MemTable::_sort_by_cluster_keys() { |
372 | 5 | SCOPED_RAW_TIMER(&_stat.sort_ns); |
373 | 5 | _stat.sort_times++; |
374 | | // sort all rows |
375 | 5 | Block in_block = _output_mutable_block.to_block(); |
376 | 5 | auto clone_block = in_block.clone_without_columns(); |
377 | 5 | MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block)); |
378 | 5 | _output_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block)); |
379 | | |
380 | 5 | DorisVector<std::shared_ptr<RowInBlock>> row_in_blocks; |
381 | 5 | row_in_blocks.reserve(mutable_block.rows()); |
382 | 3.07k | auto rows = make_row_batch(mutable_block.rows(), [&](size_t i) { return RowInBlock(i); }); |
383 | 3.08k | for (size_t i = 0; i < mutable_block.rows(); ++i) { |
384 | 3.07k | row_in_blocks.emplace_back(rows, &rows[i]); |
385 | 3.07k | } |
386 | 5 | Tie tie = Tie(0, mutable_block.rows()); |
387 | | |
388 | 6 | for (auto cid : _tablet_schema->cluster_key_uids()) { |
389 | 6 | auto index = _tablet_schema->field_index(cid); |
390 | 6 | if (index == -1) { |
391 | 1 | return Status::InternalError("could not find cluster key column with unique_id=" + |
392 | 1 | std::to_string(cid) + " in tablet schema"); |
393 | 1 | } |
394 | 8.26k | auto cmp = [&](const RowInBlock* lhs, const RowInBlock* rhs) -> int { |
395 | 8.26k | return mutable_block.compare_one_column(lhs->_row_pos, rhs->_row_pos, index, -1); |
396 | 8.26k | }; |
397 | 5 | _sort_one_column(row_in_blocks, tie, cmp); |
398 | 5 | } |
399 | | |
400 | | // sort extra round by _row_pos to make the sort stable |
401 | 4 | auto iter = tie.iter(); |
402 | 4 | while (iter.next()) { |
403 | 0 | pdqsort(std::next(row_in_blocks.begin(), iter.left()), |
404 | 0 | std::next(row_in_blocks.begin(), iter.right()), |
405 | 0 | [](const std::shared_ptr<RowInBlock>& lhs, const std::shared_ptr<RowInBlock>& rhs) |
406 | 0 | -> bool { return lhs->_row_pos < rhs->_row_pos; }); |
407 | 0 | } |
408 | | |
409 | 4 | in_block = mutable_block.to_block(); |
410 | 4 | SCOPED_RAW_TIMER(&_stat.put_into_output_ns); |
411 | 4 | DorisVector<uint32_t> row_pos_vec; |
412 | 4 | DCHECK(in_block.rows() <= std::numeric_limits<int>::max()); |
413 | 4 | row_pos_vec.reserve(in_block.rows()); |
414 | 2.05k | for (int i = 0; i < row_in_blocks.size(); i++) { |
415 | 2.05k | row_pos_vec.emplace_back(row_in_blocks[i]->_row_pos); |
416 | 2.05k | } |
417 | 4 | std::vector<int> column_offset; |
418 | 18 | for (int i = 0; i < _column_offset.size(); ++i) { |
419 | 14 | column_offset.emplace_back(i); |
420 | 14 | } |
421 | 4 | return _output_mutable_block.add_rows(&in_block, row_pos_vec.data(), |
422 | 4 | row_pos_vec.data() + in_block.rows(), &column_offset); |
423 | 5 | } |
424 | | |
425 | | void MemTable::_sort_one_column(DorisVector<std::shared_ptr<RowInBlock>>& row_in_blocks, Tie& tie, |
426 | 54 | std::function<int(RowInBlock*, RowInBlock*)> cmp) { |
427 | 54 | auto iter = tie.iter(); |
428 | 1.10k | while (iter.next()) { |
429 | 1.04k | pdqsort(std::next(row_in_blocks.begin(), static_cast<int>(iter.left())), |
430 | 1.04k | std::next(row_in_blocks.begin(), static_cast<int>(iter.right())), |
431 | 47.9k | [&cmp](auto lhs, auto rhs) -> bool { return cmp(lhs.get(), rhs.get()) < 0; }); |
432 | 1.04k | tie[iter.left()] = 0; |
433 | 10.2k | for (auto i = iter.left() + 1; i < iter.right(); i++) { |
434 | 9.23k | tie[i] = (cmp(row_in_blocks[i - 1].get(), row_in_blocks[i].get()) == 0); |
435 | 9.23k | } |
436 | 1.04k | } |
437 | 54 | } |
438 | | |
439 | | template <bool is_final> |
440 | 5.13k | void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) { |
441 | | // move key columns |
442 | 15.3k | for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) { |
443 | 10.2k | _output_mutable_block.get_column_by_position(i)->insert_from( |
444 | 10.2k | *mutable_block.get_column_by_position(i), row->_row_pos); |
445 | 10.2k | } |
446 | 5.13k | if (row->has_init_agg()) { |
447 | | // get value columns from agg_places |
448 | 10.2k | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { |
449 | 5.13k | auto function = _agg_functions[i]; |
450 | 5.13k | auto* agg_place = row->agg_places(i); |
451 | 5.13k | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); |
452 | 5.13k | function->insert_result_into(agg_place, *col_ptr); |
453 | | |
454 | 5.13k | if constexpr (is_final) { |
455 | 1.03k | function->destroy(agg_place); |
456 | 4.10k | } else { |
457 | 4.10k | function->reset(agg_place); |
458 | 4.10k | } |
459 | 5.13k | } |
460 | | |
461 | 5.12k | if constexpr (is_final) { |
462 | 1.02k | row->remove_init_agg(); |
463 | 4.10k | } else { |
464 | 8.20k | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { |
465 | 4.10k | auto function = _agg_functions[i]; |
466 | 4.10k | auto* agg_place = row->agg_places(i); |
467 | 4.10k | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); |
468 | 4.10k | function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos, |
469 | 4.10k | _arena); |
470 | 4.10k | } |
471 | 4.10k | } |
472 | 5.12k | } else { |
473 | | // move columns for rows do not need agg |
474 | 12 | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { |
475 | 9 | _output_mutable_block.get_column_by_position(i)->insert_from( |
476 | 9 | *mutable_block.get_column_by_position(i), row->_row_pos); |
477 | 9 | } |
478 | 3 | } |
479 | 5.13k | if constexpr (!is_final) { |
480 | 4.10k | row->_row_pos = row_pos; |
481 | 4.10k | } |
482 | 5.13k | } _ZN5doris8MemTable17_finalize_one_rowILb0EEEvPNS_10RowInBlockERNS_12MutableBlockEi Line | Count | Source | 440 | 4.10k | void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) { | 441 | | // move key columns | 442 | 12.2k | for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) { | 443 | 8.19k | _output_mutable_block.get_column_by_position(i)->insert_from( | 444 | 8.19k | *mutable_block.get_column_by_position(i), row->_row_pos); | 445 | 8.19k | } | 446 | 4.10k | if (row->has_init_agg()) { | 447 | | // get value columns from agg_places | 448 | 8.20k | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 449 | 4.10k | auto function = _agg_functions[i]; | 450 | 4.10k | auto* agg_place = row->agg_places(i); | 451 | 4.10k | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); | 452 | 4.10k | function->insert_result_into(agg_place, *col_ptr); | 453 | | | 454 | | if constexpr (is_final) { | 455 | | function->destroy(agg_place); | 456 | 4.10k | } else { | 457 | 4.10k | function->reset(agg_place); | 458 | 4.10k | } | 459 | 4.10k | } | 460 | | | 461 | | if constexpr (is_final) { | 462 | | row->remove_init_agg(); | 463 | 4.10k | } else { | 464 | 8.20k | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 465 | 4.10k | auto function = _agg_functions[i]; | 466 | 4.10k | auto* agg_place = row->agg_places(i); | 467 | 4.10k | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); | 468 | 4.10k | function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos, | 469 | 4.10k | _arena); | 470 | 4.10k | } | 471 | 4.10k | } | 472 | 4.10k | } else { | 473 | | // move columns for rows do not need agg | 474 | 0 | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 475 | 0 | _output_mutable_block.get_column_by_position(i)->insert_from( | 476 | 0 | *mutable_block.get_column_by_position(i), row->_row_pos); | 477 | 0 | } | 478 | 0 | } | 479 | 4.10k | if constexpr (!is_final) { | 480 | 4.10k | row->_row_pos = row_pos; | 481 | 4.10k | } | 482 | 4.10k | } |
_ZN5doris8MemTable17_finalize_one_rowILb1EEEvPNS_10RowInBlockERNS_12MutableBlockEi Line | Count | Source | 440 | 1.03k | void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) { | 441 | | // move key columns | 442 | 3.09k | for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) { | 443 | 2.06k | _output_mutable_block.get_column_by_position(i)->insert_from( | 444 | 2.06k | *mutable_block.get_column_by_position(i), row->_row_pos); | 445 | 2.06k | } | 446 | 1.03k | if (row->has_init_agg()) { | 447 | | // get value columns from agg_places | 448 | 2.06k | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 449 | 1.03k | auto function = _agg_functions[i]; | 450 | 1.03k | auto* agg_place = row->agg_places(i); | 451 | 1.03k | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); | 452 | 1.03k | function->insert_result_into(agg_place, *col_ptr); | 453 | | | 454 | 1.03k | if constexpr (is_final) { | 455 | 1.03k | function->destroy(agg_place); | 456 | | } else { | 457 | | function->reset(agg_place); | 458 | | } | 459 | 1.03k | } | 460 | | | 461 | 1.02k | if constexpr (is_final) { | 462 | 1.02k | row->remove_init_agg(); | 463 | | } else { | 464 | | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 465 | | auto function = _agg_functions[i]; | 466 | | auto* agg_place = row->agg_places(i); | 467 | | auto* col_ptr = _output_mutable_block.get_column_by_position(i).get(); | 468 | | function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos, | 469 | | _arena); | 470 | | } | 471 | | } | 472 | 1.02k | } else { | 473 | | // move columns for rows do not need agg | 474 | 12 | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { | 475 | 9 | _output_mutable_block.get_column_by_position(i)->insert_from( | 476 | 9 | *mutable_block.get_column_by_position(i), row->_row_pos); | 477 | 9 | } | 478 | 3 | } | 479 | | if constexpr (!is_final) { | 480 | | row->_row_pos = row_pos; | 481 | | } | 482 | 1.03k | } |
|
483 | | |
484 | 1.03k | void MemTable::_init_row_for_agg(RowInBlock* row, MutableBlock& mutable_block) { |
485 | 1.03k | auto* agg_mem = _arena.aligned_alloc(_total_size_of_aggregate_states, 16); |
486 | 1.03k | auto first_value_column = _tablet_schema->num_key_columns(); |
487 | 1.03k | auto cid = first_value_column; |
488 | 1.03k | try { |
489 | 2.07k | for (; cid < _num_columns; ++cid) { |
490 | 1.04k | _agg_functions[cid]->create(agg_mem + _offsets_of_aggregate_states[cid]); |
491 | 1.04k | } |
492 | 1.03k | } catch (...) { |
493 | | // A failed constructor leaves only the completed prefix available for destruction. |
494 | 2 | while (cid > first_value_column) { |
495 | 1 | --cid; |
496 | 1 | _agg_functions[cid]->destroy(agg_mem + _offsets_of_aggregate_states[cid]); |
497 | 1 | } |
498 | 1 | throw; |
499 | 1 | } |
500 | | // add() can allocate. Publish the row only after all states can be safely destroyed. |
501 | 1.03k | row->init_agg_places(agg_mem, _offsets_of_aggregate_states.data()); |
502 | 2.07k | for (cid = first_value_column; cid < _num_columns; ++cid) { |
503 | 1.03k | auto* col_ptr = mutable_block.mutable_columns()[cid].get(); |
504 | 1.03k | auto* data = row->agg_places(cid); |
505 | 1.03k | _agg_functions[cid]->add(data, const_cast<const doris::IColumn**>(&col_ptr), row->_row_pos, |
506 | 1.03k | _arena); |
507 | 1.03k | } |
508 | 1.03k | } |
509 | 5.12k | void MemTable::_clear_row_agg(RowInBlock* row) { |
510 | 5.12k | if (row->has_init_agg()) { |
511 | 0 | for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) { |
512 | 0 | auto function = _agg_functions[i]; |
513 | 0 | auto* agg_place = row->agg_places(i); |
514 | 0 | function->destroy(agg_place); |
515 | 0 | } |
516 | 0 | row->remove_init_agg(); |
517 | 0 | } |
518 | 5.12k | } |
519 | | // only in `to_block` the `is_final` flag will be true, in other cases, it will be false |
520 | | template <bool is_final, bool has_skip_bitmap_col> |
521 | 13 | void MemTable::_aggregate() { |
522 | 13 | SCOPED_RAW_TIMER(&_stat.agg_ns); |
523 | 13 | _stat.agg_times++; |
524 | 13 | Block in_block = _input_mutable_block.to_block(); |
525 | 13 | std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0); |
526 | 13 | MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block)); |
527 | 13 | _vec_row_comparator->set_block(&mutable_block); |
528 | 13 | DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks; |
529 | 13 | temp_row_in_blocks.reserve(_last_sorted_pos); |
530 | | //only init agg if needed |
531 | | |
532 | 13 | if constexpr (!has_skip_bitmap_col) { |
533 | 13 | RowInBlock* prev_row = nullptr; |
534 | 13 | int row_pos = -1; |
535 | 10.2k | for (const auto& cur_row_ptr : *_row_in_blocks) { |
536 | 10.2k | RowInBlock* cur_row = cur_row_ptr.get(); |
537 | 10.2k | if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) { |
538 | 5.13k | if (!prev_row->has_init_agg()) { |
539 | 1.03k | _init_row_for_agg(prev_row, mutable_block); |
540 | 1.03k | } |
541 | 5.13k | _stat.merged_rows++; |
542 | 5.13k | _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row); |
543 | | // Clean up aggregation state of the merged row to avoid memory leak |
544 | 5.13k | _clear_row_agg(cur_row); |
545 | 5.13k | } else { |
546 | 5.13k | prev_row = cur_row; |
547 | 5.13k | if (!temp_row_in_blocks.empty()) { |
548 | | // The rows from the previous batch of _row_in_blocks have been merged into |
549 | | // temp_row_in_blocks, now finalize into the mutable block. |
550 | 5.12k | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, |
551 | 5.12k | row_pos); |
552 | 5.12k | } |
553 | 5.13k | temp_row_in_blocks.push_back(cur_row_ptr); |
554 | 5.13k | row_pos++; |
555 | 5.13k | } |
556 | 10.2k | } |
557 | 13 | if (!temp_row_in_blocks.empty()) { |
558 | | // finalize the last low |
559 | 11 | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos); |
560 | 11 | } |
561 | 13 | } else { |
562 | 0 | DCHECK(_delete_sign_col_idx != -1); |
563 | 0 | if (_seq_col_idx_in_block == -1) { |
564 | 0 | _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block, |
565 | 0 | temp_row_in_blocks); |
566 | 0 | } else { |
567 | 0 | _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block, |
568 | 0 | temp_row_in_blocks); |
569 | 0 | } |
570 | 0 | } |
571 | 13 | if constexpr (!is_final) { |
572 | | // if is not final, we collect the agg results to input_block and then continue to insert |
573 | 9 | _input_mutable_block.swap(_output_mutable_block); |
574 | | //TODO(weixang):opt here. |
575 | 9 | _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block)); |
576 | 9 | _output_mutable_block.clear_column_data(); |
577 | | // Repack surviving rows so they do not retain batches of merged-away rows. |
578 | | // Aggregation states belong to the memtable; preserve their pointers and flags. |
579 | 9 | DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this)); |
580 | 9 | auto rows = make_row_batch(temp_row_in_blocks.size(), |
581 | 4.09k | [&](size_t i) { return *temp_row_in_blocks[i]; });_ZZN5doris8MemTable10_aggregateILb0ELb0EEEvvENKUlmE_clEm Line | Count | Source | 581 | 4.09k | [&](size_t i) { return *temp_row_in_blocks[i]; }); |
Unexecuted instantiation: _ZZN5doris8MemTable10_aggregateILb0ELb1EEEvvENKUlmE_clEm |
582 | 4.10k | for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) { |
583 | 4.09k | temp_row_in_blocks[i] = {rows, &rows[i]}; |
584 | 4.09k | } |
585 | 9 | *_row_in_blocks = temp_row_in_blocks; |
586 | 9 | _last_sorted_pos = _row_in_blocks->size(); |
587 | 9 | } |
588 | 13 | } _ZN5doris8MemTable10_aggregateILb0ELb0EEEvv Line | Count | Source | 521 | 9 | void MemTable::_aggregate() { | 522 | 9 | SCOPED_RAW_TIMER(&_stat.agg_ns); | 523 | 9 | _stat.agg_times++; | 524 | 9 | Block in_block = _input_mutable_block.to_block(); | 525 | 9 | std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0); | 526 | 9 | MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block)); | 527 | 9 | _vec_row_comparator->set_block(&mutable_block); | 528 | 9 | DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks; | 529 | 9 | temp_row_in_blocks.reserve(_last_sorted_pos); | 530 | | //only init agg if needed | 531 | | | 532 | 9 | if constexpr (!has_skip_bitmap_col) { | 533 | 9 | RowInBlock* prev_row = nullptr; | 534 | 9 | int row_pos = -1; | 535 | 8.20k | for (const auto& cur_row_ptr : *_row_in_blocks) { | 536 | 8.20k | RowInBlock* cur_row = cur_row_ptr.get(); | 537 | 8.20k | if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) { | 538 | 4.10k | if (!prev_row->has_init_agg()) { | 539 | 1.03k | _init_row_for_agg(prev_row, mutable_block); | 540 | 1.03k | } | 541 | 4.10k | _stat.merged_rows++; | 542 | 4.10k | _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row); | 543 | | // Clean up aggregation state of the merged row to avoid memory leak | 544 | 4.10k | _clear_row_agg(cur_row); | 545 | 4.10k | } else { | 546 | 4.10k | prev_row = cur_row; | 547 | 4.10k | if (!temp_row_in_blocks.empty()) { | 548 | | // The rows from the previous batch of _row_in_blocks have been merged into | 549 | | // temp_row_in_blocks, now finalize into the mutable block. | 550 | 4.09k | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, | 551 | 4.09k | row_pos); | 552 | 4.09k | } | 553 | 4.10k | temp_row_in_blocks.push_back(cur_row_ptr); | 554 | 4.10k | row_pos++; | 555 | 4.10k | } | 556 | 8.20k | } | 557 | 9 | if (!temp_row_in_blocks.empty()) { | 558 | | // finalize the last low | 559 | 7 | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos); | 560 | 7 | } | 561 | | } else { | 562 | | DCHECK(_delete_sign_col_idx != -1); | 563 | | if (_seq_col_idx_in_block == -1) { | 564 | | _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block, | 565 | | temp_row_in_blocks); | 566 | | } else { | 567 | | _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block, | 568 | | temp_row_in_blocks); | 569 | | } | 570 | | } | 571 | 9 | if constexpr (!is_final) { | 572 | | // if is not final, we collect the agg results to input_block and then continue to insert | 573 | 9 | _input_mutable_block.swap(_output_mutable_block); | 574 | | //TODO(weixang):opt here. | 575 | 9 | _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block)); | 576 | 9 | _output_mutable_block.clear_column_data(); | 577 | | // Repack surviving rows so they do not retain batches of merged-away rows. | 578 | | // Aggregation states belong to the memtable; preserve their pointers and flags. | 579 | 9 | DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this)); | 580 | 9 | auto rows = make_row_batch(temp_row_in_blocks.size(), | 581 | 9 | [&](size_t i) { return *temp_row_in_blocks[i]; }); | 582 | 4.10k | for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) { | 583 | 4.09k | temp_row_in_blocks[i] = {rows, &rows[i]}; | 584 | 4.09k | } | 585 | 9 | *_row_in_blocks = temp_row_in_blocks; | 586 | 9 | _last_sorted_pos = _row_in_blocks->size(); | 587 | 9 | } | 588 | 9 | } |
Unexecuted instantiation: _ZN5doris8MemTable10_aggregateILb0ELb1EEEvv _ZN5doris8MemTable10_aggregateILb1ELb0EEEvv Line | Count | Source | 521 | 4 | void MemTable::_aggregate() { | 522 | 4 | SCOPED_RAW_TIMER(&_stat.agg_ns); | 523 | 4 | _stat.agg_times++; | 524 | 4 | Block in_block = _input_mutable_block.to_block(); | 525 | 4 | std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0); | 526 | 4 | MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block)); | 527 | 4 | _vec_row_comparator->set_block(&mutable_block); | 528 | 4 | DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks; | 529 | 4 | temp_row_in_blocks.reserve(_last_sorted_pos); | 530 | | //only init agg if needed | 531 | | | 532 | 4 | if constexpr (!has_skip_bitmap_col) { | 533 | 4 | RowInBlock* prev_row = nullptr; | 534 | 4 | int row_pos = -1; | 535 | 2.05k | for (const auto& cur_row_ptr : *_row_in_blocks) { | 536 | 2.05k | RowInBlock* cur_row = cur_row_ptr.get(); | 537 | 2.05k | if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) { | 538 | 1.02k | if (!prev_row->has_init_agg()) { | 539 | 3 | _init_row_for_agg(prev_row, mutable_block); | 540 | 3 | } | 541 | 1.02k | _stat.merged_rows++; | 542 | 1.02k | _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row); | 543 | | // Clean up aggregation state of the merged row to avoid memory leak | 544 | 1.02k | _clear_row_agg(cur_row); | 545 | 1.03k | } else { | 546 | 1.03k | prev_row = cur_row; | 547 | 1.03k | if (!temp_row_in_blocks.empty()) { | 548 | | // The rows from the previous batch of _row_in_blocks have been merged into | 549 | | // temp_row_in_blocks, now finalize into the mutable block. | 550 | 1.02k | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, | 551 | 1.02k | row_pos); | 552 | 1.02k | } | 553 | 1.03k | temp_row_in_blocks.push_back(cur_row_ptr); | 554 | 1.03k | row_pos++; | 555 | 1.03k | } | 556 | 2.05k | } | 557 | 4 | if (!temp_row_in_blocks.empty()) { | 558 | | // finalize the last low | 559 | 4 | _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos); | 560 | 4 | } | 561 | | } else { | 562 | | DCHECK(_delete_sign_col_idx != -1); | 563 | | if (_seq_col_idx_in_block == -1) { | 564 | | _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block, | 565 | | temp_row_in_blocks); | 566 | | } else { | 567 | | _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block, | 568 | | temp_row_in_blocks); | 569 | | } | 570 | | } | 571 | | if constexpr (!is_final) { | 572 | | // if is not final, we collect the agg results to input_block and then continue to insert | 573 | | _input_mutable_block.swap(_output_mutable_block); | 574 | | //TODO(weixang):opt here. | 575 | | _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block)); | 576 | | _output_mutable_block.clear_column_data(); | 577 | | // Repack surviving rows so they do not retain batches of merged-away rows. | 578 | | // Aggregation states belong to the memtable; preserve their pointers and flags. | 579 | | DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this)); | 580 | | auto rows = make_row_batch(temp_row_in_blocks.size(), | 581 | | [&](size_t i) { return *temp_row_in_blocks[i]; }); | 582 | | for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) { | 583 | | temp_row_in_blocks[i] = {rows, &rows[i]}; | 584 | | } | 585 | | *_row_in_blocks = temp_row_in_blocks; | 586 | | _last_sorted_pos = _row_in_blocks->size(); | 587 | | } | 588 | 4 | } |
Unexecuted instantiation: _ZN5doris8MemTable10_aggregateILb1ELb1EEEvv |
589 | | |
590 | | template <bool is_final> |
591 | | void MemTable::_aggregate_for_flexible_partial_update_without_seq_col( |
592 | 0 | MutableBlock& mutable_block, DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks) { |
593 | 0 | std::shared_ptr<RowInBlock> prev_row {nullptr}; |
594 | 0 | int row_pos = -1; |
595 | 0 | auto& skip_bitmaps = |
596 | 0 | assert_cast<ColumnBitmap*>(mutable_block.mutable_columns()[_skip_bitmap_col_idx].get()) |
597 | 0 | ->get_data(); |
598 | 0 | auto& delete_signs = |
599 | 0 | assert_cast<ColumnInt8*>(mutable_block.mutable_columns()[_delete_sign_col_idx].get()) |
600 | 0 | ->get_data(); |
601 | 0 | std::shared_ptr<RowInBlock> row_with_delete_sign {nullptr}; |
602 | 0 | std::shared_ptr<RowInBlock> row_without_delete_sign {nullptr}; |
603 | |
|
604 | 0 | auto finalize_rows = [&]() { |
605 | 0 | if (row_with_delete_sign != nullptr) { |
606 | 0 | temp_row_in_blocks.push_back(row_with_delete_sign); |
607 | 0 | _finalize_one_row<is_final>(row_with_delete_sign.get(), mutable_block, ++row_pos); |
608 | 0 | row_with_delete_sign = nullptr; |
609 | 0 | } |
610 | 0 | if (row_without_delete_sign != nullptr) { |
611 | 0 | temp_row_in_blocks.push_back(row_without_delete_sign); |
612 | 0 | _finalize_one_row<is_final>(row_without_delete_sign.get(), mutable_block, ++row_pos); |
613 | 0 | row_without_delete_sign = nullptr; |
614 | 0 | } |
615 | | // _arena.clear(); |
616 | 0 | }; Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlvE_clEv Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlvE_clEv |
617 | |
|
618 | 0 | auto add_row = [&](std::shared_ptr<RowInBlock> row, bool with_delete_sign) { |
619 | 0 | if (with_delete_sign) { |
620 | 0 | row_with_delete_sign = std::move(row); |
621 | 0 | } else { |
622 | 0 | row_without_delete_sign = std::move(row); |
623 | 0 | } |
624 | 0 | }; Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlS7_bE_clES7_b Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlS7_bE_clES7_b |
625 | 0 | for (const auto& cur_row_ptr : *_row_in_blocks) { |
626 | 0 | RowInBlock* cur_row = cur_row_ptr.get(); |
627 | 0 | const BitmapValue& skip_bitmap = skip_bitmaps[cur_row->_row_pos]; |
628 | 0 | bool cur_row_has_delete_sign = (!skip_bitmap.contains(_delete_sign_col_unique_id) && |
629 | 0 | delete_signs[cur_row->_row_pos] != 0); |
630 | 0 | prev_row = |
631 | 0 | (row_with_delete_sign == nullptr) ? row_without_delete_sign : row_with_delete_sign; |
632 | | // compare keys, the keys of row_with_delete_sign and row_without_delete_sign is the same, |
633 | | // choose any of them if it's valid |
634 | 0 | if (prev_row != nullptr && (*_vec_row_comparator)(prev_row.get(), cur_row) == 0) { |
635 | 0 | if (cur_row_has_delete_sign) { |
636 | 0 | if (row_without_delete_sign != nullptr) { |
637 | | // if there exits row without delete sign, remove it first |
638 | 0 | _clear_row_agg(row_without_delete_sign.get()); |
639 | 0 | _stat.merged_rows++; |
640 | 0 | row_without_delete_sign = nullptr; |
641 | 0 | } |
642 | | // and then unconditionally replace the previous row |
643 | 0 | prev_row = row_with_delete_sign; |
644 | 0 | } else { |
645 | 0 | prev_row = row_without_delete_sign; |
646 | 0 | } |
647 | |
|
648 | 0 | if (prev_row == nullptr) { |
649 | 0 | add_row(cur_row_ptr, cur_row_has_delete_sign); |
650 | 0 | } else { |
651 | 0 | if (!prev_row->has_init_agg()) { |
652 | 0 | _init_row_for_agg(prev_row.get(), mutable_block); |
653 | 0 | } |
654 | 0 | _stat.merged_rows++; |
655 | 0 | _aggregate_two_row_in_block<true>(mutable_block, cur_row, prev_row.get()); |
656 | 0 | } |
657 | 0 | } else { |
658 | 0 | finalize_rows(); |
659 | 0 | add_row(cur_row_ptr, cur_row_has_delete_sign); |
660 | 0 | } |
661 | 0 | } |
662 | | // finalize the last lows |
663 | 0 | finalize_rows(); |
664 | 0 | } Unexecuted instantiation: _ZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE Unexecuted instantiation: _ZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE |
665 | | |
666 | | template <bool is_final> |
667 | | void MemTable::_aggregate_for_flexible_partial_update_with_seq_col( |
668 | 0 | MutableBlock& mutable_block, DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks) { |
669 | | // For flexible partial update, when table has sequence column, we don't do any aggregation |
670 | | // in memtable. These duplicate rows will be aggregated in VerticalSegmentWriter |
671 | 0 | int row_pos = -1; |
672 | 0 | for (const auto& row_ptr : *_row_in_blocks) { |
673 | 0 | RowInBlock* row = row_ptr.get(); |
674 | 0 | temp_row_in_blocks.push_back(row_ptr); |
675 | 0 | _finalize_one_row<is_final>(row, mutable_block, ++row_pos); |
676 | 0 | } |
677 | 0 | } Unexecuted instantiation: _ZN5doris8MemTable51_aggregate_for_flexible_partial_update_with_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE Unexecuted instantiation: _ZN5doris8MemTable51_aggregate_for_flexible_partial_update_with_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE |
678 | | |
679 | 9 | void MemTable::shrink_memtable_by_agg() { |
680 | 9 | SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER( |
681 | 9 | _resource_ctx->memory_context()->mem_tracker()->write_tracker()); |
682 | 9 | SCOPED_CONSUME_MEM_TRACKER(_mem_tracker); |
683 | 9 | if (_keys_type == KeysType::DUP_KEYS) { |
684 | 0 | return; |
685 | 0 | } |
686 | 9 | size_t same_keys_num = _sort(); |
687 | 9 | if (same_keys_num != 0) { |
688 | 9 | (_skip_bitmap_col_idx == -1) ? _aggregate<false, false>() : _aggregate<false, true>(); |
689 | 9 | } |
690 | 9 | _last_agg_pos = memory_usage(); |
691 | 9 | } |
692 | | |
693 | 31 | bool MemTable::need_flush() const { |
694 | 31 | DBUG_EXECUTE_IF("MemTable.need_flush", { return true; }); |
695 | 31 | auto max_size = _adaptive_write_buffer_size(); |
696 | 31 | if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) { |
697 | 0 | auto update_columns_size = _num_columns; |
698 | 0 | auto min_buffer_size = config::min_write_buffer_size_for_partial_update; |
699 | 0 | max_size = max_size * update_columns_size / _tablet_schema->num_columns(); |
700 | 0 | max_size = max_size > min_buffer_size ? max_size : min_buffer_size; |
701 | 0 | } |
702 | | |
703 | 31 | if (memory_usage() >= max_size) { |
704 | 1 | g_flush_cuz_memtable_full << 1; |
705 | 1 | return true; |
706 | 1 | } |
707 | 30 | return false; |
708 | 31 | } |
709 | | |
710 | 31 | int64_t MemTable::_adaptive_write_buffer_size() const { |
711 | 31 | if (!config::enable_adaptive_write_buffer_size) [[unlikely]] { |
712 | 8 | return config::write_buffer_size; |
713 | 8 | } |
714 | 23 | auto* memtable_limiter = ExecEnv::GetInstance()->memtable_memory_limiter(); |
715 | 23 | int64_t memtable_mem = |
716 | 23 | (memtable_limiter != nullptr && memtable_limiter->mem_tracker() != nullptr) |
717 | 23 | ? memtable_limiter->mem_tracker()->consumption() |
718 | 23 | : 0; |
719 | 23 | int64_t factor = 4; |
720 | | // Memory usage intervals: |
721 | | // (80 %, 100 %] → 1× buffer |
722 | | // (50 %, 80 %] → 2× buffer |
723 | | // [0 %, 50 %] → 4× buffer |
724 | 23 | if (memtable_mem > (_load_mem_limit * 4) / 5) { // > 80 % |
725 | 0 | factor = 1; |
726 | 23 | } else if (memtable_mem > _load_mem_limit / 2) { // > 50 % |
727 | 0 | factor = 2; |
728 | 0 | } |
729 | 23 | return config::write_buffer_size * factor; |
730 | 31 | } |
731 | | |
732 | 33 | bool MemTable::need_agg() const { |
733 | 33 | if (_keys_type == KeysType::AGG_KEYS) { |
734 | 15 | auto max_size = _last_agg_pos + config::write_buffer_size_for_agg; |
735 | 15 | return memory_usage() >= max_size; |
736 | 15 | } |
737 | 18 | return false; |
738 | 33 | } |
739 | | |
740 | 0 | size_t MemTable::get_flush_reserve_memory_size() const { |
741 | 0 | if (_keys_type == KeysType::DUP_KEYS && _tablet_schema->num_key_columns() == 0) { |
742 | 0 | return 0; // no need to reserve |
743 | 0 | } |
744 | 0 | return static_cast<size_t>(static_cast<double>(_input_mutable_block.allocated_bytes()) * 1.2); |
745 | 0 | } |
746 | | |
747 | 14 | Status MemTable::_to_block(std::unique_ptr<Block>* res) { |
748 | 14 | size_t same_keys_num = _sort(); |
749 | 14 | if (_keys_type == KeysType::DUP_KEYS || same_keys_num == 0) { |
750 | 10 | if (_keys_type == KeysType::DUP_KEYS && _tablet_schema->num_key_columns() == 0) { |
751 | 0 | _output_mutable_block.swap(_input_mutable_block); |
752 | 10 | } else { |
753 | 10 | Block in_block = _input_mutable_block.to_block(); |
754 | 10 | RETURN_IF_ERROR(_put_into_output(in_block)); |
755 | 10 | } |
756 | 10 | } else { |
757 | 4 | (_skip_bitmap_col_idx == -1) ? _aggregate<true, false>() : _aggregate<true, true>(); |
758 | 4 | } |
759 | 14 | if (_keys_type == KeysType::UNIQUE_KEYS && _enable_unique_key_mow && |
760 | 14 | !_tablet_schema->cluster_key_uids().empty()) { |
761 | 3 | if (_partial_update_mode != UniqueKeyUpdateModePB::UPSERT) { |
762 | 0 | return Status::InternalError( |
763 | 0 | "Partial update for mow with cluster keys is not supported"); |
764 | 0 | } |
765 | 3 | RETURN_IF_ERROR(_sort_by_cluster_keys()); |
766 | 3 | } |
767 | 14 | _input_mutable_block.clear(); |
768 | 14 | *res = Block::create_unique(_output_mutable_block.to_block()); |
769 | 14 | return Status::OK(); |
770 | 14 | } |
771 | | |
772 | 14 | Status MemTable::to_block(std::unique_ptr<Block>* res) { |
773 | 14 | RETURN_IF_ERROR_OR_CATCH_EXCEPTION(_to_block(res)); |
774 | 14 | return Status::OK(); |
775 | 14 | } |
776 | | |
777 | | #include "common/compile_check_end.h" |
778 | | } // namespace doris |