2012-07-16 20:25:19 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2012-07-23 06:23:29 +00:00
|
|
|
|
#include <boost/thread.hpp>
|
|
|
|
|
|
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>
|
2012-07-17 20:04:39 +00:00
|
|
|
|
#include <DB/Interpreters/Expression.h>
|
2012-07-16 20:25:19 +00:00
|
|
|
|
#include <DB/Storages/IStorage.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2012-07-21 05:07:14 +00:00
|
|
|
|
struct Range;
|
|
|
|
|
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
/** Движок, использующий 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
|
|
|
|
*
|
2012-08-20 05:32:50 +00:00
|
|
|
|
* Если указано sign_column, то при склейке кусков, также "схлопываются"
|
|
|
|
|
* пары записей с разными значениями sign_column для одного значения первичного ключа.
|
2012-08-16 17:27:40 +00:00
|
|
|
|
* (см. CollapsingSortedBlockInputStream.h)
|
2012-07-16 20:25:19 +00:00
|
|
|
|
*/
|
2012-08-29 20:23:19 +00:00
|
|
|
|
|
|
|
|
|
struct StorageMergeTreeSettings
|
|
|
|
|
{
|
|
|
|
|
/// В каких случаях можно объединять куски разного уровня.
|
|
|
|
|
ssize_t delay_time_to_merge_different_level_parts;
|
|
|
|
|
size_t max_level_to_merge_different_level_parts;
|
|
|
|
|
size_t max_rows_to_merge_different_level_parts;
|
|
|
|
|
|
|
|
|
|
/// Куски настолько большого размера объединять нельзя вообще.
|
|
|
|
|
size_t max_rows_to_merge_parts;
|
2012-11-28 08:52:15 +00:00
|
|
|
|
|
|
|
|
|
/// Сколько потоков использовать для объединения кусков.
|
|
|
|
|
size_t merging_threads;
|
2012-08-29 20:23:19 +00:00
|
|
|
|
|
|
|
|
|
StorageMergeTreeSettings() :
|
|
|
|
|
delay_time_to_merge_different_level_parts(36000),
|
|
|
|
|
max_level_to_merge_different_level_parts(10),
|
|
|
|
|
max_rows_to_merge_different_level_parts(10 * 1024 * 1024),
|
2012-11-28 08:52:15 +00:00
|
|
|
|
max_rows_to_merge_parts(100 * 1024 * 1024),
|
|
|
|
|
merging_threads(2) {}
|
2012-08-29 20:23:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
class StorageMergeTree : public IStorage
|
|
|
|
|
{
|
2012-07-21 06:47:17 +00:00
|
|
|
|
friend class MergeTreeBlockInputStream;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
friend class MergeTreeBlockOutputStream;
|
2012-07-30 20:32:36 +00:00
|
|
|
|
friend class MergedBlockOutputStream;
|
2012-07-17 20:04:39 +00:00
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
public:
|
|
|
|
|
/** Подцепить таблицу с соответствующим именем, по соответствующему пути (с / на конце),
|
|
|
|
|
* (корректность имён и путей не проверяется)
|
|
|
|
|
* состоящую из указанных столбцов.
|
|
|
|
|
*
|
|
|
|
|
* primary_expr - выражение для сортировки;
|
|
|
|
|
* date_column_name - имя столбца с датой;
|
|
|
|
|
* index_granularity - на сколько строчек пишется одно значение индекса.
|
|
|
|
|
*/
|
|
|
|
|
StorageMergeTree(const String & path_, const String & name_, NamesAndTypesListPtr columns_,
|
2012-07-17 20:04:39 +00:00
|
|
|
|
Context & context_,
|
|
|
|
|
ASTPtr & primary_expr_ast_, const String & date_column_name_,
|
2012-07-31 16:37:20 +00:00
|
|
|
|
size_t index_granularity_,
|
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
|
|
|
|
|
2012-07-30 20:32:36 +00:00
|
|
|
|
~StorageMergeTree();
|
|
|
|
|
|
2012-07-16 20:25:19 +00:00
|
|
|
|
std::string getName() const { return "MergeTree"; }
|
|
|
|
|
std::string getTableName() const { return name; }
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
/** При записи, данные сортируются и пишутся в новые куски.
|
|
|
|
|
*/
|
|
|
|
|
BlockOutputStreamPtr write(
|
|
|
|
|
ASTPtr query);
|
|
|
|
|
|
|
|
|
|
/** Выполнить очередной шаг объединения кусков.
|
|
|
|
|
*/
|
2012-07-31 16:37:20 +00:00
|
|
|
|
bool optimize()
|
|
|
|
|
{
|
2012-09-10 19:05:06 +00:00
|
|
|
|
merge(1, false);
|
|
|
|
|
return true;
|
2012-07-31 16:37:20 +00:00
|
|
|
|
}
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2012-08-16 18:17:01 +00:00
|
|
|
|
void drop();
|
2012-07-16 20:25:19 +00:00
|
|
|
|
|
2012-07-18 19:40:22 +00:00
|
|
|
|
// void rename(const String & new_path_to_db, const String & new_name);
|
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
|
|
|
|
|
|
|
|
|
Context context;
|
|
|
|
|
ASTPtr primary_expr_ast;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
String date_column_name;
|
|
|
|
|
size_t index_granularity;
|
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
|
|
|
|
|
2012-07-18 20:14:41 +00:00
|
|
|
|
SharedPtr<Expression> 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;
|
|
|
|
|
|
|
|
|
|
/// Описание куска с данными.
|
|
|
|
|
struct DataPart
|
|
|
|
|
{
|
2012-07-31 20:03:53 +00:00
|
|
|
|
DataPart(StorageMergeTree & storage_) : storage(storage_) {}
|
|
|
|
|
|
|
|
|
|
StorageMergeTree & storage;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
Yandex::DayNum_t left_date;
|
|
|
|
|
Yandex::DayNum_t right_date;
|
|
|
|
|
UInt64 left;
|
|
|
|
|
UInt64 right;
|
|
|
|
|
UInt32 level;
|
|
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
|
size_t size; /// в количестве засечек.
|
|
|
|
|
time_t modification_time;
|
|
|
|
|
|
|
|
|
|
Yandex::DayNum_t left_month;
|
|
|
|
|
Yandex::DayNum_t right_month;
|
2012-11-28 08:52:15 +00:00
|
|
|
|
|
|
|
|
|
/// Смотреть и изменять это поле следует под залоченным data_parts_mutex.
|
|
|
|
|
bool currently_merging;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
2012-07-23 06:23:29 +00:00
|
|
|
|
/// NOTE можно загружать индекс и засечки в оперативку
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
&& right >= rhs.right
|
|
|
|
|
&& (left == rhs.left /// У кусков общее начало или конец.
|
|
|
|
|
|| right == rhs.right); /// (только такие образуются после объединения)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
struct DataPartRange
|
|
|
|
|
{
|
|
|
|
|
DataPartPtr data_part;
|
|
|
|
|
size_t first_mark;
|
|
|
|
|
size_t last_mark;
|
|
|
|
|
|
|
|
|
|
DataPartRange()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataPartRange(DataPartPtr data_part_, size_t first_mark_, size_t last_mark_)
|
|
|
|
|
: data_part(data_part_), first_mark(first_mark_), last_mark(last_mark_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
2012-07-23 06:23:29 +00:00
|
|
|
|
|
|
|
|
|
/** Множество всех кусков с данными, включая уже слитые в более крупные, но ещё не удалённые. Оно обычно небольшое (десятки элементов).
|
|
|
|
|
* Ссылки на кусок есть отсюда, из списка актуальных кусков, и из каждого потока чтения, который его сейчас использует.
|
|
|
|
|
* То есть, если количество ссылок равно 1 - то кусок не актуален и не используется прямо сейчас, и его можно удалить.
|
|
|
|
|
*/
|
|
|
|
|
DataParts all_data_parts;
|
|
|
|
|
Poco::FastMutex all_data_parts_mutex;
|
|
|
|
|
|
|
|
|
|
/** Актуальное множество кусков с данными. */
|
2012-08-10 20:04:34 +00:00
|
|
|
|
DataParts data_parts;
|
|
|
|
|
Poco::FastMutex data_parts_mutex;
|
2012-07-19 20:32:10 +00:00
|
|
|
|
|
|
|
|
|
static String getPartName(Yandex::DayNum_t left_date, Yandex::DayNum_t right_date, UInt64 left_id, UInt64 right_id, UInt64 level);
|
|
|
|
|
|
2012-08-10 20:04:34 +00:00
|
|
|
|
/// Загрузить множество кусков с данными с диска. Вызывается один раз - при создании объекта.
|
2012-07-19 20:32:10 +00:00
|
|
|
|
void loadDataParts();
|
2012-07-21 05:07:14 +00:00
|
|
|
|
|
2012-07-23 06:23:29 +00:00
|
|
|
|
/// Удалить неактуальные куски.
|
|
|
|
|
void clearOldParts();
|
|
|
|
|
|
2012-07-21 05:07:14 +00:00
|
|
|
|
/** Определить диапазоны индексов для запроса.
|
|
|
|
|
* Возвращается:
|
|
|
|
|
* date_range - диапазон дат;
|
|
|
|
|
* primary_prefix - префикс первичного ключа, для которого требуется равенство;
|
|
|
|
|
* primary_range - диапазон значений следующего после префикса столбца первичного ключа.
|
|
|
|
|
*/
|
|
|
|
|
void getIndexRanges(ASTPtr & query, Range & date_range, Row & primary_prefix, Range & primary_range);
|
2012-07-23 06:23:29 +00:00
|
|
|
|
|
2012-11-28 08:52:15 +00:00
|
|
|
|
typedef Poco::SharedPtr<boost::thread> ThreadPtr;
|
|
|
|
|
|
|
|
|
|
/// Определяет, какие куски нужно объединять, и запускает их слияние в отдельном потоке. Если iterations=0, объединяет, пока это возможно.
|
2012-09-10 19:05:06 +00:00
|
|
|
|
void merge(size_t iterations = 1, bool async = true);
|
2012-11-28 08:52:15 +00:00
|
|
|
|
/// Если while_can, объединяет в цикле, пока можно; иначе выбирает и объединяет только одну пару кусков.
|
|
|
|
|
void mergeThread(bool while_can);
|
|
|
|
|
/// Сразу помечает их как currently_merging.
|
|
|
|
|
bool selectPartsToMerge(std::vector<DataPartPtr> & parts);
|
|
|
|
|
void mergeParts(std::vector<DataPartPtr> parts);
|
|
|
|
|
|
|
|
|
|
/// Дождаться, пока фоновые потоки закончат слияния.
|
|
|
|
|
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;
|
2012-07-16 20:25:19 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|