mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 23:52:03 +00:00
Merge
This commit is contained in:
parent
489696281e
commit
9690a3003b
@ -73,12 +73,15 @@ public:
|
||||
}
|
||||
|
||||
/// Получает набор диапазонов засечек, вне которых не могут находиться ключи из заданного диапазона.
|
||||
static MarkRanges markRangesFromPkRange(const String & path,
|
||||
size_t marks_count,
|
||||
StorageMergeTree & storage,
|
||||
PKCondition & key_condition)
|
||||
static MarkRanges markRangesFromPkRange(
|
||||
const StorageMergeTree::DataPart::Index & index,
|
||||
StorageMergeTree & storage,
|
||||
PKCondition & key_condition)
|
||||
{
|
||||
MarkRanges res;
|
||||
|
||||
size_t key_size = storage.sort_descr.size();
|
||||
size_t marks_count = index.size() / key_size;
|
||||
|
||||
/// Если индекс не используется.
|
||||
if (key_condition.alwaysTrue())
|
||||
@ -87,31 +90,11 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Читаем индекс.
|
||||
typedef AutoArray<Row> Index;
|
||||
size_t key_size = storage.sort_descr.size();
|
||||
Index index(marks_count);
|
||||
for (size_t i = 0; i < marks_count; ++i)
|
||||
index[i].resize(key_size);
|
||||
|
||||
{
|
||||
String index_path = path + "primary.idx";
|
||||
ReadBufferFromFile index_file(index_path, std::min(static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize()));
|
||||
|
||||
for (size_t i = 0; i < marks_count; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < key_size; ++j)
|
||||
storage.primary_key_sample.getByPosition(j).type->deserializeBinary(index[i][j], index_file);
|
||||
}
|
||||
|
||||
if (!index_file.eof())
|
||||
throw Exception("index file " + index_path + " is unexpectedly long", ErrorCodes::EXPECTED_END_OF_FILE);
|
||||
}
|
||||
|
||||
/// В стеке всегда будут находиться непересекающиеся подозрительные отрезки, самый левый наверху (back).
|
||||
/// На каждом шаге берем левый отрезок и проверяем, подходит ли он.
|
||||
/// Если подходит, разбиваем его на более мелкие и кладем их в стек. Если нет - выбрасываем его.
|
||||
/// Если отрезок уже длиной в одну засечку, добавляем его в ответ и выбрасываем.
|
||||
/** В стеке всегда будут находиться непересекающиеся подозрительные отрезки, самый левый наверху (back).
|
||||
* На каждом шаге берем левый отрезок и проверяем, подходит ли он.
|
||||
* Если подходит, разбиваем его на более мелкие и кладем их в стек. Если нет - выбрасываем его.
|
||||
* Если отрезок уже длиной в одну засечку, добавляем его в ответ и выбрасываем.
|
||||
*/
|
||||
std::vector<MarkRange> ranges_stack;
|
||||
ranges_stack.push_back(MarkRange(0, marks_count));
|
||||
while (!ranges_stack.empty())
|
||||
@ -121,9 +104,9 @@ public:
|
||||
|
||||
bool may_be_true;
|
||||
if (range.end == marks_count)
|
||||
may_be_true = key_condition.mayBeTrueAfter(index[range.begin]);
|
||||
may_be_true = key_condition.mayBeTrueAfter(&index[range.begin * key_size]);
|
||||
else
|
||||
may_be_true = key_condition.mayBeTrueInRange(index[range.begin], index[range.end]);
|
||||
may_be_true = key_condition.mayBeTrueInRange(&index[range.begin * key_size], &index[range.end * key_size]);
|
||||
|
||||
if (!may_be_true)
|
||||
continue;
|
||||
@ -132,23 +115,19 @@ public:
|
||||
{
|
||||
/// Увидели полезный промежуток между соседними засечками. Либо добавим его к последнему диапазону, либо начнем новый диапазон.
|
||||
if (res.empty() || range.begin - res.back().end > storage.min_marks_for_seek)
|
||||
{
|
||||
res.push_back(range);
|
||||
}
|
||||
else
|
||||
{
|
||||
res.back().end = range.end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Разбиваем отрезок и кладем результат в стек справа налево.
|
||||
size_t step = (range.end - range.begin - 1) / storage.settings.coarse_index_granularity + 1;
|
||||
size_t end;
|
||||
|
||||
for (end = range.end; end > range.begin + step; end -= step)
|
||||
{
|
||||
ranges_stack.push_back(MarkRange(end - step, end));
|
||||
}
|
||||
|
||||
ranges_stack.push_back(MarkRange(range.begin, end));
|
||||
}
|
||||
}
|
||||
|
@ -104,10 +104,11 @@ private:
|
||||
size_t rows = block.rows();
|
||||
size_t columns = block.columns();
|
||||
UInt64 part_id = storage.increment.get(false);
|
||||
size_t part_size = (rows + storage.index_granularity - 1) / storage.index_granularity;
|
||||
|
||||
String part_name = storage.getPartName(
|
||||
DayNum_t(min_date), DayNum_t(max_date),
|
||||
part_id, part_id, 0);
|
||||
part_id, part_id, 0);
|
||||
|
||||
String part_tmp_path = storage.full_path + "tmp_" + part_name + "/";
|
||||
String part_res_path = storage.full_path + part_name + "/";
|
||||
@ -128,6 +129,9 @@ private:
|
||||
LOG_TRACE(storage.log, "Writing index.");
|
||||
|
||||
/// Сначала пишем индекс. Индекс содержит значение PK для каждой index_granularity строки.
|
||||
StorageMergeTree::DataPart::Index index_vec;
|
||||
index_vec.reserve(part_size * storage.sort_descr.size());
|
||||
|
||||
{
|
||||
WriteBufferFromFile index(part_tmp_path + "primary.idx", DBMS_DEFAULT_BUFFER_SIZE, flags);
|
||||
|
||||
@ -141,8 +145,13 @@ private:
|
||||
: &block.getByPosition(storage.sort_descr[i].column_number));
|
||||
|
||||
for (size_t i = 0; i < rows; i += storage.index_granularity)
|
||||
{
|
||||
for (PrimaryColumns::const_iterator it = primary_columns.begin(); it != primary_columns.end(); ++it)
|
||||
(*it)->type->serializeBinary((*(*it)->column)[i], index);
|
||||
{
|
||||
index_vec.push_back((*(*it)->column)[i]);
|
||||
(*it)->type->serializeBinary(index_vec.back(), index);
|
||||
}
|
||||
}
|
||||
|
||||
index.next();
|
||||
}
|
||||
@ -175,10 +184,11 @@ private:
|
||||
new_data_part->right = part_id;
|
||||
new_data_part->level = 0;
|
||||
new_data_part->name = part_name;
|
||||
new_data_part->size = (rows + storage.index_granularity - 1) / storage.index_granularity;
|
||||
new_data_part->size = part_size;
|
||||
new_data_part->modification_time = time(0);
|
||||
new_data_part->left_month = date_lut.toFirstDayNumOfMonth(new_data_part->left_date);
|
||||
new_data_part->right_month = date_lut.toFirstDayNumOfMonth(new_data_part->right_date);
|
||||
new_data_part->index.swap(index_vec);
|
||||
|
||||
storage.data_parts.insert(new_data_part);
|
||||
storage.all_data_parts.insert(new_data_part);
|
||||
|
@ -158,11 +158,11 @@ public:
|
||||
|
||||
/// Выполнимо ли условие в диапазоне ключей.
|
||||
/// left_pk и right_pk должны содержать все поля из sort_descr в соответствующем порядке.
|
||||
bool mayBeTrueInRange(const Row & left_pk, const Row & right_pk);
|
||||
bool mayBeTrueInRange(const Field * left_pk, const Field * right_pk);
|
||||
|
||||
/// Выполнимо ли условие в полубесконечном (не ограниченном справа) диапазоне ключей.
|
||||
/// left_pk должен содержать все поля из sort_descr в соответствующем порядке.
|
||||
bool mayBeTrueAfter(const Row & left_pk);
|
||||
bool mayBeTrueAfter(const Field * left_pk);
|
||||
|
||||
bool alwaysTrue()
|
||||
{
|
||||
@ -230,7 +230,7 @@ private:
|
||||
typedef std::vector<RPNElement> RPN;
|
||||
typedef std::map<String, size_t> ColumnIndices;
|
||||
|
||||
bool mayBeTrueInRange(const Row & left_pk, const Row & right_pk, bool right_bounded);
|
||||
bool mayBeTrueInRange(const Field * left_pk, const Field * right_pk, bool right_bounded);
|
||||
|
||||
void traverseAST(ASTPtr & node, Block & block_with_constants);
|
||||
bool atomFromAST(ASTPtr & node, Block & block_with_constants, RPNElement & out);
|
||||
|
@ -260,7 +260,11 @@ private:
|
||||
/// Смотреть и изменять это поле следует под залоченным data_parts_mutex.
|
||||
bool currently_merging;
|
||||
|
||||
/// NOTE можно загружать индекс и засечки в оперативку
|
||||
/// Первичный ключ. Всегда загружается в оперативку.
|
||||
typedef std::vector<Field> Index;
|
||||
Index index;
|
||||
|
||||
/// NOTE можно загружать засечки тоже в оперативку
|
||||
|
||||
void remove() const
|
||||
{
|
||||
@ -318,6 +322,23 @@ private:
|
||||
&& left <= rhs.left
|
||||
&& right >= rhs.right;
|
||||
}
|
||||
|
||||
/// Загрузить индекс.
|
||||
void loadIndex()
|
||||
{
|
||||
size_t key_size = storage.sort_descr.size();
|
||||
index.resize(key_size * size);
|
||||
|
||||
String index_path = storage.full_path + name + "/primary.idx";
|
||||
ReadBufferFromFile index_file(index_path, std::min(static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE), Poco::File(index_path).getSize()));
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
for (size_t j = 0; j < key_size; ++j)
|
||||
storage.primary_key_sample.getByPosition(j).type->deserializeBinary(index[i * key_size + j], index_file);
|
||||
|
||||
if (!index_file.eof())
|
||||
throw Exception("index file " + index_path + " is unexpectedly long", ErrorCodes::EXPECTED_END_OF_FILE);
|
||||
}
|
||||
};
|
||||
|
||||
typedef SharedPtr<DataPart> DataPartPtr;
|
||||
@ -370,6 +391,7 @@ private:
|
||||
bool use_uncompressed_cache,
|
||||
ExpressionActionsPtr prewhere_actions,
|
||||
const String & prewhere_column);
|
||||
|
||||
BlockInputStreams spreadMarkRangesAmongThreadsFinal(
|
||||
RangesInDataParts parts,
|
||||
size_t threads,
|
||||
|
@ -241,7 +241,7 @@ struct BoolMask
|
||||
}
|
||||
};
|
||||
|
||||
bool PKCondition::mayBeTrueInRange(const Row & left_pk, const Row & right_pk, bool right_bounded)
|
||||
bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk, bool right_bounded)
|
||||
{
|
||||
/// Найдем диапазоны элементов ключа.
|
||||
std::vector<Range> key_ranges(sort_descr.size(), Range());
|
||||
@ -311,14 +311,14 @@ bool PKCondition::mayBeTrueInRange(const Row & left_pk, const Row & right_pk, bo
|
||||
return rpn_stack[0].can_be_true;
|
||||
}
|
||||
|
||||
bool PKCondition::mayBeTrueInRange(const Row & left_pk, const Row & right_pk)
|
||||
bool PKCondition::mayBeTrueInRange(const Field * left_pk, const Field * right_pk)
|
||||
{
|
||||
return mayBeTrueInRange(left_pk, right_pk, true);
|
||||
}
|
||||
|
||||
bool PKCondition::mayBeTrueAfter(const Row & left_pk)
|
||||
bool PKCondition::mayBeTrueAfter(const Field * left_pk)
|
||||
{
|
||||
return mayBeTrueInRange(left_pk, Row(), false);
|
||||
return mayBeTrueInRange(left_pk, NULL, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -166,10 +166,15 @@ BlockInputStreams StorageMergeTree::read(
|
||||
/// Выберем куски, в которых могут быть данные, удовлетворяющие date_condition.
|
||||
{
|
||||
Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
|
||||
|
||||
|
||||
for (DataParts::iterator it = data_parts.begin(); it != data_parts.end(); ++it)
|
||||
if (date_condition.mayBeTrueInRange(Row(1, static_cast<UInt64>((*it)->left_date)),Row(1, static_cast<UInt64>((*it)->right_date))))
|
||||
{
|
||||
Field left = static_cast<UInt64>((*it)->left_date);
|
||||
Field right = static_cast<UInt64>((*it)->right_date);
|
||||
|
||||
if (date_condition.mayBeTrueInRange(&left, &right))
|
||||
parts.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
/// Семплирование.
|
||||
@ -196,10 +201,8 @@ BlockInputStreams StorageMergeTree::read(
|
||||
for (size_t i = 0; i < parts.size(); ++i)
|
||||
{
|
||||
DataPartPtr & part = parts[i];
|
||||
MarkRanges ranges = MergeTreeBlockInputStream::markRangesFromPkRange(full_path + part->name + '/',
|
||||
part->size,
|
||||
*this,
|
||||
key_condition);
|
||||
MarkRanges ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition);
|
||||
|
||||
for (size_t j = 0; j < ranges.size(); ++j)
|
||||
total_count += ranges[j].end - ranges[j].begin;
|
||||
}
|
||||
@ -272,10 +275,8 @@ BlockInputStreams StorageMergeTree::read(
|
||||
{
|
||||
DataPartPtr & part = parts[i];
|
||||
RangesInDataPart ranges(part);
|
||||
ranges.ranges = MergeTreeBlockInputStream::markRangesFromPkRange(full_path + part->name + '/',
|
||||
part->size,
|
||||
*this,
|
||||
key_condition);
|
||||
ranges.ranges = MergeTreeBlockInputStream::markRangesFromPkRange(part->index, *this, key_condition);
|
||||
|
||||
if (!ranges.ranges.empty())
|
||||
{
|
||||
parts_with_ranges.push_back(ranges);
|
||||
@ -638,6 +639,17 @@ void StorageMergeTree::loadDataParts()
|
||||
|
||||
part->modification_time = Poco::File(full_path + file_name).getLastModified().epochTime();
|
||||
|
||||
try
|
||||
{
|
||||
part->loadIndex();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// Не будем вставлять в набор кусок с битым индексом. Пропустим кусок и позволим серверу запуститься.
|
||||
tryLogCurrentException(__PRETTY_FUNCTION__);
|
||||
continue;
|
||||
}
|
||||
|
||||
data_parts.insert(part);
|
||||
}
|
||||
|
||||
@ -1070,6 +1082,7 @@ void StorageMergeTree::mergeParts(std::vector<DataPartPtr> parts)
|
||||
|
||||
new_data_part->size = to->marksCount();
|
||||
new_data_part->modification_time = time(0);
|
||||
new_data_part->loadIndex(); /// NOTE Только что записанный индекс заново считывается с диска. Можно было бы формировать его сразу при записи.
|
||||
|
||||
{
|
||||
Poco::ScopedLock<Poco::FastMutex> lock(data_parts_mutex);
|
||||
|
Loading…
Reference in New Issue
Block a user