Skip to content

Commit 93c48b6

Browse files
ti-chi-botJinheLin
andauthored
Storages: Fix MVCC bitmap read bytes estimation (#10378) (#10386)
ref #10380 Signed-off-by: ti-chi-bot <ti-community-prow-bot@tidb.io> Co-authored-by: jinhelin <linjinhe33@gmail.com>
1 parent e0274b5 commit 93c48b6

6 files changed

Lines changed: 181 additions & 12 deletions

File tree

dbms/src/Storages/DeltaMerge/Segment.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,62 @@ SegmentSnapshotPtr Segment::createSnapshot(const DMContext & dm_context, bool fo
932932
Logger::get(dm_context.tracing_id));
933933
}
934934

935+
std::vector<DMFilePackFilter> Segment::loadDMFilePackFilters(
936+
const DMFiles & dmfiles,
937+
const MinMaxIndexCachePtr & index_cache,
938+
const RowKeyRanges & rowkey_ranges,
939+
const RSOperatorPtr & rs_filter,
940+
const FileProviderPtr & file_provider,
941+
const ReadLimiterPtr & read_limiter,
942+
const ScanContextPtr & scan_context,
943+
const String & tracing_id,
944+
const ReadTag & read_tag)
945+
{
946+
std::vector<DMFilePackFilter> pack_filters;
947+
pack_filters.reserve(dmfiles.size());
948+
for (const auto & dmfile : dmfiles)
949+
{
950+
auto pack_filter = DMFilePackFilter::loadFrom(
951+
dmfile,
952+
index_cache,
953+
/*set_cache_if_miss*/ true,
954+
rowkey_ranges,
955+
rs_filter,
956+
/*read_packs*/ {},
957+
file_provider,
958+
read_limiter,
959+
scan_context,
960+
tracing_id,
961+
read_tag);
962+
pack_filters.emplace_back(std::move(pack_filter));
963+
}
964+
return pack_filters;
965+
}
966+
967+
UInt64 Segment::estimatedBytesOfInternalColumns(
968+
const SegmentSnapshotPtr & read_snap,
969+
std::vector<DMFilePackFilter> & pack_filters,
970+
UInt64 start_ts)
971+
{
972+
// stable->getDMFiles() at least return one DMFile.
973+
const auto & dmfiles = read_snap->stable->getDMFiles();
974+
RUNTIME_CHECK(!dmfiles.empty());
975+
auto handle_size = dmfiles.front()->getColumnStat(EXTRA_HANDLE_COLUMN_ID).avg_size;
976+
if (handle_size == 0)
977+
handle_size = sizeof(UInt64);
978+
constexpr auto version_size = sizeof(UInt64);
979+
constexpr auto delmark_size = sizeof(UInt8);
980+
981+
// For delta, rs_filter does not filter rows, so we need to read all rows.
982+
const auto delta_read_rows = read_snap->delta->getRows();
983+
// For Stable, rs_filter may filter rows.
984+
const auto stable_read_rows = read_snap->stable->estimatedReadRows(
985+
pack_filters,
986+
start_ts,
987+
/*use_delta_index*/ delta_read_rows != 0);
988+
return (handle_size + version_size + delmark_size) * (delta_read_rows + stable_read_rows);
989+
}
990+
935991
BlockInputStreamPtr Segment::getInputStream(
936992
const ReadMode & read_mode,
937993
const DMContext & dm_context,
@@ -949,6 +1005,33 @@ BlockInputStreamPtr Segment::getInputStream(
9491005
expected_block_size,
9501006
columns_to_read,
9511007
segment_snap->stable->stable);
1008+
1009+
// Building MVCC bitmap can be a resource-intensive operation that cannot be paused midway.
1010+
// To prevent scenarios where multiple segments concurrently build MVCC bitmaps but exhaust
1011+
// RU during execution and causing RU consumption to exceed limitations, we need to first
1012+
// estimate the cost of building the MVCC bitmap, pre-consume the corresponding RU,
1013+
// and then proceed with the building task.
1014+
const auto & res_group_name = dm_context.scan_context->resource_group_name;
1015+
if (likely(read_mode == ReadMode::Bitmap && !res_group_name.empty()))
1016+
{
1017+
auto real_ranges = shrinkRowKeyRanges(read_ranges);
1018+
if (!real_ranges.empty())
1019+
{
1020+
auto pack_filters = loadDMFilePackFilters(
1021+
segment_snap->stable->getDMFiles(),
1022+
dm_context.global_context.getMinMaxIndexCache(),
1023+
real_ranges,
1024+
filter ? filter->rs_operator : EMPTY_RS_OPERATOR,
1025+
dm_context.global_context.getFileProvider(),
1026+
dm_context.global_context.getReadLimiter(),
1027+
dm_context.scan_context,
1028+
dm_context.tracing_id,
1029+
ReadTag::MVCC);
1030+
auto bytes = estimatedBytesOfInternalColumns(segment_snap, pack_filters, start_ts);
1031+
LocalAdmissionController::global_instance->consumeBytesResource(res_group_name, bytesToRU(bytes));
1032+
}
1033+
}
1034+
9521035
switch (read_mode)
9531036
{
9541037
case ReadMode::Normal:

dbms/src/Storages/DeltaMerge/Segment.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class RSOperator;
4444
using RSOperatorPtr = std::shared_ptr<RSOperator>;
4545
class PushDownFilter;
4646
using PushDownFilterPtr = std::shared_ptr<PushDownFilter>;
47+
class MinMaxIndexCache;
48+
using MinMaxIndexCachePtr = std::shared_ptr<MinMaxIndexCache>;
4749

4850
enum class ReadMode;
4951

@@ -68,13 +70,6 @@ struct SegmentSnapshot : private boost::noncopyable
6870

6971
bool isForUpdate() const { return delta->isForUpdate(); }
7072

71-
UInt64 estimatedBytesOfInternalColumns() const
72-
{
73-
// TODO: how about cluster index?
74-
// handle + version + flag
75-
return (sizeof(Int64) + sizeof(UInt64) + sizeof(UInt8)) * getRows();
76-
}
77-
7873
String detailInfo() const;
7974
};
8075

@@ -730,6 +725,7 @@ class Segment
730725
bool relevant_place) const;
731726

732727
static bool useCleanRead(const SegmentSnapshotPtr & segment_snap, const ColumnDefines & columns_to_read);
728+
// Shrink the read_ranges by the segment real rowkey_range
733729
RowKeyRanges shrinkRowKeyRanges(const RowKeyRanges & read_ranges) const;
734730
BitmapFilterPtr buildBitmapFilter(
735731
const DMContext & dm_context,
@@ -795,6 +791,21 @@ class Segment
795791
const ColumnDefines & read_columns,
796792
const StableValueSpacePtr & stable);
797793

794+
static std::vector<DMFilePackFilter> loadDMFilePackFilters(
795+
const DMFiles & dmfiles,
796+
const MinMaxIndexCachePtr & index_cache,
797+
const RowKeyRanges & rowkey_ranges,
798+
const RSOperatorPtr & rs_filter,
799+
const FileProviderPtr & file_provider,
800+
const ReadLimiterPtr & read_limiter,
801+
const ScanContextPtr & scan_context,
802+
const String & tracing_id,
803+
const ReadTag & read_tag);
804+
static UInt64 estimatedBytesOfInternalColumns(
805+
const SegmentSnapshotPtr & read_snap,
806+
std::vector<DMFilePackFilter> & pack_filters,
807+
UInt64 start_ts);
808+
798809
#ifndef DBMS_PUBLIC_GTEST
799810
private:
800811
#else

dbms/src/Storages/DeltaMerge/SegmentReadTaskPool.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,6 @@ BlockInputStreamPtr SegmentReadTaskPool::buildInputStream(SegmentReadTaskPtr & t
8989

9090
t->fetchPages();
9191

92-
if (likely(read_mode == ReadMode::Bitmap && !res_group_name.empty()))
93-
{
94-
auto bytes = t->read_snapshot->estimatedBytesOfInternalColumns();
95-
LocalAdmissionController::global_instance->consumeBytesResource(res_group_name, bytesToRU(bytes));
96-
}
9792
t->initInputStream(
9893
columns_to_read,
9994
start_ts,

dbms/src/Storages/DeltaMerge/StableValueSpace.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,48 @@ StableValueSpace::Snapshot::getAtLeastRowsAndBytes(const DMContext & context, co
633633
return ret;
634634
}
635635

636+
// Estimating the number of rows that need to be read to build the MVCC bitmap.
637+
// If a delta index is used, the relevant data must be read and merged with the delta.
638+
// For stable-only (use_delta_index is false), no merging with the delta is required,
639+
// so reading occurs only when filtering of the pack is necessary.
640+
UInt64 StableValueSpace::Snapshot::estimatedReadRows(
641+
std::vector<DMFilePackFilter> & pack_filters,
642+
UInt64 start_ts,
643+
bool use_delta_index) const
644+
{
645+
const auto & dmfiles = getDMFiles();
646+
UInt64 rows = 0;
647+
for (size_t i = 0; i < dmfiles.size(); ++i)
648+
{
649+
const auto & dmfile = dmfiles[i];
650+
auto & pack_filter = pack_filters[i];
651+
const auto & pack_res = pack_filter.getPackResConst();
652+
const auto & handle_res = pack_filter.getHandleRes();
653+
const auto & pack_stats = dmfile->getPackStats();
654+
for (size_t pack_id = 0; pack_id < pack_stats.size(); ++pack_id)
655+
{
656+
const auto & pack_stat = pack_stats[pack_id];
657+
if (!pack_res[pack_id].isUse())
658+
continue;
659+
660+
if (use_delta_index)
661+
{
662+
rows += pack_stat.rows;
663+
}
664+
else if (
665+
handle_res[pack_id] == RSResult::Some || pack_stat.not_clean > 0
666+
|| pack_filter.getMaxVersion(pack_id) > start_ts)
667+
{
668+
// `not_clean > 0` means there are more than one version for some rowkeys in this pack
669+
// `pack.max_version > start_ts` means some rows will be filtered by MVCC reading
670+
// We need to read this pack to do RowKey or MVCC filter.
671+
rows += pack_stat.rows;
672+
}
673+
}
674+
}
675+
return rows;
676+
}
677+
636678
static size_t defaultValueBytes(const Field & f)
637679
{
638680
switch (f.getType())

dbms/src/Storages/DeltaMerge/StableValueSpace.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ class StableValueSpace : public std::enable_shared_from_this<StableValueSpace>
253253
*/
254254
AtLeastRowsAndBytesResult getAtLeastRowsAndBytes(const DMContext & context, const RowKeyRange & range) const;
255255

256+
UInt64 estimatedReadRows(std::vector<DMFilePackFilter> & pack_filters, UInt64 start_ts, bool use_delta_index)
257+
const;
258+
256259
private:
257260
LoggerPtr log;
258261
};

dbms/src/Storages/DeltaMerge/tests/gtest_segment_bitmap.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <DataStreams/OneBlockInputStream.h>
1717
#include <Interpreters/Context.h>
1818
#include <Storages/DeltaMerge/DeltaMergeStore.h>
19+
#include <Storages/DeltaMerge/File/DMFilePackFilter.h>
1920
#include <Storages/DeltaMerge/tests/gtest_segment_test_basic.h>
2021
#include <Storages/DeltaMerge/tests/gtest_segment_util.h>
2122
#include <TestUtils/FunctionTestUtils.h>
@@ -177,12 +178,34 @@ class SegmentBitmapFilterTest : public SegmentTestBasic
177178
ASSERT_TRUE(sequenceEqual(expected_handle.data(), handle->data(), test_case.expected_size));
178179
}
179180
}
181+
182+
UInt64 estimatedBytesOfInternalColumns(UInt64 start_ts)
183+
{
184+
auto [seg, snap] = getSegmentForRead(SEG_ID);
185+
auto pack_filters = Segment::loadDMFilePackFilters(
186+
snap->stable->getDMFiles(),
187+
dm_context->global_context.getMinMaxIndexCache(),
188+
/*rowkey_ranges*/ {},
189+
EMPTY_RS_OPERATOR,
190+
dm_context->global_context.getFileProvider(),
191+
dm_context->global_context.getReadLimiter(),
192+
dm_context->scan_context,
193+
dm_context->tracing_id,
194+
ReadTag::MVCC);
195+
return Segment::estimatedBytesOfInternalColumns(snap, pack_filters, start_ts);
196+
}
180197
};
181198

182199
TEST_F(SegmentBitmapFilterTest, InMemory1)
183200
try
184201
{
185202
runTestCase(TestCase("d_mem:[0, 1000)", 1000, "[0, 1000)", "[0, 1000)"));
203+
204+
// Delta only
205+
auto bytes = estimatedBytesOfInternalColumns(std::numeric_limits<UInt64>::max());
206+
ASSERT_EQ(bytes, 17 * 1000);
207+
bytes = estimatedBytesOfInternalColumns(0);
208+
ASSERT_EQ(bytes, 17 * 1000);
186209
}
187210
CATCH
188211

@@ -272,6 +295,12 @@ TEST_F(SegmentBitmapFilterTest, Stable1)
272295
try
273296
{
274297
runTestCase(TestCase{"s:[0, 1024)", 1024, "[0, 1024)", "[0, 1024)"});
298+
299+
// Stable only
300+
auto bytes = estimatedBytesOfInternalColumns(std::numeric_limits<UInt64>::max());
301+
ASSERT_EQ(bytes, 0);
302+
bytes = estimatedBytesOfInternalColumns(0);
303+
ASSERT_EQ(bytes, 17 * 1024);
275304
}
276305
CATCH
277306

@@ -302,6 +331,12 @@ try
302331
946,
303332
"[0, 128)|[1034, 1089)|[256, 298)|[1089, 1096)|[310, 1024)",
304333
"[0, 128)|[200, 255)|[256, 305)|[310, 1024)"});
334+
335+
// Delta + Stable
336+
auto bytes = estimatedBytesOfInternalColumns(std::numeric_limits<UInt64>::max());
337+
ASSERT_EQ(bytes, 17 * (1024 + 10 + 55 + 7));
338+
bytes = estimatedBytesOfInternalColumns(0);
339+
ASSERT_EQ(bytes, 17 * (1024 + 10 + 55 + 7));
305340
}
306341
CATCH
307342

0 commit comments

Comments
 (0)