2012-07-16 20:25:19 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2012-07-17 20:04:39 +00:00
|
|
|
|
#include <statdaemons/Increment.h>
|
2012-11-28 08:52:15 +00:00
|
|
|
|
#include <statdaemons/threadpool.hpp>
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
|
|
|
|
#include <DB/Core/SortDescription.h>
|
2012-07-16 20:25:19 +00:00
|
|
|
|
#include <DB/Interpreters/Context.h>
|
2013-06-03 13:17:17 +00:00
|
|
|
|
#include <DB/Interpreters/ExpressionActions.h>
|
2012-07-16 20:25:19 +00:00
|
|
|
|
#include <DB/Storages/IStorage.h>
|
2014-02-28 14:25:15 +00:00
|
|
|
|
#include <Poco/RWLock.h>
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/** Движок, использующий merge tree для инкрементальной сортировки данных.
|
|
|
|
|
* Таблица представлена набором сортированных кусков.
|
|
|
|
|
* При вставке, данные сортируются по указанному выражению (первичному ключу) и пишутся в новый кусок.
|
|
|
|
|
* Куски объединяются в фоне, согласно некоторой эвристике.
|
|
|
|
|
* Для каждого куска, создаётся индексный файл, содержащий значение первичного ключа для каждой n-ой строки.
|
|
|
|
|
* Таким образом, реализуется эффективная выборка по диапазону первичного ключа.
|
|
|
|
|
*
|
|
|
|
|
* Дополнительно:
|
|
|
|
|
*
|
|
|
|
|
* Указывается столбец, содержащий дату.
|
|
|
|
|
* Для каждого куска пишется минимальная и максимальная дата.
|
|
|
|
|
* (по сути - ещё один индекс)
|
|
|
|
|
*
|
|
|
|
|
* Данные разделяются по разным месяцам (пишутся в разные куски для разных месяцев).
|
|
|
|
|
* Куски для разных месяцев не объединяются - для простоты эксплуатации.
|
|
|
|
|
* (дают локальность обновлений, что удобно для синхронизации и бэкапа)
|
|
|
|
|
*
|
|
|
|
|
* Структура файлов:
|
|
|
|
|
* / increment.txt - файл, содержащий одно число, увеличивающееся на 1 - для генерации идентификаторов кусков.
|
|
|
|
|
* / min-date _ max-date _ min-id _ max-id _ level / - директория с куском.
|
2012-07-18 19:16:16 +00:00
|
|
|
|
* / min-date _ max-date _ min-id _ max-id _ level / primary.idx - индексный файл.
|
2012-07-16 20:25:19 +00:00
|
|
|
|
* Внутри директории с куском:
|
|
|
|
|
* Column.bin - данные столбца
|
|
|
|
|
* Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк.
|
2012-08-16 17:27:40 +00:00
|
|
|
|
*
|
2013-09-30 19:54:25 +00:00
|
|
|
|
* Имеется несколько режимов работы, определяющих, что делать при мердже:
|
|
|
|
|
* - Ordinary - ничего дополнительно не делать;
|
|
|
|
|
* - Collapsing - при склейке кусков "схлопывать"
|
|
|
|
|
* пары записей с разными значениями sign_column для одного значения первичного ключа.
|
|
|
|
|
* (см. CollapsingSortedBlockInputStream.h)
|
|
|
|
|
* - Summing - при склейке кусков, при совпадении PK суммировать все числовые столбцы, не входящие в PK.
|
2012-07-16 20:25:19 +00:00
|
|
|
|
*/
|
2012-08-29 20:23:19 +00:00
|
|
|
|
|
|
|
|
|
struct StorageMergeTreeSettings
|
|
|
|
|
{
|
2012-11-29 10:50:17 +00:00
|
|
|
|
/// Набор кусков разрешено объединить, если среди них максимальный размер не более чем во столько раз больше суммы остальных.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
double max_size_ratio_to_merge_parts = 5;
|
2012-11-29 10:50:17 +00:00
|
|
|
|
|
|
|
|
|
/// Сколько за раз сливать кусков.
|
|
|
|
|
/// Трудоемкость выбора кусков O(N * max_parts_to_merge_at_once), так что не следует делать это число слишком большим.
|
|
|
|
|
/// С другой стороны, чтобы слияния точно не могли зайти в тупик, нужно хотя бы
|
|
|
|
|
/// log(max_rows_to_merge_parts/index_granularity)/log(max_size_ratio_to_merge_parts).
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t max_parts_to_merge_at_once = 10;
|
2012-12-06 09:45:09 +00:00
|
|
|
|
|
2013-12-03 14:40:20 +00:00
|
|
|
|
/// Куски настолько большого размера в основном потоке объединять нельзя вообще.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t max_rows_to_merge_parts = 100 * 1024 * 1024;
|
2012-11-28 08:52:15 +00:00
|
|
|
|
|
2013-12-03 14:40:20 +00:00
|
|
|
|
/// Куски настолько большого размера во втором потоке объединять нельзя вообще.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t max_rows_to_merge_parts_second = 1024 * 1024;
|
2013-12-03 14:40:20 +00:00
|
|
|
|
|
|
|
|
|
/// Во столько раз ночью увеличиваем коэффициент.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t merge_parts_at_night_inc = 10;
|
2013-12-03 14:40:20 +00:00
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
/// Сколько потоков использовать для объединения кусков.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t merging_threads = 2;
|
2012-08-29 20:23:19 +00:00
|
|
|
|
|
2012-11-29 10:50:17 +00:00
|
|
|
|
/// Если из одного файла читается хотя бы столько строк, чтение можно распараллелить.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t min_rows_for_concurrent_read = 20 * 8192;
|
2012-11-29 10:50:17 +00:00
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
/// Можно пропускать чтение более чем стольки строк ценой одного seek по файлу.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t min_rows_for_seek = 5 * 8192;
|
2012-12-06 09:45:09 +00:00
|
|
|
|
|
2012-12-10 10:23:10 +00:00
|
|
|
|
/// Если отрезок индекса может содержать нужные ключи, делим его на столько частей и рекурсивно проверяем их.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t coarse_index_granularity = 8;
|
2013-09-08 07:30:52 +00:00
|
|
|
|
|
|
|
|
|
/** Максимальное количество строк на запрос, для использования кэша разжатых данных. Если запрос большой - кэш не используется.
|
|
|
|
|
* (Чтобы большие запросы не вымывали кэш.)
|
|
|
|
|
*/
|
2014-02-28 20:19:00 +00:00
|
|
|
|
size_t max_rows_to_use_cache = 1024 * 1024;
|
2012-12-10 10:23:10 +00:00
|
|
|
|
|
2013-10-03 12:46:17 +00:00
|
|
|
|
/// Через сколько секунд удалять old_куски.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
time_t old_parts_lifetime = 5 * 60;
|
2012-12-06 09:45:09 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Пара засечек, определяющая диапазон строк в куске. Именно, диапазон имеет вид [begin * index_granularity, end * index_granularity).
|
|
|
|
|
struct MarkRange
|
|
|
|
|
{
|
2012-12-06 11:10:05 +00:00
|
|
|
|
size_t begin;
|
|
|
|
|
size_t end;
|
2012-12-06 09:45:09 +00:00
|
|
|
|
|
|
|
|
|
MarkRange() {}
|
2012-12-06 11:10:05 +00:00
|
|
|
|
MarkRange(size_t begin_, size_t end_) : begin(begin_), end(end_) {}
|
2012-08-29 20:23:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
typedef std::vector<MarkRange> MarkRanges;
|
|
|
|
|
|
2012-08-29 20:23:19 +00:00
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
class StorageMergeTree : public IStorage
|
|
|
|
|
{
|
2013-11-26 11:55:11 +00:00
|
|
|
|
friend class MergeTreeReader;
|
2012-07-21 06:47:17 +00:00
|
|
|
|
friend class MergeTreeBlockInputStream;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
friend class MergeTreeBlockOutputStream;
|
2014-03-04 11:30:50 +00:00
|
|
|
|
friend class IMergedBlockOutputStream;
|
2012-07-30 20:32:36 +00:00
|
|
|
|
friend class MergedBlockOutputStream;
|
2014-03-04 11:30:50 +00:00
|
|
|
|
friend class MergedColumnOnlyOutputStream;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
public:
|
2013-09-30 19:54:25 +00:00
|
|
|
|
/// Режим работы. См. выше.
|
|
|
|
|
enum Mode
|
|
|
|
|
{
|
|
|
|
|
Ordinary,
|
|
|
|
|
Collapsing,
|
|
|
|
|
Summing,
|
|
|
|
|
};
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
/** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце),
|
|
|
|
|
* (корректность имён и путей не проверяется)
|
|
|
|
|
* состоящую из указанных столбцов.
|
|
|
|
|
*
|
2012-11-30 00:52:45 +00:00
|
|
|
|
* primary_expr_ast - выражение для сортировки;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
* date_column_name - имя столбца с датой;
|
|
|
|
|
* index_granularity - на сколько строчек пишется одно значение индекса.
|
|
|
|
|
*/
|
2013-02-06 11:26:35 +00:00
|
|
|
|
static StoragePtr create(const String & path_, const String & name_, NamesAndTypesListPtr columns_,
|
2013-05-05 18:02:05 +00:00
|
|
|
|
const Context & context_,
|
2012-12-12 14:25:55 +00:00
|
|
|
|
ASTPtr & primary_expr_ast_,
|
|
|
|
|
const String & date_column_name_,
|
2012-12-12 15:45:08 +00:00
|
|
|
|
const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается.
|
2012-07-31 16:37:20 +00:00
|
|
|
|
size_t index_granularity_,
|
2013-09-30 19:54:25 +00:00
|
|
|
|
Mode mode_ = Ordinary,
|
2012-08-20 05:32:50 +00:00
|
|
|
|
const String & sign_column_ = "",
|
2012-08-29 20:23:19 +00:00
|
|
|
|
const StorageMergeTreeSettings & settings_ = StorageMergeTreeSettings());
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2013-09-30 01:29:19 +00:00
|
|
|
|
void shutdown();
|
|
|
|
|
~StorageMergeTree();
|
2012-07-30 20:32:36 +00:00
|
|
|
|
|
2013-09-30 19:54:25 +00:00
|
|
|
|
std::string getName() const
|
|
|
|
|
{
|
|
|
|
|
switch (mode)
|
|
|
|
|
{
|
|
|
|
|
case Ordinary: return "MergeTree";
|
|
|
|
|
case Collapsing: return "CollapsingMergeTree";
|
|
|
|
|
case Summing: return "SummingMergeTree";
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw Exception("Unknown mode of operation for StorageMergeTree: " + toString(mode), ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
std::string getTableName() const { return name; }
|
2013-04-24 13:34:04 +00:00
|
|
|
|
std::string getSignColumnName() const { return sign_column; }
|
2012-12-12 15:45:08 +00:00
|
|
|
|
bool supportsSampling() const { return !!sampling_expression; }
|
2013-04-23 11:08:41 +00:00
|
|
|
|
bool supportsFinal() const { return !sign_column.empty(); }
|
2013-11-28 13:16:46 +00:00
|
|
|
|
bool supportsPrewhere() const { return true; }
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
|
|
|
|
const NamesAndTypesList & getColumnsList() const { return *columns; }
|
|
|
|
|
|
|
|
|
|
/** При чтении, выбирается набор кусков, покрывающий нужный диапазон индекса.
|
|
|
|
|
*/
|
2012-07-21 03:45:48 +00:00
|
|
|
|
BlockInputStreams read(
|
2012-07-16 20:25:19 +00:00
|
|
|
|
const Names & column_names,
|
|
|
|
|
ASTPtr query,
|
2013-02-01 19:02:04 +00:00
|
|
|
|
const Settings & settings,
|
2012-07-16 20:25:19 +00:00
|
|
|
|
QueryProcessingStage::Enum & processed_stage,
|
|
|
|
|
size_t max_block_size = DEFAULT_BLOCK_SIZE,
|
2012-07-21 03:45:48 +00:00
|
|
|
|
unsigned threads = 1);
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
|
|
|
|
/** При записи, данные сортируются и пишутся в новые куски.
|
|
|
|
|
*/
|
2014-03-04 11:30:50 +00:00
|
|
|
|
BlockOutputStreamPtr write(ASTPtr query);
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
|
|
|
|
/** Выполнить очередной шаг объединения кусков.
|
|
|
|
|
*/
|
2012-07-31 16:37:20 +00:00
|
|
|
|
bool optimize()
|
|
|
|
|
{
|
2013-12-16 03:51:30 +00:00
|
|
|
|
merge(1, false, true);
|
2012-09-10 19:05:06 +00:00
|
|
|
|
return true;
|
2012-07-31 16:37:20 +00:00
|
|
|
|
}
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2013-01-23 17:43:19 +00:00
|
|
|
|
void dropImpl();
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2013-01-23 11:16:32 +00:00
|
|
|
|
void rename(const String & new_path_to_db, const String & new_name);
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2013-08-08 09:50:15 +00:00
|
|
|
|
/// Метод ALTER позволяет добавлять и удалять столбцы.
|
|
|
|
|
/// Метод ALTER нужно применять, когда обращения к базе приостановлены.
|
|
|
|
|
/// Например если параллельно с INSERT выполнить ALTER, то ALTER выполниться, а INSERT бросит исключение
|
2013-08-09 00:12:59 +00:00
|
|
|
|
void alter(const ASTAlterQuery::Parameters & params);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2014-03-05 18:07:35 +00:00
|
|
|
|
class BigLock
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
BigLock(StorageMergeTree & storage) : merge_lock(storage.merge_lock),
|
|
|
|
|
write_lock(storage.write_lock), read_lock(storage.read_lock)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
Poco::ScopedWriteRWLock merge_lock;
|
|
|
|
|
Poco::ScopedWriteRWLock write_lock;
|
|
|
|
|
Poco::ScopedWriteRWLock read_lock;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef Poco::SharedPtr<BigLock> BigLockPtr;
|
|
|
|
|
BigLockPtr lockAllOperations()
|
|
|
|
|
{
|
|
|
|
|
return new BigLock(*this);
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
private:
|
|
|
|
|
String path;
|
|
|
|
|
String name;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
String full_path;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
NamesAndTypesListPtr columns;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
2013-05-05 18:02:05 +00:00
|
|
|
|
const Context & context;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
ASTPtr primary_expr_ast;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
String date_column_name;
|
2012-12-12 15:45:08 +00:00
|
|
|
|
ASTPtr sampling_expression;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
size_t index_granularity;
|
2012-12-06 13:07:29 +00:00
|
|
|
|
|
|
|
|
|
size_t min_marks_for_seek;
|
|
|
|
|
size_t min_marks_for_concurrent_read;
|
2013-09-08 07:30:52 +00:00
|
|
|
|
size_t max_marks_to_use_cache;
|
2012-08-16 17:27:40 +00:00
|
|
|
|
|
2013-09-30 19:54:25 +00:00
|
|
|
|
/// Режим работы - какие дополнительные действия делать при мердже.
|
|
|
|
|
Mode mode;
|
|
|
|
|
/// Для схлопывания записей об изменениях, если используется Collapsing режим работы.
|
2012-08-16 17:27:40 +00:00
|
|
|
|
String sign_column;
|
|
|
|
|
|
2012-08-29 20:23:19 +00:00
|
|
|
|
StorageMergeTreeSettings settings;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
2013-06-03 13:17:17 +00:00
|
|
|
|
ExpressionActionsPtr primary_expr;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
SortDescription sort_descr;
|
2012-07-21 07:02:55 +00:00
|
|
|
|
Block primary_key_sample;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
|
|
|
|
Increment increment;
|
|
|
|
|
|
2012-07-19 20:32:10 +00:00
|
|
|
|
Logger * log;
|
2013-10-22 18:39:14 +00:00
|
|
|
|
volatile bool shutdown_called;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
2013-08-07 13:07:42 +00:00
|
|
|
|
/// Регулярное выражение соответсвующее названию директории с кусочками
|
|
|
|
|
Poco::RegularExpression file_name_regexp;
|
|
|
|
|
|
2012-07-19 20:32:10 +00:00
|
|
|
|
/// Описание куска с данными.
|
|
|
|
|
struct DataPart
|
|
|
|
|
{
|
2013-12-13 14:23:04 +00:00
|
|
|
|
DataPart(StorageMergeTree & storage_) : storage(storage_), size_in_bytes(0), currently_merging(false) {}
|
2012-07-31 20:03:53 +00:00
|
|
|
|
|
|
|
|
|
StorageMergeTree & storage;
|
2013-08-11 03:40:14 +00:00
|
|
|
|
DayNum_t left_date;
|
|
|
|
|
DayNum_t right_date;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
UInt64 left;
|
|
|
|
|
UInt64 right;
|
2012-11-29 10:50:17 +00:00
|
|
|
|
/// Уровень игнорируется. Использовался предыдущей эвристикой слияния.
|
2012-07-19 20:32:10 +00:00
|
|
|
|
UInt32 level;
|
|
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
|
size_t size; /// в количестве засечек.
|
2013-12-13 14:23:04 +00:00
|
|
|
|
size_t size_in_bytes; /// размер в байтах, 0 - если не посчитано
|
2012-07-19 20:32:10 +00:00
|
|
|
|
time_t modification_time;
|
|
|
|
|
|
2013-08-11 03:40:14 +00:00
|
|
|
|
DayNum_t left_month;
|
|
|
|
|
DayNum_t right_month;
|
2013-12-13 14:23:04 +00:00
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
/// Смотреть и изменять это поле следует под залоченным data_parts_mutex.
|
|
|
|
|
bool currently_merging;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
2013-12-09 00:29:24 +00:00
|
|
|
|
/// Первичный ключ. Всегда загружается в оперативку.
|
|
|
|
|
typedef std::vector<Field> Index;
|
|
|
|
|
Index index;
|
2013-12-13 14:23:04 +00:00
|
|
|
|
|
2013-12-09 00:29:24 +00:00
|
|
|
|
/// NOTE можно загружать засечки тоже в оперативку
|
2012-07-23 06:23:29 +00:00
|
|
|
|
|
2013-12-13 14:23:04 +00:00
|
|
|
|
/// Вычисляем сумарный размер всей директории со всеми файлами
|
|
|
|
|
static size_t calcTotalSize(const String &from)
|
|
|
|
|
{
|
|
|
|
|
Poco::File cur(from);
|
|
|
|
|
if (cur.isFile())
|
|
|
|
|
return cur.getSize();
|
|
|
|
|
std::vector<std::string> files;
|
|
|
|
|
cur.list(files);
|
|
|
|
|
size_t res = 0;
|
|
|
|
|
for (size_t i = 0; i < files.size(); ++i)
|
|
|
|
|
res += calcTotalSize(from + files[i]);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2012-07-19 20:32:10 +00:00
|
|
|
|
void remove() const
|
|
|
|
|
{
|
2012-07-31 20:03:53 +00:00
|
|
|
|
String from = storage.full_path + name + "/";
|
|
|
|
|
String to = storage.full_path + "tmp2_" + name + "/";
|
|
|
|
|
|
|
|
|
|
Poco::File(from).renameTo(to);
|
|
|
|
|
Poco::File(to).remove(true);
|
2012-07-19 20:32:10 +00:00
|
|
|
|
}
|
2013-10-03 12:46:17 +00:00
|
|
|
|
|
|
|
|
|
void renameToOld() const
|
|
|
|
|
{
|
|
|
|
|
String from = storage.full_path + name + "/";
|
|
|
|
|
String to = storage.full_path + "old_" + name + "/";
|
|
|
|
|
|
|
|
|
|
Poco::File f(from);
|
|
|
|
|
f.setLastModified(Poco::Timestamp::fromEpochTime(time(0)));
|
|
|
|
|
f.renameTo(to);
|
|
|
|
|
}
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
|
|
|
|
bool operator< (const DataPart & rhs) const
|
|
|
|
|
{
|
|
|
|
|
if (left_month < rhs.left_month)
|
|
|
|
|
return true;
|
|
|
|
|
if (left_month > rhs.left_month)
|
|
|
|
|
return false;
|
|
|
|
|
if (right_month < rhs.right_month)
|
|
|
|
|
return true;
|
|
|
|
|
if (right_month > rhs.right_month)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (left < rhs.left)
|
|
|
|
|
return true;
|
|
|
|
|
if (left > rhs.left)
|
|
|
|
|
return false;
|
|
|
|
|
if (right < rhs.right)
|
|
|
|
|
return true;
|
|
|
|
|
if (right > rhs.right)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (level < rhs.level)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Содержит другой кусок (получен после объединения другого куска с каким-то ещё)
|
|
|
|
|
bool contains(const DataPart & rhs) const
|
|
|
|
|
{
|
|
|
|
|
return left_month == rhs.left_month /// Куски за разные месяцы не объединяются
|
|
|
|
|
&& right_month == rhs.right_month
|
|
|
|
|
&& level > rhs.level
|
|
|
|
|
&& left_date <= rhs.left_date
|
|
|
|
|
&& right_date >= rhs.right_date
|
|
|
|
|
&& left <= rhs.left
|
2012-11-29 16:52:01 +00:00
|
|
|
|
&& right >= rhs.right;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
}
|
2013-12-09 00:29:24 +00:00
|
|
|
|
|
2013-12-13 16:20:06 +00:00
|
|
|
|
/// Загрузить индекс и вычислить размер.
|
2013-12-09 00:29:24 +00:00
|
|
|
|
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);
|
2013-12-13 14:23:04 +00:00
|
|
|
|
|
|
|
|
|
size_in_bytes = calcTotalSize(storage.full_path + name + "/");
|
2013-12-09 00:29:24 +00:00
|
|
|
|
}
|
2012-07-19 20:32:10 +00:00
|
|
|
|
};
|
|
|
|
|
|
2012-07-23 06:23:29 +00:00
|
|
|
|
typedef SharedPtr<DataPart> DataPartPtr;
|
2012-08-30 17:43:31 +00:00
|
|
|
|
struct DataPartPtrLess { bool operator() (const DataPartPtr & lhs, const DataPartPtr & rhs) const { return *lhs < *rhs; } };
|
|
|
|
|
typedef std::set<DataPartPtr, DataPartPtrLess> DataParts;
|
2012-11-28 08:52:15 +00:00
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
struct RangesInDataPart
|
2012-11-28 08:52:15 +00:00
|
|
|
|
{
|
|
|
|
|
DataPartPtr data_part;
|
2012-12-06 09:45:09 +00:00
|
|
|
|
MarkRanges ranges;
|
2012-11-30 00:52:45 +00:00
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
RangesInDataPart() {}
|
2012-11-30 00:52:45 +00:00
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
RangesInDataPart(DataPartPtr data_part_)
|
|
|
|
|
: data_part(data_part_)
|
2012-11-28 08:52:15 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
2013-12-12 13:54:16 +00:00
|
|
|
|
|
|
|
|
|
/// Пока существует, помечает части как currently_merging и пересчитывает общий объем сливаемых данных.
|
|
|
|
|
/// Вероятно, что части будут помечены заранее.
|
|
|
|
|
class CurrentlyMergingPartsTagger
|
|
|
|
|
{
|
|
|
|
|
public:
|
2013-12-13 16:20:06 +00:00
|
|
|
|
std::vector<DataPartPtr> parts;
|
2014-02-28 20:19:00 +00:00
|
|
|
|
Poco::FastMutex & data_mutex;
|
2013-12-13 16:20:06 +00:00
|
|
|
|
|
2014-02-28 20:19:00 +00:00
|
|
|
|
CurrentlyMergingPartsTagger(const std::vector<DataPartPtr> & parts_, Poco::FastMutex & data_mutex_) : parts(parts_), data_mutex(data_mutex_)
|
2013-12-12 13:54:16 +00:00
|
|
|
|
{
|
2013-12-13 16:20:06 +00:00
|
|
|
|
/// Здесь не лочится мьютекс, так как конструктор вызывается внутри selectPartsToMerge, где он уже залочен
|
|
|
|
|
/// Poco::ScopedLock<Poco::FastMutex> lock(data_mutex);
|
2013-12-12 13:54:16 +00:00
|
|
|
|
for (size_t i = 0; i < parts.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
parts[i]->currently_merging = true;
|
2013-12-13 16:20:06 +00:00
|
|
|
|
StorageMergeTree::total_size_of_currently_merging_parts += parts[i]->size_in_bytes;
|
2013-12-12 13:54:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-28 20:19:00 +00:00
|
|
|
|
|
2013-12-13 14:23:04 +00:00
|
|
|
|
~CurrentlyMergingPartsTagger()
|
2013-12-12 13:54:16 +00:00
|
|
|
|
{
|
2013-12-13 14:23:04 +00:00
|
|
|
|
Poco::ScopedLock<Poco::FastMutex> lock(data_mutex);
|
2013-12-12 13:54:16 +00:00
|
|
|
|
for (size_t i = 0; i < parts.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
parts[i]->currently_merging = false;
|
2013-12-13 16:20:06 +00:00
|
|
|
|
StorageMergeTree::total_size_of_currently_merging_parts -= parts[i]->size_in_bytes;
|
2013-12-12 13:54:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2013-12-13 14:23:04 +00:00
|
|
|
|
/// Сумарный размер currently_merging кусочков в байтах.
|
|
|
|
|
/// Нужно чтобы оценить количество места на диске, которое может понадобится для завершения этих мерджей.
|
|
|
|
|
static size_t total_size_of_currently_merging_parts;
|
2013-12-12 13:54:16 +00:00
|
|
|
|
|
2012-12-06 09:45:09 +00:00
|
|
|
|
typedef std::vector<RangesInDataPart> RangesInDataParts;
|
2012-07-23 06:23:29 +00:00
|
|
|
|
|
2014-03-04 11:30:50 +00:00
|
|
|
|
/** @warning Если берете насколько блокировок, то берите их везде в одинаковом порядке - в том же как они написаны в этом файле */
|
2014-02-28 14:25:15 +00:00
|
|
|
|
/** Взятие этого лока на запись, запрещает мердж */
|
|
|
|
|
Poco::RWLock merge_lock;
|
|
|
|
|
|
|
|
|
|
/** Взятие этого лока на запись, запрещает запись */
|
|
|
|
|
Poco::RWLock write_lock;
|
|
|
|
|
|
|
|
|
|
/** Взятие этого лока на запись, запрещает чтение */
|
|
|
|
|
Poco::RWLock read_lock;
|
|
|
|
|
|
2014-03-04 11:30:50 +00:00
|
|
|
|
/** Актуальное множество кусков с данными. */
|
|
|
|
|
DataParts data_parts;
|
|
|
|
|
Poco::FastMutex data_parts_mutex;
|
|
|
|
|
|
|
|
|
|
/** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов).
|
|
|
|
|
* Ссылки на кусок есть отсюда, из списка актуальных кусков, и из каждого потока чтения, который его сейчас использует.
|
|
|
|
|
* То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить.
|
|
|
|
|
*/
|
|
|
|
|
DataParts all_data_parts;
|
|
|
|
|
Poco::FastMutex all_data_parts_mutex;
|
|
|
|
|
|
2013-02-06 11:26:35 +00:00
|
|
|
|
StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_,
|
2013-11-28 13:16:46 +00:00
|
|
|
|
const Context & context_,
|
|
|
|
|
ASTPtr & primary_expr_ast_,
|
|
|
|
|
const String & date_column_name_,
|
|
|
|
|
const ASTPtr & sampling_expression_, /// NULL, если семплирование не поддерживается.
|
|
|
|
|
size_t index_granularity_,
|
|
|
|
|
Mode mode_ = Ordinary,
|
|
|
|
|
const String & sign_column_ = "",
|
|
|
|
|
const StorageMergeTreeSettings & settings_ = StorageMergeTreeSettings());
|
2013-02-06 11:26:35 +00:00
|
|
|
|
|
2013-08-11 03:40:14 +00:00
|
|
|
|
static String getPartName(DayNum_t left_date, DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level);
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
2013-11-28 13:16:46 +00:00
|
|
|
|
BlockInputStreams spreadMarkRangesAmongThreads(
|
|
|
|
|
RangesInDataParts parts,
|
|
|
|
|
size_t threads,
|
|
|
|
|
const Names & column_names,
|
|
|
|
|
size_t max_block_size,
|
|
|
|
|
bool use_uncompressed_cache,
|
|
|
|
|
ExpressionActionsPtr prewhere_actions,
|
|
|
|
|
const String & prewhere_column);
|
2013-12-09 00:29:24 +00:00
|
|
|
|
|
2013-11-28 13:16:46 +00:00
|
|
|
|
BlockInputStreams spreadMarkRangesAmongThreadsFinal(
|
|
|
|
|
RangesInDataParts parts,
|
|
|
|
|
size_t threads,
|
|
|
|
|
const Names & column_names,
|
|
|
|
|
size_t max_block_size,
|
|
|
|
|
bool use_uncompressed_cache,
|
|
|
|
|
ExpressionActionsPtr prewhere_actions,
|
|
|
|
|
const String & prewhere_column);
|
2012-12-06 09:45:09 +00:00
|
|
|
|
|
2013-04-26 13:20:42 +00:00
|
|
|
|
/// Создать выражение "Sign == 1".
|
2013-06-03 13:17:17 +00:00
|
|
|
|
void createPositiveSignCondition(ExpressionActionsPtr & out_expression, String & out_column);
|
2013-04-26 13:20:42 +00:00
|
|
|
|
|
2012-08-10 20:04:34 +00:00
|
|
|
|
/// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта.
|
2012-07-19 20:32:10 +00:00
|
|
|
|
void loadDataParts();
|
2013-10-03 12:46:17 +00:00
|
|
|
|
|
2012-07-23 06:23:29 +00:00
|
|
|
|
/// Удалить неактуальные куски.
|
|
|
|
|
void clearOldParts();
|
|
|
|
|
|
2013-12-16 03:51:30 +00:00
|
|
|
|
/** Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations = 0, объединяет, пока это возможно.
|
|
|
|
|
* Если aggressive - выбрать куски не обращая внимание на соотношение размеров и их новизну (для запроса OPTIMIZE).
|
|
|
|
|
*/
|
|
|
|
|
void merge(size_t iterations = 1, bool async = true, bool aggressive = false);
|
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
/// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков.
|
2013-12-16 03:51:30 +00:00
|
|
|
|
void mergeThread(bool while_can, bool aggressive);
|
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
/// Сразу помечает их как currently_merging.
|
2013-02-14 11:22:56 +00:00
|
|
|
|
/// Если merge_anything_for_old_months, для кусков за прошедшие месяцы снимается ограничение на соотношение размеров.
|
2014-02-28 20:19:00 +00:00
|
|
|
|
bool selectPartsToMerge(Poco::SharedPtr<CurrentlyMergingPartsTagger> & what, bool merge_anything_for_old_months, bool aggressive);
|
2013-12-16 03:51:30 +00:00
|
|
|
|
|
2014-02-28 20:19:00 +00:00
|
|
|
|
void mergeParts(Poco::SharedPtr<CurrentlyMergingPartsTagger> & what);
|
2012-11-28 08:52:15 +00:00
|
|
|
|
|
|
|
|
|
/// Дождаться, пока фоновые потоки закончат слияния.
|
|
|
|
|
void joinMergeThreads();
|
2012-07-23 06:23:29 +00:00
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
Poco::SharedPtr<boost::threadpool::pool> merge_threads;
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
2013-09-23 12:01:19 +00:00
|
|
|
|
void removeColumnFiles(String column_name);
|
2013-08-07 13:07:42 +00:00
|
|
|
|
|
|
|
|
|
/// Возвращает true если имя директории совпадает с форматом имени директории кусочков
|
2013-08-09 00:12:59 +00:00
|
|
|
|
bool isPartDirectory(const String & dir_name, Poco::RegularExpression::MatchVec & matches) const;
|
2013-09-15 03:44:32 +00:00
|
|
|
|
|
2013-10-03 12:46:17 +00:00
|
|
|
|
/// Кладет в DataPart данные из имени кусочка.
|
|
|
|
|
void parsePartName(const String & file_name, const Poco::RegularExpression::MatchVec & matches, DataPart & part);
|
|
|
|
|
|
|
|
|
|
/// Определить, не битые ли данные в директории. Проверяет индекс и засечеки, но не сами данные.
|
|
|
|
|
bool isBrokenPart(const String & path);
|
|
|
|
|
|
|
|
|
|
/// Найти самые большие old_куски, из которых получен этот кусок.
|
|
|
|
|
/// Переименовать их, убрав префикс old_ и вернуть их имена.
|
|
|
|
|
Strings tryRestorePart(const String & path, const String & file_name, Strings & old_parts);
|
2014-03-04 11:30:50 +00:00
|
|
|
|
|
|
|
|
|
void createConvertExpression(const String & in_column_name, const String & out_type, ExpressionActionsPtr & out_expression, String & out_column);
|
2012-07-16 20:25:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|