Когда появляется необходимость документировать схемы баз данных,
разные DBMS предоставляют свои инструменты для подобных задач. И
большинство из них поддерживает DESC table_name
, в том
числе и ClickHouse. Однако, результат этой команды не столь
выразителен, как хотелось бы.
DESCRIBE TABLE data_lrname type default_type default_expression comment codec_expression ttl_expressionPath String ZSTD(3)Value Float64 Gorilla, LZ4Time UInt32 DoubleDelta, LZ4Date Date DoubleDelta, LZ4Timestamp UInt32 DoubleDelta, LZ4
При этом, системные таблицы tables
и
columns
содержат исчерпывающую информацию, объединив
которую, можно получить вот такой симпатичный результат:
Вдохновение
Перед тем, как начать заботливо пилить собственное решение, лищённое фатальных недостатков, я поискал доступные варианты. И вот эта работа вдохновила меня начать собственный проект. Некоторые заготовки с благодарностью позаимствованы оттуда, с указанием авторства.
Что такое PlantUML
Несколько слов о том, что это представляет из себя этот продукт. Для наилучшего понимания советую зайти на официальную страницу. Если в двух словах, то это программа, которая преобразует текстовое описание диаграмм, например:
@startumlBob -> Alice : hello@enduml
в изображения
Поддерживаются очень многие форматы, например диаграммы прецедентов, классов, деятельности, компонентов и другие. Имеется плагин для Atlassian Confluence, позволяющий использовать макрос и генерировать изображения прямо на страницах wiki. Плагин для pandoc (и не один), для LaTeX, и многое другое.
Можно даже попробовать прямо на сайте.
Идея
Идея проста: в таблице system.tables содержится общая информация о таблицах, движках, ключах партиционирования, сортировки и сэмплирования, и др. А в другой системной таблице, system.columns, подробные данные о каждой из колонок. Комбинируя эти данные, можно легко сгенерировать часть, которая относится к колонкам и ключам таблиц. Это вторая половина каждой из таблиц на диаграмме.
Реализация
Про таблицы с колонками ясно, но есть ещё одна часть, которой я уделил отдельное внимание. Каждый из движков таблиц имеет параметры, которые необходимо распарсить. Например, ReplacingMergeTree содержит опциональный параметр Version. И параметры определяются исключительно движком. Дополнительные параметры необходимы для создания Replicated*MergeTree. Также для MaterializedView всегда создаётся таблица с данными. Эту таблицу не имеет смысла показывать отдельно, и она отображается как отдельная часть конфига для MV.
Это было одной из самых интересных для меня частей проекта. Пришлось познакомиться с токенами и парсингом текста, хотя записать строки и идентификаторы в массив настроек движка оказалось легко. А уже при помощи них записать пары ключ-значение. И это первая половина таблиц на диаграмме.
Также некоторые таблицы зависят друг от друга. Например,
Distributed всегда указывает на локальные
таблицы, а Buffer располагается перед
*MergeTree
таблицами, чтобы принимать мелкие вставки.
И для этих таблиц отдельно устанавливаются отношения. На диаграмме
они представлены стрелочками.
Результат
@startuml' This diagram is generated with https://github.com/Felixoid/clickhouse-plantuml!define Table(x) class x << (T,mistyrose) >>!define View(x) class x << (V,lightblue) >>!define MaterializedView(x) class x << (m,orange) >>!define Distributed(x) class x << (D,violet) >>hide empty methodshide stereotypesskinparam classarrowcolor grayDistributed(graphite.data) { ENGINE=**Distributed** ..engine config.. cluster: graphite_data database: graphite table: data_lr sharding_key: cityHash64(Path) ==columns== Path: String Value: Float64 Time: UInt32 Date: Date Timestamp: UInt32}Table(graphite.data_lr) { ENGINE=**ReplicatedGraphiteMergeTree** ..engine config.. rollup_config: graphite_rollup ..replication.. zoo_path: /clickhouse/tables/graphite.data_lr/{shard} replica: {replica} ==columns== Path: String <size:15><&signal></size> Value: Float64 Time: UInt32 <size:15><&signal></size> Date: Date <size:15><&list-rich></size> Timestamp: UInt32 ..<size:15><&list-rich></size>partition key.. toYYYYMMDD(toStartOfInterval(Date, toIntervalDay(3))) ..<size:15><&signal></size>sorting key.. Path, Time}Table(graphite.index) { ENGINE=**ReplicatedReplacingMergeTree** ..engine config.. version: Version ..replication.. zoo_path: /clickhouse/tables/graphite.index/1 replica: {replica} ==columns== Date: Date <size:15><&list-rich></size> <size:15><&signal></size> Level: UInt32 <size:15><&signal></size> Path: String <size:15><&signal></size> Version: UInt32 ..<size:15><&list-rich></size>partition key.. toYYYYMM(Date) ..<size:15><&signal></size>sorting key.. Level, Path, Date}Table(graphite.tagged) { ENGINE=**ReplicatedReplacingMergeTree** ..engine config.. version: Version ..replication.. zoo_path: /clickhouse/tables/graphite.tagged/1 replica: {replica} ==columns== Date: Date <size:15><&list-rich></size> <size:15><&signal></size> Tag1: String <size:15><&signal></size> Path: String <size:15><&signal></size> Tags: Array(String) Version: UInt32 ..<size:15><&list-rich></size>partition key.. toYYYYMM(Date) ..<size:15><&signal></size>sorting key.. Tag1, Path, Date}graphite.data_lr -|> graphite.data@enduml
Ближайшие планы
Добавить тесты, в идеале интеграционные с поддержкой нескольких версий ClickHouse. А также добавить генерацию диаграмм для кластеров ClickHouse. Возможно, кто-то найдёт для себя интересные кейсы и составит issue. Например можно добавить опциональное описание кодеков сжатия, хотя для меня они пока не важны.
Вместо заключения
Я буду рад, если этот маленький проект позволит кому-то сэкономить время и предоставит возможность составить хорошую документацию.
Начать пользоваться можно, просто скачав модуль из pypi:
pip install clickhouse-plantumlclickhouse-plantuml -h
Исходный код находится здесь.